Day 2 vibes
This commit is contained in:
+162
-30
@@ -1,10 +1,12 @@
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Text;
|
||||
using System.Text.Encodings.Web;
|
||||
using System.Text.Json;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
var exeDir = AppContext.BaseDirectory;
|
||||
|
||||
var solutionDir = FindContainingDir(exeDir, "ET.sln")
|
||||
?? throw new InvalidOperationException("Cannot find ET.sln");
|
||||
?? throw new InvalidOperationException("Cannot find ET.sln");
|
||||
|
||||
var srcDir = Path.GetFullPath(Path.Combine(solutionDir, "..", "docs", "Notes"));
|
||||
var dstDir = Path.GetFullPath(Path.Combine(solutionDir, "Web", "wwwroot", "docs", "notes"));
|
||||
@@ -18,13 +20,18 @@ if (!Directory.Exists(srcDir))
|
||||
return 1;
|
||||
}
|
||||
|
||||
SyncNotes(srcDir, dstDir);
|
||||
var entries = SyncNotes(srcDir, dstDir);
|
||||
CopyOverview(solutionDir, dstDir, entries);
|
||||
CopyImages(solutionDir, Path.GetFullPath(Path.Combine(solutionDir, "Web", "wwwroot", "docs")));
|
||||
var indexJson = BuildIndex(entries);
|
||||
File.WriteAllText(Path.Combine(Path.GetDirectoryName(dstDir)!, "notes-index.json"), indexJson);
|
||||
GenerateMap(srcDir, Path.GetFullPath(Path.Combine(solutionDir, "Web", "wwwroot", "docs")));
|
||||
GenerateOverviewPage(solutionDir);
|
||||
|
||||
Console.WriteLine("Done.");
|
||||
return 0;
|
||||
|
||||
static void SyncNotes(string srcDir, string dstDir)
|
||||
static List<object> SyncNotes(string srcDir, string dstDir)
|
||||
{
|
||||
Directory.CreateDirectory(dstDir);
|
||||
|
||||
@@ -38,16 +45,33 @@ static void SyncNotes(string srcDir, string dstDir)
|
||||
var name = Path.GetFileNameWithoutExtension(file);
|
||||
var slug = Slugify(name);
|
||||
|
||||
File.Copy(file, Path.Combine(dstDir, $"{slug}.md"), overwrite: true);
|
||||
File.Copy(file, Path.Combine(dstDir, $"{slug}.md"), true);
|
||||
|
||||
string? category = null;
|
||||
string? cost = null;
|
||||
string? gearCategory = null;
|
||||
string? effect = null;
|
||||
string? location = null;
|
||||
var content = File.ReadAllText(file);
|
||||
var fmMatch = Regex.Match(content, @"^---\s*\n(.*?)\n---", RegexOptions.Singleline);
|
||||
if (fmMatch.Success)
|
||||
{
|
||||
var catMatch = Regex.Match(fmMatch.Groups[1].Value, @"(?m)^category:\s*(.+)$");
|
||||
var fm = fmMatch.Groups[1].Value;
|
||||
var catMatch = Regex.Match(fm, @"(?m)^category:\s*(.+)$", RegexOptions.IgnoreCase);
|
||||
if (catMatch.Success)
|
||||
category = catMatch.Groups[1].Value.Trim().Trim('"');
|
||||
var costMatch = Regex.Match(fm, @"(?m)^cost:\s*(.+)$", RegexOptions.IgnoreCase);
|
||||
if (costMatch.Success)
|
||||
cost = costMatch.Groups[1].Value.Trim().Trim('"');
|
||||
var gcMatch = Regex.Match(fm, @"(?m)^gear category:\s*(.+)$", RegexOptions.IgnoreCase);
|
||||
if (gcMatch.Success)
|
||||
gearCategory = gcMatch.Groups[1].Value.Trim().Trim('"');
|
||||
var effMatch = Regex.Match(fm, @"(?m)^effect:\s*(.+)$", RegexOptions.IgnoreCase);
|
||||
if (effMatch.Success)
|
||||
effect = effMatch.Groups[1].Value.Trim().Trim('"');
|
||||
var locMatch = Regex.Match(fm, @"(?m)^location:\s*(.+)$", RegexOptions.IgnoreCase);
|
||||
if (locMatch.Success)
|
||||
location = locMatch.Groups[1].Value.Trim().Trim('"');
|
||||
}
|
||||
|
||||
var entry = new Dictionary<string, object?>
|
||||
@@ -57,18 +81,122 @@ static void SyncNotes(string srcDir, string dstDir)
|
||||
};
|
||||
if (category != null)
|
||||
entry["category"] = category;
|
||||
if (cost != null)
|
||||
entry["cost"] = cost;
|
||||
if (gearCategory != null)
|
||||
entry["gearCategory"] = gearCategory;
|
||||
if (effect != null)
|
||||
entry["effect"] = effect;
|
||||
if (location != null)
|
||||
entry["location"] = location;
|
||||
|
||||
entries.Add(entry);
|
||||
}
|
||||
|
||||
var index = new Dictionary<string, object> { ["notes"] = entries };
|
||||
var json = JsonSerializer.Serialize(index, new JsonSerializerOptions { WriteIndented = true });
|
||||
|
||||
var indexPath = Path.Combine(Path.GetDirectoryName(dstDir)!, "notes-index.json");
|
||||
File.WriteAllText(indexPath, json);
|
||||
|
||||
Console.WriteLine($"Copied {entries.Count} notes.");
|
||||
Console.WriteLine($"Index written to: {indexPath}");
|
||||
return entries;
|
||||
}
|
||||
|
||||
static void CopyOverview(string solutionDir, string dstDir, List<object> entries)
|
||||
{
|
||||
var srcOverview = Path.GetFullPath(Path.Combine(solutionDir, "..", "docs", "Overview.md"));
|
||||
if (!File.Exists(srcOverview)) return;
|
||||
|
||||
var slug = "overview";
|
||||
File.Copy(srcOverview, Path.Combine(dstDir, $"{slug}.md"), true);
|
||||
|
||||
entries.Add(new Dictionary<string, object?>
|
||||
{
|
||||
["slug"] = slug,
|
||||
["title"] = "Overview",
|
||||
["category"] = "Overview"
|
||||
});
|
||||
|
||||
Console.WriteLine("Copied Overview.md.");
|
||||
}
|
||||
|
||||
static void CopyImages(string solutionDir, string dstDir)
|
||||
{
|
||||
var srcImgs = Path.GetFullPath(Path.Combine(solutionDir, "..", "docs", "Images"));
|
||||
if (!Directory.Exists(srcImgs)) return;
|
||||
|
||||
var dstImgs = Path.Combine(dstDir, "images");
|
||||
Directory.CreateDirectory(dstImgs);
|
||||
|
||||
foreach (var file in Directory.EnumerateFiles(srcImgs))
|
||||
{
|
||||
var name = Path.GetFileName(file);
|
||||
File.Copy(file, Path.Combine(dstImgs, name), true);
|
||||
}
|
||||
|
||||
Console.WriteLine($"Copied images from {srcImgs}.");
|
||||
}
|
||||
|
||||
static string BuildIndex(List<object> entries)
|
||||
{
|
||||
var index = new Dictionary<string, object> { ["notes"] = entries };
|
||||
return JsonSerializer.Serialize(index, new JsonSerializerOptions { WriteIndented = true });
|
||||
}
|
||||
|
||||
static void GenerateOverviewPage(string solutionDir)
|
||||
{
|
||||
var pagesDir = Path.GetFullPath(Path.Combine(solutionDir, "Web", "Pages"));
|
||||
Directory.CreateDirectory(pagesDir);
|
||||
|
||||
var component = """
|
||||
@page "/overview"
|
||||
@inject DocsService DocsService
|
||||
|
||||
<PageTitle>Overview</PageTitle>
|
||||
|
||||
@if (loading)
|
||||
{
|
||||
<div class="d-flex justify-content-center py-5">
|
||||
<div class="spinner-border text-success" role="status">
|
||||
<span class="visually-hidden">Loading...</span>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
else if (doc == null)
|
||||
{
|
||||
<h1>Overview Not Found</h1>
|
||||
<p>The overview document could not be found.</p>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="section-header d-flex align-items-center mb-4">
|
||||
<h1 class="mb-0">@doc.Title</h1>
|
||||
<div class="ms-3 flex-grow-1 border-bottom opacity-25"></div>
|
||||
</div>
|
||||
|
||||
@if (!string.IsNullOrEmpty(doc.FrontmatterHtml))
|
||||
{
|
||||
<details class="frontmatter-section" open>
|
||||
<summary>Frontmatter</summary>
|
||||
@((MarkupString)doc.FrontmatterHtml)
|
||||
</details>
|
||||
}
|
||||
|
||||
<div class="markdown-body overview-markdown">
|
||||
@((MarkupString)doc.HtmlContent)
|
||||
</div>
|
||||
}
|
||||
|
||||
@code {
|
||||
private NoteDocument? doc;
|
||||
private bool loading = true;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
doc = await DocsService.GetNoteAsync("overview");
|
||||
loading = false;
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
var overviewPagePath = Path.Combine(pagesDir, "Overview.razor");
|
||||
File.WriteAllText(overviewPagePath, component);
|
||||
Console.WriteLine($"Overview page written to: {overviewPagePath}");
|
||||
}
|
||||
|
||||
static void GenerateMap(string srcDir, string dstDir)
|
||||
@@ -79,7 +207,7 @@ static void GenerateMap(string srcDir, string dstDir)
|
||||
["Forest"] = "#2e7d32",
|
||||
["Mountain"] = "#78909c",
|
||||
["Water"] = "#42a5f5",
|
||||
["Wasteland"] = "#8d6e63",
|
||||
["Wasteland"] = "#8d6e63"
|
||||
};
|
||||
|
||||
var regionFiles = Directory.EnumerateFiles(srcDir, "*.md")
|
||||
@@ -130,19 +258,19 @@ static void GenerateMap(string srcDir, string dstDir)
|
||||
var srcMapImage = Path.GetFullPath(Path.Combine(srcDir, "..", "Images", "Map.png"));
|
||||
var dstMapImage = Path.Combine(dstDir, "Map.png");
|
||||
if (File.Exists(srcMapImage))
|
||||
File.Copy(srcMapImage, dstMapImage, overwrite: true);
|
||||
File.Copy(srcMapImage, dstMapImage, true);
|
||||
|
||||
var nameLookup = regions.ToDictionary(r => r.Name);
|
||||
|
||||
var pad = 60;
|
||||
var maxX = (regions.Count > 0 ? regions.Max(r => r.X) : 0) + pad * 2;
|
||||
var maxY = (regions.Count > 0 ? regions.Max(r => r.Y) : 0) + pad * 2;
|
||||
var svg = new System.Text.StringBuilder();
|
||||
var svg = new StringBuilder();
|
||||
svg.AppendLine("<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 700 461\">");
|
||||
|
||||
svg.AppendLine("""<image href="Map.png" width="700" height="461" preserveAspectRatio="xMidYMid meet"/>""");
|
||||
|
||||
svg.AppendLine($"""<defs>""");
|
||||
svg.AppendLine("""<defs>""");
|
||||
foreach (var (terrain, color) in terrainColors)
|
||||
{
|
||||
svg.AppendLine($"""<radialGradient id="glow-{terrain}" cx="50%" cy="50%" r="50%">""");
|
||||
@@ -150,17 +278,18 @@ static void GenerateMap(string srcDir, string dstDir)
|
||||
svg.AppendLine($"""<stop offset="100%" stop-color="{color}" stop-opacity="0"/>""");
|
||||
svg.AppendLine("</radialGradient>");
|
||||
}
|
||||
|
||||
svg.AppendLine("</defs>");
|
||||
|
||||
foreach (var region in regions)
|
||||
foreach (var conn in region.Connections)
|
||||
{
|
||||
foreach (var conn in region.Connections)
|
||||
{
|
||||
if (!nameLookup.TryGetValue(conn, out var target) || string.Compare(region.Name, conn, StringComparison.OrdinalIgnoreCase) >= 0)
|
||||
continue;
|
||||
if (!nameLookup.TryGetValue(conn, out var target) ||
|
||||
string.Compare(region.Name, conn, StringComparison.OrdinalIgnoreCase) >= 0)
|
||||
continue;
|
||||
|
||||
svg.AppendLine($"""<line x1="{region.X}" y1="{region.Y}" x2="{target.X}" y2="{target.Y}" stroke="rgba(255,255,255,0.12)" stroke-width="2" stroke-linecap="round"/>""");
|
||||
}
|
||||
svg.AppendLine(
|
||||
$"""<line x1="{region.X}" y1="{region.Y}" x2="{target.X}" y2="{target.Y}" stroke="rgba(255,255,255,0.12)" stroke-width="2" stroke-linecap="round"/>""");
|
||||
}
|
||||
|
||||
foreach (var region in regions)
|
||||
@@ -171,17 +300,20 @@ static void GenerateMap(string srcDir, string dstDir)
|
||||
|
||||
svg.AppendLine($"""<a href="/docs/{region.Slug}" target="_top">""");
|
||||
svg.AppendLine($"""<circle cx="{cx}" cy="{cy}" r="32" fill="url(#glow-{region.Terrain})"/>""");
|
||||
svg.AppendLine($"""<circle cx="{cx}" cy="{cy}" r="18" fill="{color}cc" stroke="rgba(255,255,255,0.5)" stroke-width="2"/>""");
|
||||
svg.AppendLine(
|
||||
$"""<circle cx="{cx}" cy="{cy}" r="18" fill="{color}cc" stroke="rgba(255,255,255,0.5)" stroke-width="2"/>""");
|
||||
|
||||
var labelX = cx + 24;
|
||||
svg.AppendLine($"""<text x="{labelX}" y="{cy + 4}" fill="rgba(255,255,255,0.9)" font-family="system-ui,sans-serif" font-size="11" font-weight="600">""");
|
||||
svg.Append(System.Text.Encodings.Web.HtmlEncoder.Default.Encode(region.Name));
|
||||
svg.AppendLine(
|
||||
$"""<text x="{labelX}" y="{cy + 4}" fill="rgba(255,255,255,0.9)" font-family="system-ui,sans-serif" font-size="11" font-weight="600">""");
|
||||
svg.Append(HtmlEncoder.Default.Encode(region.Name));
|
||||
svg.AppendLine("</text>");
|
||||
|
||||
foreach (var lm in region.Landmarks)
|
||||
{
|
||||
svg.AppendLine($"""<text x="{labelX}" y="{cy + 18}" fill="rgba(255,255,255,0.45)" font-family="system-ui,sans-serif" font-size="9" font-style="italic">""");
|
||||
svg.Append(System.Text.Encodings.Web.HtmlEncoder.Default.Encode($"\u2605 {lm}"));
|
||||
svg.AppendLine(
|
||||
$"""<text x="{labelX}" y="{cy + 18}" fill="rgba(255,255,255,0.45)" font-family="system-ui,sans-serif" font-size="9" font-style="italic">""");
|
||||
svg.Append(HtmlEncoder.Default.Encode($"\u2605 {lm}"));
|
||||
svg.AppendLine("</text>");
|
||||
}
|
||||
|
||||
@@ -231,10 +363,11 @@ static string? FindContainingDir(string startDir, string markerFile)
|
||||
return dir.FullName;
|
||||
dir = dir.Parent;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
class RegionData
|
||||
internal class RegionData
|
||||
{
|
||||
public string Name { get; set; } = "";
|
||||
public string Slug { get; set; } = "";
|
||||
@@ -244,4 +377,3 @@ class RegionData
|
||||
public List<string> Connections { get; set; } = new();
|
||||
public List<string> Landmarks { get; set; } = new();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user