Files

1237 lines
44 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
namespace AxCopilot.Services.Agent;
public class DocumentPlannerTool : IAgentTool
{
private class SectionPlan
{
public string Id { get; set; } = "";
public string Heading { get; set; } = "";
public int Level { get; set; } = 1;
public int TargetWords { get; set; } = 300;
public List<string> KeyPoints { get; set; } = new List<string>();
}
private static readonly JsonSerializerOptions _jsonOptions = new JsonSerializerOptions
{
WriteIndented = true,
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
public string Name => "document_plan";
public string Description => "Create a structured document outline/plan and optionally generate the document file immediately. Use this BEFORE generating long documents (3+ pages). When multi-pass mode is OFF, this tool directly creates and saves a document file with section headings and key points. When multi-pass mode is ON, returns a plan for section-by-section writing via document_assemble.";
public ToolParameterSchema Parameters
{
get
{
ToolParameterSchema toolParameterSchema = new ToolParameterSchema();
Dictionary<string, ToolProperty> obj = new Dictionary<string, ToolProperty> { ["topic"] = new ToolProperty
{
Type = "string",
Description = "Document topic or full user request describing what the document should cover."
} };
ToolProperty obj2 = new ToolProperty
{
Type = "string",
Description = "Type of document: report, proposal, analysis, manual, minutes, presentation, guide. Default: report"
};
int num = 7;
List<string> list = new List<string>(num);
CollectionsMarshal.SetCount(list, num);
Span<string> span = CollectionsMarshal.AsSpan(list);
span[0] = "report";
span[1] = "proposal";
span[2] = "analysis";
span[3] = "manual";
span[4] = "minutes";
span[5] = "presentation";
span[6] = "guide";
obj2.Enum = list;
obj["document_type"] = obj2;
obj["target_pages"] = new ToolProperty
{
Type = "integer",
Description = "Target number of pages (1 page ≈ 500 words). Default: 5"
};
ToolProperty obj3 = new ToolProperty
{
Type = "string",
Description = "Output document format: html, docx, markdown. Default: html"
};
num = 3;
List<string> list2 = new List<string>(num);
CollectionsMarshal.SetCount(list2, num);
Span<string> span2 = CollectionsMarshal.AsSpan(list2);
span2[0] = "html";
span2[1] = "docx";
span2[2] = "markdown";
obj3.Enum = list2;
obj["format"] = obj3;
obj["sections_hint"] = new ToolProperty
{
Type = "string",
Description = "Optional hint for desired sections/structure (e.g. '서론, 현황분석, 문제점, 개선방안, 결론')"
};
obj["reference_summary"] = new ToolProperty
{
Type = "string",
Description = "Optional summary of reference data already read via file_read (to incorporate into plan)"
};
toolParameterSchema.Properties = obj;
num = 1;
List<string> list3 = new List<string>(num);
CollectionsMarshal.SetCount(list3, num);
CollectionsMarshal.AsSpan(list3)[0] = "topic";
toolParameterSchema.Required = list3;
return toolParameterSchema;
}
}
private static bool IsMultiPassEnabled()
{
return ((!(Application.Current is App app)) ? ((bool?)null) : app.SettingsService?.Settings.Llm.EnableMultiPassDocument) == true;
}
private static string GetFolderDataUsage()
{
return ((!(Application.Current is App app)) ? null : app.SettingsService?.Settings.Llm.FolderDataUsage) ?? "none";
}
public Task<ToolResult> ExecuteAsync(JsonElement args, AgentContext context, CancellationToken ct)
{
string text = args.GetProperty("topic").GetString() ?? "";
JsonElement value;
string docType = (args.TryGetProperty("document_type", out value) ? (value.GetString() ?? "report") : "report");
JsonElement value2;
int num = (args.TryGetProperty("target_pages", out value2) ? value2.GetInt32() : 5);
JsonElement value3;
string format = (args.TryGetProperty("format", out value3) ? (value3.GetString() ?? "html") : "html");
JsonElement value4;
string hint = (args.TryGetProperty("sections_hint", out value4) ? (value4.GetString() ?? "") : "");
JsonElement value5;
string refSummary = (args.TryGetProperty("reference_summary", out value5) ? (value5.GetString() ?? "") : "");
if (string.IsNullOrWhiteSpace(text))
{
return Task.FromResult(ToolResult.Fail("topic이 비어있습니다."));
}
if (num < 1)
{
num = 1;
}
if (num > 50)
{
num = 50;
}
bool flag = IsMultiPassEnabled();
string folderDataUsage = GetFolderDataUsage();
int num2 = (flag ? ((int)Math.Ceiling((double)num * 1.5)) : num);
int totalWords = num2 * 500;
List<SectionPlan> sections = BuildSections(docType, num2, hint, refSummary);
DistributeWordCount(sections, totalWords);
if ((folderDataUsage == "active" || folderDataUsage == "passive") ? true : false)
{
return ExecuteSinglePassWithData(text, docType, format, num2, totalWords, sections, folderDataUsage, refSummary);
}
return ExecuteWithAssembleInstructions(text, docType, format, num2, totalWords, sections, flag);
}
private Task<ToolResult> ExecuteWithAssembleInstructions(string topic, string docType, string format, int targetPages, int totalWords, List<SectionPlan> sections, bool highQuality)
{
string text = SanitizeFileName(topic);
if (1 == 0)
{
}
string text2 = ((format == "docx") ? ".docx" : ((!(format == "markdown")) ? ".html" : ".md"));
if (1 == 0)
{
}
string text3 = text2;
string fileName = text + text3;
string label = (highQuality ? "[고품질]" : "[표준]");
if (!(format == "markdown"))
{
if (format == "docx")
{
return ExecuteWithDocxScaffold(topic, fileName, sections, targetPages, totalWords, label);
}
return ExecuteWithHtmlScaffold(topic, fileName, sections, targetPages, totalWords, label);
}
return ExecuteWithMarkdownScaffold(topic, fileName, sections, targetPages, totalWords, label);
}
private Task<ToolResult> ExecuteWithHtmlScaffold(string topic, string fileName, List<SectionPlan> sections, int targetPages, int totalWords, string label)
{
StringBuilder stringBuilder = new StringBuilder();
StringBuilder stringBuilder2;
StringBuilder.AppendInterpolatedStringHandler handler;
foreach (SectionPlan section in sections)
{
string value = SuggestHtmlElements(section.KeyPoints);
stringBuilder2 = stringBuilder;
StringBuilder stringBuilder3 = stringBuilder2;
handler = new StringBuilder.AppendInterpolatedStringHandler(9, 1, stringBuilder2);
handler.AppendLiteral("<h2>");
handler.AppendFormatted(Escape(section.Heading));
handler.AppendLiteral("</h2>");
stringBuilder3.AppendLine(ref handler);
stringBuilder2 = stringBuilder;
StringBuilder stringBuilder4 = stringBuilder2;
handler = new StringBuilder.AppendInterpolatedStringHandler(17, 2, stringBuilder2);
handler.AppendLiteral("<!-- 목표 ");
handler.AppendFormatted(section.TargetWords);
handler.AppendLiteral("단어 | 핵심: ");
handler.AppendFormatted(string.Join(", ", section.KeyPoints));
stringBuilder4.AppendLine(ref handler);
stringBuilder2 = stringBuilder;
StringBuilder stringBuilder5 = stringBuilder2;
handler = new StringBuilder.AppendInterpolatedStringHandler(33, 1, stringBuilder2);
handler.AppendLiteral(" 활용 가능 요소(내용에 맞게 자유 선택): ");
handler.AppendFormatted(value);
handler.AppendLiteral(" -->");
stringBuilder5.AppendLine(ref handler);
stringBuilder.AppendLine();
}
StringBuilder stringBuilder6 = new StringBuilder();
stringBuilder2 = stringBuilder6;
StringBuilder stringBuilder7 = stringBuilder2;
handler = new StringBuilder.AppendInterpolatedStringHandler(30, 4, stringBuilder2);
handler.AppendLiteral("\ud83d\udccb 문서 개요 생성 완료 ");
handler.AppendFormatted(label);
handler.AppendLiteral(" (");
handler.AppendFormatted(sections.Count);
handler.AppendLiteral("개 섹션, ");
handler.AppendFormatted(targetPages);
handler.AppendLiteral("페이지/");
handler.AppendFormatted(totalWords);
handler.AppendLiteral("단어)");
stringBuilder7.AppendLine(ref handler);
stringBuilder6.AppendLine();
stringBuilder6.AppendLine("## 즉시 실행: html_create 호출");
stringBuilder2 = stringBuilder6;
StringBuilder stringBuilder8 = stringBuilder2;
handler = new StringBuilder.AppendInterpolatedStringHandler(8, 1, stringBuilder2);
handler.AppendLiteral("path: \"");
handler.AppendFormatted(fileName);
handler.AppendLiteral("\"");
stringBuilder8.AppendLine(ref handler);
stringBuilder2 = stringBuilder6;
StringBuilder stringBuilder9 = stringBuilder2;
handler = new StringBuilder.AppendInterpolatedStringHandler(9, 1, stringBuilder2);
handler.AppendLiteral("title: \"");
handler.AppendFormatted(topic);
handler.AppendLiteral("\"");
stringBuilder9.AppendLine(ref handler);
stringBuilder6.AppendLine("toc: true, numbered: true, mood: \"professional\"");
stringBuilder2 = stringBuilder6;
StringBuilder stringBuilder10 = stringBuilder2;
handler = new StringBuilder.AppendInterpolatedStringHandler(50, 1, stringBuilder2);
handler.AppendLiteral("cover: {\"title\": \"");
handler.AppendFormatted(topic);
handler.AppendLiteral("\", \"author\": \"AX Copilot Agent\"}");
stringBuilder10.AppendLine(ref handler);
stringBuilder6.AppendLine();
stringBuilder6.AppendLine("body에 아래 섹션 구조를 기반으로 각 섹션의 내용과 시각화를 자유롭게 작성하세요:");
stringBuilder6.AppendLine("(주석의 '활용 가능 요소'는 참고용이며, 내용에 맞게 다른 요소를 써도 됩니다)");
stringBuilder6.AppendLine();
stringBuilder6.AppendLine("--- body 시작 ---");
stringBuilder6.Append(stringBuilder);
stringBuilder6.AppendLine("--- body 끝 ---");
stringBuilder6.AppendLine();
stringBuilder6.AppendLine("⚠ html_create를 지금 즉시 호출하세요. 모든 섹션에 충분한 실제 내용을 작성하세요.");
return Task.FromResult(ToolResult.Ok(stringBuilder6.ToString()));
}
private static string SuggestHtmlElements(List<string> keyPoints)
{
string text = string.Join(" ", keyPoints).ToLowerInvariant();
List<string> list = new List<string>();
if (ContainsAny(text, "매출", "실적", "통계", "수치", "비교", "현황", "지표", "점유율", "비율", "순위", "성과"))
{
list.Add("table(데이터 정리)");
list.Add("div.chart-bar(수치 시각화)");
}
if (ContainsAny(text, "강점", "약점", "기회", "위협", "swot", "리스크", "문제점", "장점", "단점"))
{
list.Add("div.grid-2(항목 대비)");
list.Add("callout-warning/callout-tip");
}
if (ContainsAny(text, "전략", "제안", "방안", "계획", "방향", "로드맵", "목표"))
{
list.Add("callout-info(핵심 전략)");
list.Add("span.badge-blue/green(구분 배지)");
}
if (ContainsAny(text, "일정", "단계", "절차", "프로세스", "과정", "순서", "연혁", "역사"))
{
list.Add("div.timeline(단계/일정)");
}
if (ContainsAny(text, "달성", "진행", "완료", "목표 대비", "진척"))
{
list.Add("div.progress(달성률)");
}
if (ContainsAny(text, "종류", "유형", "분류", "구성", "항목", "요소", "구분"))
{
list.Add("div.grid-3.card(항목 카드)");
}
if (ContainsAny(text, "핵심", "요약", "결론", "시사점", "포인트"))
{
list.Add("blockquote(핵심 메시지)");
}
list.Add("p/ul/ol(일반 서술)");
return string.Join(" | ", list.Distinct().Take(4));
}
private static bool ContainsAny(string text, params string[] keywords)
{
return keywords.Any((string k) => text.Contains(k));
}
private Task<ToolResult> ExecuteWithDocxScaffold(string topic, string fileName, List<SectionPlan> sections, int targetPages, int totalWords, string label)
{
var sections2 = sections.Select((SectionPlan s) => new
{
heading = s.Heading,
content = $"[{s.Heading} 내용을 {s.TargetWords}단어 이상으로 작성. 핵심 항목: {string.Join(", ", s.KeyPoints)}]",
level = s.Level
});
string value = JsonSerializer.Serialize(new
{
path = fileName,
title = topic,
format = "docx",
cover_subtitle = "AX Copilot Agent",
sections = sections2
}, _jsonOptions);
StringBuilder stringBuilder = new StringBuilder();
StringBuilder stringBuilder2 = stringBuilder;
StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(30, 4, stringBuilder2);
handler.AppendLiteral("\ud83d\udccb 문서 개요 생성 완료 ");
handler.AppendFormatted(label);
handler.AppendLiteral(" (");
handler.AppendFormatted(sections.Count);
handler.AppendLiteral("개 섹션, ");
handler.AppendFormatted(targetPages);
handler.AppendLiteral("페이지/");
handler.AppendFormatted(totalWords);
handler.AppendLiteral("단어)");
stringBuilder2.AppendLine(ref handler);
stringBuilder.AppendLine("아래 document_assemble 파라미터에서 각 sections[].content의 [내용...] 부분을 실제 내용으로 채워서 즉시 호출하세요.");
stringBuilder.AppendLine();
stringBuilder.AppendLine("## 즉시 실행: document_assemble 호출 파라미터");
stringBuilder.AppendLine(value);
stringBuilder.AppendLine("⚠ 주의: 설명하지 말고 document_assemble 도구를 지금 즉시 호출하세요.");
return Task.FromResult(ToolResult.Ok(stringBuilder.ToString()));
}
private Task<ToolResult> ExecuteWithMarkdownScaffold(string topic, string fileName, List<SectionPlan> sections, int targetPages, int totalWords, string label)
{
StringBuilder stringBuilder = new StringBuilder();
StringBuilder stringBuilder2 = stringBuilder;
StringBuilder stringBuilder3 = stringBuilder2;
StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(2, 1, stringBuilder2);
handler.AppendLiteral("# ");
handler.AppendFormatted(topic);
stringBuilder3.AppendLine(ref handler);
stringBuilder.AppendLine();
stringBuilder2 = stringBuilder;
StringBuilder stringBuilder4 = stringBuilder2;
handler = new StringBuilder.AppendInterpolatedStringHandler(7, 1, stringBuilder2);
handler.AppendLiteral("*작성일: ");
handler.AppendFormatted(DateTime.Now, "yyyy-MM-dd");
handler.AppendLiteral("*");
stringBuilder4.AppendLine(ref handler);
stringBuilder.AppendLine();
foreach (SectionPlan section in sections)
{
stringBuilder2 = stringBuilder;
StringBuilder stringBuilder5 = stringBuilder2;
handler = new StringBuilder.AppendInterpolatedStringHandler(3, 1, stringBuilder2);
handler.AppendLiteral("## ");
handler.AppendFormatted(section.Heading);
stringBuilder5.AppendLine(ref handler);
stringBuilder.AppendLine();
stringBuilder2 = stringBuilder;
StringBuilder stringBuilder6 = stringBuilder2;
handler = new StringBuilder.AppendInterpolatedStringHandler(21, 2, stringBuilder2);
handler.AppendLiteral("<!-- 목표 ");
handler.AppendFormatted(section.TargetWords);
handler.AppendLiteral("단어 | 핵심: ");
handler.AppendFormatted(string.Join(" / ", section.KeyPoints));
handler.AppendLiteral(" -->");
stringBuilder6.AppendLine(ref handler);
stringBuilder2 = stringBuilder;
StringBuilder stringBuilder7 = stringBuilder2;
handler = new StringBuilder.AppendInterpolatedStringHandler(24, 2, stringBuilder2);
handler.AppendLiteral("[");
handler.AppendFormatted(section.Heading);
handler.AppendLiteral(" 내용을 ");
handler.AppendFormatted(section.TargetWords);
handler.AppendLiteral("단어 이상으로 상세히 작성하세요]");
stringBuilder7.AppendLine(ref handler);
stringBuilder.AppendLine();
}
StringBuilder stringBuilder8 = new StringBuilder();
stringBuilder2 = stringBuilder8;
StringBuilder stringBuilder9 = stringBuilder2;
handler = new StringBuilder.AppendInterpolatedStringHandler(30, 4, stringBuilder2);
handler.AppendLiteral("\ud83d\udccb 문서 개요 생성 완료 ");
handler.AppendFormatted(label);
handler.AppendLiteral(" (");
handler.AppendFormatted(sections.Count);
handler.AppendLiteral("개 섹션, ");
handler.AppendFormatted(targetPages);
handler.AppendLiteral("페이지/");
handler.AppendFormatted(totalWords);
handler.AppendLiteral("단어)");
stringBuilder9.AppendLine(ref handler);
stringBuilder8.AppendLine("아래 file_write 파라미터에서 각 섹션의 [내용...] 부분을 실제 내용으로 채워서 즉시 호출하세요.");
stringBuilder8.AppendLine();
stringBuilder8.AppendLine("## 즉시 실행: file_write 호출");
stringBuilder2 = stringBuilder8;
StringBuilder stringBuilder10 = stringBuilder2;
handler = new StringBuilder.AppendInterpolatedStringHandler(10, 1, stringBuilder2);
handler.AppendLiteral("- path: \"");
handler.AppendFormatted(fileName);
handler.AppendLiteral("\"");
stringBuilder10.AppendLine(ref handler);
stringBuilder8.AppendLine("- content (각 [내용...] 부분을 실제 내용으로 교체):");
stringBuilder8.AppendLine(stringBuilder.ToString());
stringBuilder8.AppendLine("⚠ 주의: 설명하지 말고 file_write 도구를 지금 즉시 호출하세요. 모든 섹션에 실제 내용을 작성하세요.");
return Task.FromResult(ToolResult.Ok(stringBuilder8.ToString()));
}
private Task<ToolResult> ExecuteSinglePassWithData(string topic, string docType, string format, int targetPages, int totalWords, List<SectionPlan> sections, string folderDataUsage, string refSummary)
{
if (1 == 0)
{
}
string text = ((format == "docx") ? ".docx" : ((!(format == "markdown")) ? ".html" : ".md"));
if (1 == 0)
{
}
string text2 = text;
if (1 == 0)
{
}
text = ((format == "docx") ? "docx_create" : ((!(format == "markdown")) ? "html_create" : "file_write"));
if (1 == 0)
{
}
string value = text;
string text3 = SanitizeFileName(topic);
string text4 = text3 + text2;
bool flag = !string.IsNullOrWhiteSpace(refSummary);
var value2 = new
{
topic = topic,
document_type = docType,
format = format,
target_pages = targetPages,
estimated_total_words = totalWords,
suggested_file_name = text4,
sections = sections,
reference_data = (flag ? refSummary : null),
instructions = new
{
mode = "single-pass-with-data",
folder_data_usage = folderDataUsage,
step1 = (flag ? "Reference data is already provided above. Skip to step 3." : "Use folder_map to scan the work folder, then use document_read to read RELEVANT files only."),
step2 = (flag ? "(skipped)" : "Summarize the key findings from the folder documents relevant to the topic."),
step3 = "Write the COMPLETE document content covering ALL sections above, incorporating folder data.",
step4 = $"Save the document using {value} with the path '{text4}'. " + "Write ALL sections in a SINGLE tool call. Do NOT split into multiple calls.",
note = "Minimize LLM calls. Read data → write complete document → save. Maximum 3 iterations."
}
};
string value3 = JsonSerializer.Serialize(value2, _jsonOptions);
string value4 = (flag ? "참조 데이터가 이미 제공되었습니다. 바로 문서를 작성하세요." : ("폴더 데이터 활용 모드(" + folderDataUsage + ")가 활성화되어 있습니다. 먼저 관련 데이터를 읽은 후 문서를 작성하세요."));
return Task.FromResult(ToolResult.Ok($"\ud83d\udccb 문서 개요가 생성되었습니다 [싱글패스+데이터활용] ({sections.Count}개 섹션, 목표 {targetPages}페이지/{totalWords}단어).\n{value4}\n작성 완료 후 {value}로 '{text4}' 파일에 저장하세요.\n\n{value3}"));
}
private static void GenerateHtml(string path, string title, string docType, List<SectionPlan> sections)
{
string css = TemplateService.GetCss("professional");
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.AppendLine("<!DOCTYPE html>");
stringBuilder.AppendLine("<html lang=\"ko\">");
stringBuilder.AppendLine("<head>");
stringBuilder.AppendLine("<meta charset=\"utf-8\">");
StringBuilder stringBuilder2 = stringBuilder;
StringBuilder stringBuilder3 = stringBuilder2;
StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(15, 1, stringBuilder2);
handler.AppendLiteral("<title>");
handler.AppendFormatted(Escape(title));
handler.AppendLiteral("</title>");
stringBuilder3.AppendLine(ref handler);
stringBuilder.AppendLine("<style>");
stringBuilder.AppendLine(css);
stringBuilder.AppendLine("\n.doc { max-width: 900px; margin: 0 auto; padding: 40px 30px 60px; }\n.doc h1 { font-size: 28px; margin-bottom: 8px; border-bottom: 3px solid var(--accent, #4B5EFC); padding-bottom: 10px; }\n.doc h2 { font-size: 22px; margin-top: 36px; margin-bottom: 12px; border-bottom: 2px solid var(--accent, #4B5EFC); padding-bottom: 6px; }\n.doc .meta { color: #888; font-size: 13px; margin-bottom: 24px; }\n.doc .section { line-height: 1.8; margin-bottom: 20px; }\n.doc .toc { background: #f8f9fa; border-radius: 12px; padding: 20px 28px; margin: 24px 0 32px; }\n.doc .toc h3 { margin-top: 0; }\n.doc .toc a { color: inherit; text-decoration: none; }\n.doc .toc a:hover { text-decoration: underline; }\n.doc .toc ul { list-style: none; padding-left: 0; }\n.doc .toc li { padding: 4px 0; }\n.doc .key-point { background: #f0f4ff; border-left: 4px solid var(--accent, #4B5EFC); padding: 12px 16px; margin: 12px 0; border-radius: 0 8px 8px 0; }\n@media print { .doc h2 { page-break-before: auto; } }\n");
stringBuilder.AppendLine("</style>");
stringBuilder.AppendLine("</head>");
stringBuilder.AppendLine("<body>");
stringBuilder.AppendLine("<div class=\"doc\">");
stringBuilder2 = stringBuilder;
StringBuilder stringBuilder4 = stringBuilder2;
handler = new StringBuilder.AppendInterpolatedStringHandler(9, 1, stringBuilder2);
handler.AppendLiteral("<h1>");
handler.AppendFormatted(Escape(title));
handler.AppendLiteral("</h1>");
stringBuilder4.AppendLine(ref handler);
stringBuilder2 = stringBuilder;
StringBuilder stringBuilder5 = stringBuilder2;
handler = new StringBuilder.AppendInterpolatedStringHandler(47, 3, stringBuilder2);
handler.AppendLiteral("<div class=\"meta\">문서 유형: ");
handler.AppendFormatted(Escape(GetDocTypeLabel(docType)));
handler.AppendLiteral(" | 작성일: ");
handler.AppendFormatted(DateTime.Now, "yyyy-MM-dd");
handler.AppendLiteral(" | 섹션: ");
handler.AppendFormatted(sections.Count);
handler.AppendLiteral("개</div>");
stringBuilder5.AppendLine(ref handler);
if (sections.Count > 1)
{
stringBuilder.AppendLine("<div class=\"toc\">");
stringBuilder.AppendLine("<h3>\ud83d\udccb 목차</h3>");
stringBuilder.AppendLine("<ul>");
for (int i = 0; i < sections.Count; i++)
{
stringBuilder2 = stringBuilder;
StringBuilder stringBuilder6 = stringBuilder2;
handler = new StringBuilder.AppendInterpolatedStringHandler(29, 2, stringBuilder2);
handler.AppendLiteral("<li><a href=\"#sec-");
handler.AppendFormatted(i + 1);
handler.AppendLiteral("\">");
handler.AppendFormatted(Escape(sections[i].Heading));
handler.AppendLiteral("</a></li>");
stringBuilder6.AppendLine(ref handler);
}
stringBuilder.AppendLine("</ul>");
stringBuilder.AppendLine("</div>");
}
for (int j = 0; j < sections.Count; j++)
{
SectionPlan sectionPlan = sections[j];
stringBuilder2 = stringBuilder;
StringBuilder stringBuilder7 = stringBuilder2;
handler = new StringBuilder.AppendInterpolatedStringHandler(19, 2, stringBuilder2);
handler.AppendLiteral("<h2 id=\"sec-");
handler.AppendFormatted(j + 1);
handler.AppendLiteral("\">");
handler.AppendFormatted(Escape(sectionPlan.Heading));
handler.AppendLiteral("</h2>");
stringBuilder7.AppendLine(ref handler);
stringBuilder.AppendLine("<div class=\"section\">");
foreach (string keyPoint in sectionPlan.KeyPoints)
{
stringBuilder.AppendLine("<div class=\"key-point\">");
stringBuilder2 = stringBuilder;
StringBuilder stringBuilder8 = stringBuilder2;
handler = new StringBuilder.AppendInterpolatedStringHandler(19, 1, stringBuilder2);
handler.AppendLiteral("<strong>▸ ");
handler.AppendFormatted(Escape(keyPoint));
handler.AppendLiteral("</strong>");
stringBuilder8.AppendLine(ref handler);
stringBuilder2 = stringBuilder;
StringBuilder stringBuilder9 = stringBuilder2;
handler = new StringBuilder.AppendInterpolatedStringHandler(40, 2, stringBuilder2);
handler.AppendLiteral("<p>");
handler.AppendFormatted(Escape(keyPoint));
handler.AppendLiteral("에 대한 상세 내용을 여기에 작성합니다. (목표: 약 ");
handler.AppendFormatted(sectionPlan.TargetWords / Math.Max(1, sectionPlan.KeyPoints.Count));
handler.AppendLiteral("단어)</p>");
stringBuilder9.AppendLine(ref handler);
stringBuilder.AppendLine("</div>");
}
stringBuilder.AppendLine("</div>");
}
stringBuilder.AppendLine("</div>");
stringBuilder.AppendLine("</body>");
stringBuilder.AppendLine("</html>");
File.WriteAllText(path, stringBuilder.ToString(), Encoding.UTF8);
}
private static void GenerateDocx(string path, string title, List<SectionPlan> sections)
{
using WordprocessingDocument wordprocessingDocument = WordprocessingDocument.Create(path, WordprocessingDocumentType.Document);
MainDocumentPart mainDocumentPart = wordprocessingDocument.AddMainDocumentPart();
mainDocumentPart.Document = new Document();
Body body = mainDocumentPart.Document.AppendChild(new Body());
AddDocxParagraph(body, title, bold: true, "48");
AddDocxParagraph(body, $"작성일: {DateTime.Now:yyyy-MM-dd}", bold: false, "20", "888888");
AddDocxParagraph(body, "");
foreach (SectionPlan section in sections)
{
AddDocxParagraph(body, section.Heading, bold: true, "32", "2B579A");
foreach (string keyPoint in section.KeyPoints)
{
AddDocxParagraph(body, "▸ " + keyPoint, bold: true);
AddDocxParagraph(body, keyPoint + "에 대한 상세 내용을 여기에 작성합니다.");
}
AddDocxParagraph(body, "");
}
}
private static void AddDocxParagraph(Body body, string text, bool bold = false, string fontSize = "22", string? color = null)
{
Paragraph paragraph = new Paragraph();
Run run = new Run();
RunProperties runProperties = new RunProperties
{
FontSize = new FontSize
{
Val = fontSize
}
};
if (bold)
{
runProperties.Bold = new Bold();
}
if (color != null)
{
runProperties.Color = new Color
{
Val = color
};
}
run.AppendChild(runProperties);
run.AppendChild(new Text(text)
{
Space = SpaceProcessingModeValues.Preserve
});
paragraph.AppendChild(run);
body.AppendChild(paragraph);
}
private static void GenerateMarkdown(string path, string title, List<SectionPlan> sections)
{
StringBuilder stringBuilder = new StringBuilder();
StringBuilder stringBuilder2 = stringBuilder;
StringBuilder stringBuilder3 = stringBuilder2;
StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(2, 1, stringBuilder2);
handler.AppendLiteral("# ");
handler.AppendFormatted(title);
stringBuilder3.AppendLine(ref handler);
stringBuilder.AppendLine();
stringBuilder2 = stringBuilder;
StringBuilder stringBuilder4 = stringBuilder2;
handler = new StringBuilder.AppendInterpolatedStringHandler(7, 1, stringBuilder2);
handler.AppendLiteral("*작성일: ");
handler.AppendFormatted(DateTime.Now, "yyyy-MM-dd");
handler.AppendLiteral("*");
stringBuilder4.AppendLine(ref handler);
stringBuilder.AppendLine();
if (sections.Count > 1)
{
stringBuilder.AppendLine("## 목차");
stringBuilder.AppendLine();
foreach (SectionPlan section in sections)
{
stringBuilder2 = stringBuilder;
StringBuilder stringBuilder5 = stringBuilder2;
handler = new StringBuilder.AppendInterpolatedStringHandler(7, 2, stringBuilder2);
handler.AppendLiteral("- [");
handler.AppendFormatted(section.Heading);
handler.AppendLiteral("](#");
handler.AppendFormatted(section.Heading.Replace(" ", "-").ToLowerInvariant());
handler.AppendLiteral(")");
stringBuilder5.AppendLine(ref handler);
}
stringBuilder.AppendLine();
stringBuilder.AppendLine("---");
stringBuilder.AppendLine();
}
foreach (SectionPlan section2 in sections)
{
stringBuilder2 = stringBuilder;
StringBuilder stringBuilder6 = stringBuilder2;
handler = new StringBuilder.AppendInterpolatedStringHandler(3, 1, stringBuilder2);
handler.AppendLiteral("## ");
handler.AppendFormatted(section2.Heading);
stringBuilder6.AppendLine(ref handler);
stringBuilder.AppendLine();
foreach (string keyPoint in section2.KeyPoints)
{
stringBuilder2 = stringBuilder;
StringBuilder stringBuilder7 = stringBuilder2;
handler = new StringBuilder.AppendInterpolatedStringHandler(6, 1, stringBuilder2);
handler.AppendLiteral("### ▸ ");
handler.AppendFormatted(keyPoint);
stringBuilder7.AppendLine(ref handler);
stringBuilder.AppendLine();
stringBuilder2 = stringBuilder;
StringBuilder stringBuilder8 = stringBuilder2;
handler = new StringBuilder.AppendInterpolatedStringHandler(22, 1, stringBuilder2);
handler.AppendFormatted(keyPoint);
handler.AppendLiteral("에 대한 상세 내용을 여기에 작성합니다.");
stringBuilder8.AppendLine(ref handler);
stringBuilder.AppendLine();
}
}
File.WriteAllText(path, stringBuilder.ToString(), Encoding.UTF8);
}
private static List<SectionPlan> BuildSections(string docType, int pages, string hint, string refSummary)
{
if (!string.IsNullOrWhiteSpace(hint))
{
string[] array = hint.Split(new char[4] { ',', '/', '→', '\n' }, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
List<SectionPlan> list = new List<SectionPlan>();
for (int i = 0; i < array.Length; i++)
{
SectionPlan obj = new SectionPlan
{
Id = $"sec-{i + 1}",
Heading = $"{i + 1}. {array[i].TrimStart("0123456789. ".ToCharArray())}",
Level = 1
};
int num = 1;
List<string> list2 = new List<string>(num);
CollectionsMarshal.SetCount(list2, num);
CollectionsMarshal.AsSpan(list2)[0] = array[i] + " 관련 핵심 내용을 상세히 작성";
obj.KeyPoints = list2;
list.Add(obj);
}
return list;
}
if (1 == 0)
{
}
List<SectionPlan> result;
switch (docType)
{
case "proposal":
{
List<SectionPlan> list29 = new List<SectionPlan>();
SectionPlan obj24 = new SectionPlan
{
Id = "sec-1",
Heading = "1. 개요",
Level = 1
};
int num = 3;
List<string> list30 = new List<string>(num);
CollectionsMarshal.SetCount(list30, num);
Span<string> span23 = CollectionsMarshal.AsSpan(list30);
span23[0] = "배경";
span23[1] = "목적";
span23[2] = "범위";
obj24.KeyPoints = list30;
list29.Add(obj24);
SectionPlan obj25 = new SectionPlan
{
Id = "sec-2",
Heading = "2. 현황 분석",
Level = 1
};
num = 2;
List<string> list31 = new List<string>(num);
CollectionsMarshal.SetCount(list31, num);
Span<string> span24 = CollectionsMarshal.AsSpan(list31);
span24[0] = "현재 상황";
span24[1] = "문제점 식별";
obj25.KeyPoints = list31;
list29.Add(obj25);
SectionPlan obj26 = new SectionPlan
{
Id = "sec-3",
Heading = "3. 제안 내용",
Level = 1
};
num = 3;
List<string> list32 = new List<string>(num);
CollectionsMarshal.SetCount(list32, num);
Span<string> span25 = CollectionsMarshal.AsSpan(list32);
span25[0] = "핵심 제안";
span25[1] = "기대 효과";
span25[2] = "실행 방안";
obj26.KeyPoints = list32;
list29.Add(obj26);
SectionPlan obj27 = new SectionPlan
{
Id = "sec-4",
Heading = "4. 추진 일정",
Level = 1
};
num = 2;
List<string> list33 = new List<string>(num);
CollectionsMarshal.SetCount(list33, num);
Span<string> span26 = CollectionsMarshal.AsSpan(list33);
span26[0] = "단계별 일정";
span26[1] = "마일스톤";
obj27.KeyPoints = list33;
list29.Add(obj27);
SectionPlan obj28 = new SectionPlan
{
Id = "sec-5",
Heading = "5. 소요 자원",
Level = 1
};
num = 3;
List<string> list34 = new List<string>(num);
CollectionsMarshal.SetCount(list34, num);
Span<string> span27 = CollectionsMarshal.AsSpan(list34);
span27[0] = "인력";
span27[1] = "예산";
span27[2] = "장비";
obj28.KeyPoints = list34;
list29.Add(obj28);
SectionPlan obj29 = new SectionPlan
{
Id = "sec-6",
Heading = "6. 기대 효과 및 결론",
Level = 1
};
num = 3;
List<string> list35 = new List<string>(num);
CollectionsMarshal.SetCount(list35, num);
Span<string> span28 = CollectionsMarshal.AsSpan(list35);
span28[0] = "정량적 효과";
span28[1] = "정성적 효과";
span28[2] = "결론";
obj29.KeyPoints = list35;
list29.Add(obj29);
result = list29;
break;
}
case "analysis":
{
List<SectionPlan> list22 = new List<SectionPlan>();
SectionPlan obj18 = new SectionPlan
{
Id = "sec-1",
Heading = "1. 분석 개요",
Level = 1
};
int num = 3;
List<string> list23 = new List<string>(num);
CollectionsMarshal.SetCount(list23, num);
Span<string> span17 = CollectionsMarshal.AsSpan(list23);
span17[0] = "분석 목적";
span17[1] = "분석 범위";
span17[2] = "방법론";
obj18.KeyPoints = list23;
list22.Add(obj18);
SectionPlan obj19 = new SectionPlan
{
Id = "sec-2",
Heading = "2. 데이터 현황",
Level = 1
};
num = 3;
List<string> list24 = new List<string>(num);
CollectionsMarshal.SetCount(list24, num);
Span<string> span18 = CollectionsMarshal.AsSpan(list24);
span18[0] = "데이터 출처";
span18[1] = "기본 통계";
span18[2] = "데이터 품질";
obj19.KeyPoints = list24;
list22.Add(obj19);
SectionPlan obj20 = new SectionPlan
{
Id = "sec-3",
Heading = "3. 정량 분석",
Level = 1
};
num = 3;
List<string> list25 = new List<string>(num);
CollectionsMarshal.SetCount(list25, num);
Span<string> span19 = CollectionsMarshal.AsSpan(list25);
span19[0] = "수치 분석";
span19[1] = "추세";
span19[2] = "비교";
obj20.KeyPoints = list25;
list22.Add(obj20);
SectionPlan obj21 = new SectionPlan
{
Id = "sec-4",
Heading = "4. 정성 분석",
Level = 1
};
num = 3;
List<string> list26 = new List<string>(num);
CollectionsMarshal.SetCount(list26, num);
Span<string> span20 = CollectionsMarshal.AsSpan(list26);
span20[0] = "패턴";
span20[1] = "인사이트";
span20[2] = "이상치";
obj21.KeyPoints = list26;
list22.Add(obj21);
SectionPlan obj22 = new SectionPlan
{
Id = "sec-5",
Heading = "5. 종합 해석",
Level = 1
};
num = 2;
List<string> list27 = new List<string>(num);
CollectionsMarshal.SetCount(list27, num);
Span<string> span21 = CollectionsMarshal.AsSpan(list27);
span21[0] = "핵심 발견";
span21[1] = "시사점";
obj22.KeyPoints = list27;
list22.Add(obj22);
SectionPlan obj23 = new SectionPlan
{
Id = "sec-6",
Heading = "6. 결론 및 권장사항",
Level = 1
};
num = 3;
List<string> list28 = new List<string>(num);
CollectionsMarshal.SetCount(list28, num);
Span<string> span22 = CollectionsMarshal.AsSpan(list28);
span22[0] = "결론";
span22[1] = "조치 방안";
span22[2] = "추가 분석 필요 항목";
obj23.KeyPoints = list28;
list22.Add(obj23);
result = list22;
break;
}
case "manual":
case "guide":
{
List<SectionPlan> list16 = new List<SectionPlan>();
SectionPlan obj13 = new SectionPlan
{
Id = "sec-1",
Heading = "1. 소개",
Level = 1
};
int num = 3;
List<string> list17 = new List<string>(num);
CollectionsMarshal.SetCount(list17, num);
Span<string> span12 = CollectionsMarshal.AsSpan(list17);
span12[0] = "목적";
span12[1] = "대상 독자";
span12[2] = "사전 요구사항";
obj13.KeyPoints = list17;
list16.Add(obj13);
SectionPlan obj14 = new SectionPlan
{
Id = "sec-2",
Heading = "2. 시작하기",
Level = 1
};
num = 3;
List<string> list18 = new List<string>(num);
CollectionsMarshal.SetCount(list18, num);
Span<string> span13 = CollectionsMarshal.AsSpan(list18);
span13[0] = "설치";
span13[1] = "초기 설정";
span13[2] = "기본 사용법";
obj14.KeyPoints = list18;
list16.Add(obj14);
SectionPlan obj15 = new SectionPlan
{
Id = "sec-3",
Heading = "3. 주요 기능",
Level = 1
};
num = 2;
List<string> list19 = new List<string>(num);
CollectionsMarshal.SetCount(list19, num);
Span<string> span14 = CollectionsMarshal.AsSpan(list19);
span14[0] = "기능별 상세 설명";
span14[1] = "사용 예시";
obj15.KeyPoints = list19;
list16.Add(obj15);
SectionPlan obj16 = new SectionPlan
{
Id = "sec-4",
Heading = "4. 고급 기능",
Level = 1
};
num = 3;
List<string> list20 = new List<string>(num);
CollectionsMarshal.SetCount(list20, num);
Span<string> span15 = CollectionsMarshal.AsSpan(list20);
span15[0] = "고급 설정";
span15[1] = "커스터마이징";
span15[2] = "자동화";
obj16.KeyPoints = list20;
list16.Add(obj16);
SectionPlan obj17 = new SectionPlan
{
Id = "sec-5",
Heading = "5. 문제 해결",
Level = 1
};
num = 3;
List<string> list21 = new List<string>(num);
CollectionsMarshal.SetCount(list21, num);
Span<string> span16 = CollectionsMarshal.AsSpan(list21);
span16[0] = "자주 묻는 질문";
span16[1] = "에러 대응";
span16[2] = "지원 정보";
obj17.KeyPoints = list21;
list16.Add(obj17);
result = list16;
break;
}
case "minutes":
{
List<SectionPlan> list10 = new List<SectionPlan>();
SectionPlan obj8 = new SectionPlan
{
Id = "sec-1",
Heading = "1. 회의 정보",
Level = 1
};
int num = 3;
List<string> list11 = new List<string>(num);
CollectionsMarshal.SetCount(list11, num);
Span<string> span7 = CollectionsMarshal.AsSpan(list11);
span7[0] = "일시";
span7[1] = "참석자";
span7[2] = "장소";
obj8.KeyPoints = list11;
list10.Add(obj8);
SectionPlan obj9 = new SectionPlan
{
Id = "sec-2",
Heading = "2. 안건 및 논의",
Level = 1
};
num = 2;
List<string> list12 = new List<string>(num);
CollectionsMarshal.SetCount(list12, num);
Span<string> span8 = CollectionsMarshal.AsSpan(list12);
span8[0] = "안건별 논의 내용";
span8[1] = "주요 의견";
obj9.KeyPoints = list12;
list10.Add(obj9);
SectionPlan obj10 = new SectionPlan
{
Id = "sec-3",
Heading = "3. 결정 사항",
Level = 1
};
num = 2;
List<string> list13 = new List<string>(num);
CollectionsMarshal.SetCount(list13, num);
Span<string> span9 = CollectionsMarshal.AsSpan(list13);
span9[0] = "합의 내용";
span9[1] = "변경 사항";
obj10.KeyPoints = list13;
list10.Add(obj10);
SectionPlan obj11 = new SectionPlan
{
Id = "sec-4",
Heading = "4. 액션 아이템",
Level = 1
};
num = 3;
List<string> list14 = new List<string>(num);
CollectionsMarshal.SetCount(list14, num);
Span<string> span10 = CollectionsMarshal.AsSpan(list14);
span10[0] = "담당자";
span10[1] = "기한";
span10[2] = "세부 내용";
obj11.KeyPoints = list14;
list10.Add(obj11);
SectionPlan obj12 = new SectionPlan
{
Id = "sec-5",
Heading = "5. 다음 회의",
Level = 1
};
num = 2;
List<string> list15 = new List<string>(num);
CollectionsMarshal.SetCount(list15, num);
Span<string> span11 = CollectionsMarshal.AsSpan(list15);
span11[0] = "예정일";
span11[1] = "주요 안건";
obj12.KeyPoints = list15;
list10.Add(obj12);
result = list10;
break;
}
default:
{
List<SectionPlan> list3 = new List<SectionPlan>();
SectionPlan obj2 = new SectionPlan
{
Id = "sec-1",
Heading = "1. 개요",
Level = 1
};
int num = 3;
List<string> list4 = new List<string>(num);
CollectionsMarshal.SetCount(list4, num);
Span<string> span = CollectionsMarshal.AsSpan(list4);
span[0] = "배경";
span[1] = "목적";
span[2] = "범위";
obj2.KeyPoints = list4;
list3.Add(obj2);
SectionPlan obj3 = new SectionPlan
{
Id = "sec-2",
Heading = "2. 현황",
Level = 1
};
num = 2;
List<string> list5 = new List<string>(num);
CollectionsMarshal.SetCount(list5, num);
Span<string> span2 = CollectionsMarshal.AsSpan(list5);
span2[0] = "현재 상태";
span2[1] = "주요 지표";
obj3.KeyPoints = list5;
list3.Add(obj3);
SectionPlan obj4 = new SectionPlan
{
Id = "sec-3",
Heading = "3. 분석",
Level = 1
};
num = 3;
List<string> list6 = new List<string>(num);
CollectionsMarshal.SetCount(list6, num);
Span<string> span3 = CollectionsMarshal.AsSpan(list6);
span3[0] = "데이터 분석";
span3[1] = "비교";
span3[2] = "추세";
obj4.KeyPoints = list6;
list3.Add(obj4);
SectionPlan obj5 = new SectionPlan
{
Id = "sec-4",
Heading = "4. 주요 발견",
Level = 1
};
num = 3;
List<string> list7 = new List<string>(num);
CollectionsMarshal.SetCount(list7, num);
Span<string> span4 = CollectionsMarshal.AsSpan(list7);
span4[0] = "핵심 인사이트";
span4[1] = "문제점";
span4[2] = "기회";
obj5.KeyPoints = list7;
list3.Add(obj5);
SectionPlan obj6 = new SectionPlan
{
Id = "sec-5",
Heading = "5. 제안",
Level = 1
};
num = 2;
List<string> list8 = new List<string>(num);
CollectionsMarshal.SetCount(list8, num);
Span<string> span5 = CollectionsMarshal.AsSpan(list8);
span5[0] = "개선 방안";
span5[1] = "실행 계획";
obj6.KeyPoints = list8;
list3.Add(obj6);
SectionPlan obj7 = new SectionPlan
{
Id = "sec-6",
Heading = "6. 결론",
Level = 1
};
num = 3;
List<string> list9 = new List<string>(num);
CollectionsMarshal.SetCount(list9, num);
Span<string> span6 = CollectionsMarshal.AsSpan(list9);
span6[0] = "요약";
span6[1] = "기대 효과";
span6[2] = "향후 과제";
obj7.KeyPoints = list9;
list3.Add(obj7);
result = list3;
break;
}
}
if (1 == 0)
{
}
return result;
}
private static void DistributeWordCount(List<SectionPlan> sections, int totalWords)
{
if (sections.Count != 0)
{
double[] array = new double[sections.Count];
for (int i = 0; i < sections.Count; i++)
{
array[i] = ((i == 0 || i == sections.Count - 1) ? 0.7 : 1.2);
}
double num = array.Sum();
for (int j = 0; j < sections.Count; j++)
{
sections[j].TargetWords = Math.Max(100, (int)((double)totalWords * array[j] / num));
}
}
}
private static string SanitizeFileName(string name)
{
string text = ((name.Length > 60) ? name.Substring(0, 60) : name);
char[] invalidFileNameChars = Path.GetInvalidFileNameChars();
foreach (char oldChar in invalidFileNameChars)
{
text = text.Replace(oldChar, '_');
}
return text.Trim().TrimEnd('.');
}
private static string GetDocTypeLabel(string docType)
{
if (1 == 0)
{
}
string result;
switch (docType)
{
case "proposal":
result = "제안서";
break;
case "analysis":
result = "분석 보고서";
break;
case "manual":
case "guide":
result = "매뉴얼/가이드";
break;
case "minutes":
result = "회의록";
break;
case "presentation":
result = "프레젠테이션";
break;
default:
result = "보고서";
break;
}
if (1 == 0)
{
}
return result;
}
private static string Escape(string text)
{
return text.Replace("&", "&amp;").Replace("<", "&lt;").Replace(">", "&gt;");
}
}