diff --git a/ET/Console/Console.csproj b/ET/Console/Console.csproj new file mode 100644 index 0000000..6c1dc92 --- /dev/null +++ b/ET/Console/Console.csproj @@ -0,0 +1,10 @@ + + + + Exe + net10.0 + enable + enable + + + diff --git a/ET/Console/Program.cs b/ET/Console/Program.cs new file mode 100644 index 0000000..8962163 --- /dev/null +++ b/ET/Console/Program.cs @@ -0,0 +1,92 @@ +using System.Text.RegularExpressions; + +var exeDir = AppContext.BaseDirectory; + +var solutionDir = FindContainingDir(exeDir, "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")); + +Console.WriteLine($"Source: {srcDir}"); +Console.WriteLine($"Destination: {dstDir}"); + +if (!Directory.Exists(srcDir)) +{ + Console.Error.WriteLine($"Source directory not found: {srcDir}"); + return 1; +} + +Directory.CreateDirectory(dstDir); + +foreach (var file in Directory.GetFiles(dstDir)) + File.Delete(file); + +var entries = new List(); + +foreach (var file in Directory.EnumerateFiles(srcDir, "*.md")) +{ + var name = Path.GetFileNameWithoutExtension(file); + var slug = Slugify(name); + + File.Copy(file, Path.Combine(dstDir, $"{slug}.md"), overwrite: true); + + string? category = 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*(.+)$"); + if (catMatch.Success) + category = catMatch.Groups[1].Value.Trim().Trim('"'); + } + + var entry = new Dictionary + { + ["slug"] = slug, + ["title"] = name + }; + if (category != null) + entry["category"] = category; + + entries.Add(entry); +} + +var index = new Dictionary { ["notes"] = entries }; +var json = System.Text.Json.JsonSerializer.Serialize(index, new System.Text.Json.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 0; + +static string Slugify(string name) +{ + var slug = name.ToLowerInvariant() + .Replace("'", "") + .Replace(".", "") + .Replace("(", "") + .Replace(")", ""); + slug = Regex.Replace(slug, @"[^a-z0-9 -]", ""); + slug = Regex.Replace(slug, @"\s+", "-"); + slug = Regex.Replace(slug, @"-+", "-"); + return slug.Trim('-'); +} + +static string? FindContainingDir(string startDir, string markerFile) +{ + var dir = new DirectoryInfo(startDir); + while (dir != null) + { + if (File.Exists(Path.Combine(dir.FullName, markerFile))) + return dir.FullName; + dir = dir.Parent; + } + return null; +} + diff --git a/ET/ET.sln b/ET/ET.sln index cc08319..c94b85a 100644 --- a/ET/ET.sln +++ b/ET/ET.sln @@ -2,6 +2,8 @@ Microsoft Visual Studio Solution File, Format Version 12.00 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Web", "Web\Web.csproj", "{6E375519-4DF8-455B-9AEC-0C84C31106E9}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Console", "Console\Console.csproj", "{A5946F7D-E8CE-4ACE-8D79-0F5FF0CB11C0}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -12,5 +14,9 @@ Global {6E375519-4DF8-455B-9AEC-0C84C31106E9}.Debug|Any CPU.Build.0 = Debug|Any CPU {6E375519-4DF8-455B-9AEC-0C84C31106E9}.Release|Any CPU.ActiveCfg = Release|Any CPU {6E375519-4DF8-455B-9AEC-0C84C31106E9}.Release|Any CPU.Build.0 = Release|Any CPU + {A5946F7D-E8CE-4ACE-8D79-0F5FF0CB11C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A5946F7D-E8CE-4ACE-8D79-0F5FF0CB11C0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A5946F7D-E8CE-4ACE-8D79-0F5FF0CB11C0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A5946F7D-E8CE-4ACE-8D79-0F5FF0CB11C0}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/ET/Web/Layout/MainLayout.razor b/ET/Web/Layout/MainLayout.razor index 3abdcd9..fdc3d5d 100644 --- a/ET/Web/Layout/MainLayout.razor +++ b/ET/Web/Layout/MainLayout.razor @@ -2,14 +2,8 @@
-
-
- About -
-
@Body
diff --git a/ET/Web/Layout/NavMenu.razor b/ET/Web/Layout/NavMenu.razor index a2979ef..6525fc7 100644 --- a/ET/Web/Layout/NavMenu.razor +++ b/ET/Web/Layout/NavMenu.razor @@ -16,17 +16,7 @@ Home
- - - + @if (groupedNotes == null) { diff --git a/ET/Web/Pages/Counter.razor b/ET/Web/Pages/Counter.razor deleted file mode 100644 index 372905f..0000000 --- a/ET/Web/Pages/Counter.razor +++ /dev/null @@ -1,19 +0,0 @@ -@page "/counter" - -Counter - -

Counter

- -

Current count: @currentCount

- - - -@code { - private int currentCount = 0; - - private void IncrementCount() - { - currentCount++; - } - -} \ No newline at end of file diff --git a/ET/Web/Pages/Weather.razor b/ET/Web/Pages/Weather.razor deleted file mode 100644 index be9adb1..0000000 --- a/ET/Web/Pages/Weather.razor +++ /dev/null @@ -1,60 +0,0 @@ -@page "/weather" -@inject HttpClient Http - -Weather - -

Weather

- -

This component demonstrates fetching data from the server.

- -@if (forecasts == null) -{ -

- Loading... -

-} -else -{ - - - - - - - - - - - @foreach (var forecast in forecasts) - { - - - - - - - } - -
DateTemp. (C)Temp. (F)Summary
@forecast.Date.ToShortDateString()@forecast.TemperatureC@forecast.TemperatureF@forecast.Summary
-} - -@code { - private WeatherForecast[]? forecasts; - - protected override async Task OnInitializedAsync() - { - forecasts = await Http.GetFromJsonAsync("sample-data/weather.json"); - } - - public class WeatherForecast - { - public DateOnly Date { get; set; } - - public int TemperatureC { get; set; } - - public string? Summary { get; set; } - - public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); - } - -} \ No newline at end of file diff --git a/ET/Web/SyncDocs.ps1 b/ET/Web/SyncDocs.ps1 deleted file mode 100644 index 580f59e..0000000 --- a/ET/Web/SyncDocs.ps1 +++ /dev/null @@ -1,35 +0,0 @@ -$src = Resolve-Path "$PSScriptRoot\..\..\docs\Notes" -$dst = "$PSScriptRoot\wwwroot\docs\notes" -if (Test-Path $dst) { Remove-Item "$dst\*" -Force -ErrorAction SilentlyContinue } -New-Item -ItemType Directory -Path $dst -Force | Out-Null - -$entries = @() -Get-ChildItem -Path $src -Filter "*.md" | ForEach-Object { - $base = $_.BaseName - $slug = $base.ToLowerInvariant() - $slug = $slug -replace "'", "" - $slug = $slug -replace "\.", "" - $slug = $slug -replace "[^a-z0-9 -]", "" - $slug = $slug -replace "\s+", "-" - $slug = $slug -replace "-+", "-" - $slug = $slug.Trim('-') - - Copy-Item $_.FullName (Join-Path $dst "$slug.md") - - $category = $null - $content = Get-Content $_.FullName -Raw - if ($content -match '(?s)^---\s*\n(.*?)\n---') { - $frontmatter = $Matches[1] - if ($frontmatter -match '(?m)^category:\s*(.+)$') { - $category = $Matches[1].Trim().Trim('"') - } - } - - $entry = @{ slug = $slug; title = $base } - if ($category) { $entry.category = $category } - $entries += $entry -} - -$indexPath = "$PSScriptRoot\wwwroot\docs\notes-index.json" -$index = @{ notes = $entries } | ConvertTo-Json -Set-Content -Path $indexPath -Value $index -Encoding UTF8 diff --git a/ET/Web/Web.csproj b/ET/Web/Web.csproj index 071f3f1..f719c9a 100644 --- a/ET/Web/Web.csproj +++ b/ET/Web/Web.csproj @@ -14,7 +14,7 @@ - + diff --git a/ET/Web/wwwroot/docs/notes-index.json b/ET/Web/wwwroot/docs/notes-index.json index 2fe084d..e604b33 100644 --- a/ET/Web/wwwroot/docs/notes-index.json +++ b/ET/Web/wwwroot/docs/notes-index.json @@ -1,422 +1,422 @@ -{ - "notes": [ - { - "title": "Artificer", - "category": "Role", - "slug": "artificer" - }, - { - "slug": "awareness", - "title": "Awareness" - }, - { - "title": "Cache Map", - "category": "Gear", - "slug": "cache-map" - }, - { - "slug": "card-library", - "title": "Card Library" - }, - { - "title": "Concoliator", - "category": "Role", - "slug": "concoliator" - }, - { - "slug": "contents", - "title": "Contents" - }, - { - "slug": "crisis-markers", - "title": "Crisis Markers" - }, - { - "slug": "crisis-resolution", - "title": "Crisis Resolution" - }, - { - "slug": "crisis", - "title": "Crisis" - }, - { - "slug": "dirt-roads", - "title": "Dirt Roads" - }, - { - "title": "Dolewood Canoe", - "category": "Gear", - "slug": "dolewood-canoe" - }, - { - "title": "E.03", - "category": "Event", - "slug": "e03" - }, - { - "slug": "ecology", - "title": "Ecology" - }, - { - "slug": "endeavor-tokens", - "title": "Endeavor Tokens" - }, - { - "slug": "energy-tokens", - "title": "Energy Tokens" - }, - { - "slug": "event-cards", - "title": "Event Cards" - }, - { - "slug": "events", - "title": "Events" - }, - { - "slug": "explore-phase", - "title": "Explore Phase" - }, - { - "title": "Explorer", - "category": "Role", - "slug": "explorer" - }, - { - "title": "Ferinodex", - "category": "Gear", - "slug": "ferinodex" - }, - { - "slug": "flora-meeples", - "title": "Flora Meeples" - }, - { - "title": "Forest 1", - "category": "Region", - "slug": "forest-1" - }, - { - "title": "Forest 2", - "category": "Region", - "slug": "forest-2" - }, - { - "title": "Forest 3", - "category": "Region", - "slug": "forest-3" - }, - { - "title": "Forest 4", - "category": "Region", - "slug": "forest-4" - }, - { - "title": "Forest 5", - "category": "Region", - "slug": "forest-5" - }, - { - "slug": "forest", - "title": "Forest" - }, - { - "title": "Gauzeblade", - "category": "Gear", - "slug": "gauzeblade" - }, - { - "slug": "gear-cards", - "title": "Gear Cards" - }, - { - "title": "Grass 1", - "category": "Region", - "slug": "grass-1" - }, - { - "title": "Grass 2", - "category": "Region", - "slug": "grass-2" - }, - { - "title": "Grass 3", - "category": "Region", - "slug": "grass-3" - }, - { - "title": "Grass 4", - "category": "Region", - "slug": "grass-4" - }, - { - "title": "Grass 5", - "category": "Region", - "slug": "grass-5" - }, - { - "slug": "grasslands", - "title": "Grasslands" - }, - { - "title": "Guide", - "category": "Role", - "slug": "guide" - }, - { - "title": "Hidden Trail Map", - "category": "Gear", - "slug": "hidden-trail-map" - }, - { - "slug": "injury-cards", - "title": "Injury Cards" - }, - { - "slug": "lake", - "title": "Lake" - }, - { - "slug": "losing-the-game", - "title": "Losing the Game" - }, - { - "slug": "market", - "title": "Market" - }, - { - "title": "Mountain 1", - "category": "Region", - "slug": "mountain-1" - }, - { - "title": "Mountain 2", - "category": "Region", - "slug": "mountain-2" - }, - { - "title": "Mountain 3", - "category": "Region", - "slug": "mountain-3" - }, - { - "title": "Mountain 4", - "category": "Region", - "slug": "mountain-4" - }, - { - "title": "Mountain 5", - "category": "Region", - "slug": "mountain-5" - }, - { - "slug": "mountain", - "title": "Mountain" - }, - { - "title": "Paratrepsis Whistle", - "category": "Gear", - "slug": "paratrepsis-whistle" - }, - { - "slug": "paved-roads", - "title": "Paved Roads" - }, - { - "title": "Perfect Day 1", - "category": "Event", - "slug": "perfect-day-1" - }, - { - "title": "Phonoscopic Headset", - "category": "Gear", - "slug": "phonoscopic-headset" - }, - { - "slug": "player-boards", - "title": "Player Boards" - }, - { - "slug": "player-meeples", - "title": "Player Meeples" - }, - { - "slug": "predator-meeples", - "title": "Predator Meeples" - }, - { - "slug": "prepare-phase", - "title": "Prepare Phase" - }, - { - "slug": "press-on", - "title": "Press On" - }, - { - "slug": "prey-meeples", - "title": "Prey Meeples" - }, - { - "slug": "progress", - "title": "Progress" - }, - { - "slug": "ranger-badges", - "title": "Ranger Badges" - }, - { - "slug": "ranger-meeples", - "title": "Ranger Meeples" - }, - { - "slug": "research-station", - "title": "Research Station" - }, - { - "slug": "rest", - "title": "Rest" - }, - { - "slug": "role-cards", - "title": "Role Cards" - }, - { - "slug": "round", - "title": "Round" - }, - { - "title": "Ruins Map", - "category": "Gear", - "slug": "ruins-map" - }, - { - "slug": "scout", - "title": "Scout" - }, - { - "title": "Shaper", - "category": "Role", - "slug": "shaper" - }, - { - "title": "Shepard", - "category": "Role", - "slug": "shepard" - }, - { - "slug": "story", - "title": "Story" - }, - { - "slug": "supply", - "title": "Supply" - }, - { - "title": "Terrain 2", - "category": "Terrain", - "slug": "terrain-2" - }, - { - "title": "Terrain 3", - "category": "Terrain", - "slug": "terrain-3" - }, - { - "slug": "terrain-cards", - "title": "Terrain Cards" - }, - { - "title": "Terrain", - "category": "Terrain", - "slug": "terrain" - }, - { - "title": "Totem of the Irix", - "category": "Gear", - "slug": "totem-of-the-irix" - }, - { - "slug": "trade", - "title": "Trade" - }, - { - "title": "Trader", - "category": "Role", - "slug": "trader" - }, - { - "slug": "trail-markers", - "title": "Trail Markers" - }, - { - "slug": "travel-phase", - "title": "Travel Phase" - }, - { - "slug": "traverse", - "title": "Traverse" - }, - { - "slug": "unmaintained-roads", - "title": "Unmaintained Roads" - }, - { - "slug": "valley", - "title": "Valley" - }, - { - "slug": "victory-condition", - "title": "Victory Condition" - }, - { - "title": "Wasteland 1", - "category": "Region", - "slug": "wasteland-1" - }, - { - "slug": "wasteland", - "title": "Wasteland" - }, - { - "title": "Water 1", - "category": "Region", - "slug": "water-1" - }, - { - "title": "Water 2", - "category": "Region", - "slug": "water-2" - }, - { - "title": "Water 3", - "category": "Region", - "slug": "water-3" - }, - { - "title": "Water 4", - "category": "Region", - "slug": "water-4" - }, - { - "title": "Water 5", - "category": "Region", - "slug": "water-5" - }, - { - "slug": "weather", - "title": "Weather" - }, - { - "slug": "white-sky", - "title": "White Sky" - }, - { - "slug": "xp-cubes", - "title": "XP Cubes" - }, - { - "slug": "xp", - "title": "XP" - } - ] -} +{ + "notes": [ + { + "slug": "artificer", + "title": "Artificer", + "category": "Role" + }, + { + "slug": "awareness", + "title": "Awareness" + }, + { + "slug": "cache-map", + "title": "Cache Map", + "category": "Gear" + }, + { + "slug": "card-library", + "title": "Card Library" + }, + { + "slug": "concoliator", + "title": "Concoliator", + "category": "Role" + }, + { + "slug": "contents", + "title": "Contents" + }, + { + "slug": "crisis-markers", + "title": "Crisis Markers" + }, + { + "slug": "crisis-resolution", + "title": "Crisis Resolution" + }, + { + "slug": "crisis", + "title": "Crisis" + }, + { + "slug": "dirt-roads", + "title": "Dirt Roads" + }, + { + "slug": "dolewood-canoe", + "title": "Dolewood Canoe", + "category": "Gear" + }, + { + "slug": "e03", + "title": "E.03", + "category": "Event" + }, + { + "slug": "ecology", + "title": "Ecology" + }, + { + "slug": "endeavor-tokens", + "title": "Endeavor Tokens" + }, + { + "slug": "energy-tokens", + "title": "Energy Tokens" + }, + { + "slug": "event-cards", + "title": "Event Cards" + }, + { + "slug": "events", + "title": "Events" + }, + { + "slug": "explore-phase", + "title": "Explore Phase" + }, + { + "slug": "explorer", + "title": "Explorer", + "category": "Role" + }, + { + "slug": "ferinodex", + "title": "Ferinodex", + "category": "Gear" + }, + { + "slug": "flora-meeples", + "title": "Flora Meeples" + }, + { + "slug": "forest-1", + "title": "Forest 1", + "category": "Region" + }, + { + "slug": "forest-2", + "title": "Forest 2", + "category": "Region" + }, + { + "slug": "forest-3", + "title": "Forest 3", + "category": "Region" + }, + { + "slug": "forest-4", + "title": "Forest 4", + "category": "Region" + }, + { + "slug": "forest-5", + "title": "Forest 5", + "category": "Region" + }, + { + "slug": "forest", + "title": "Forest" + }, + { + "slug": "gauzeblade", + "title": "Gauzeblade", + "category": "Gear" + }, + { + "slug": "gear-cards", + "title": "Gear Cards" + }, + { + "slug": "grass-1", + "title": "Grass 1", + "category": "Region" + }, + { + "slug": "grass-2", + "title": "Grass 2", + "category": "Region" + }, + { + "slug": "grass-3", + "title": "Grass 3", + "category": "Region" + }, + { + "slug": "grass-4", + "title": "Grass 4", + "category": "Region" + }, + { + "slug": "grass-5", + "title": "Grass 5", + "category": "Region" + }, + { + "slug": "grasslands", + "title": "Grasslands" + }, + { + "slug": "guide", + "title": "Guide", + "category": "Role" + }, + { + "slug": "hidden-trail-map", + "title": "Hidden Trail Map", + "category": "Gear" + }, + { + "slug": "injury-cards", + "title": "Injury Cards" + }, + { + "slug": "lake", + "title": "Lake" + }, + { + "slug": "losing-the-game", + "title": "Losing the Game" + }, + { + "slug": "market", + "title": "Market" + }, + { + "slug": "mountain-1", + "title": "Mountain 1", + "category": "Region" + }, + { + "slug": "mountain-2", + "title": "Mountain 2", + "category": "Region" + }, + { + "slug": "mountain-3", + "title": "Mountain 3", + "category": "Region" + }, + { + "slug": "mountain-4", + "title": "Mountain 4", + "category": "Region" + }, + { + "slug": "mountain-5", + "title": "Mountain 5", + "category": "Region" + }, + { + "slug": "mountain", + "title": "Mountain" + }, + { + "slug": "paratrepsis-whistle", + "title": "Paratrepsis Whistle", + "category": "Gear" + }, + { + "slug": "paved-roads", + "title": "Paved Roads" + }, + { + "slug": "perfect-day-1", + "title": "Perfect Day 1", + "category": "Event" + }, + { + "slug": "phonoscopic-headset", + "title": "Phonoscopic Headset", + "category": "Gear" + }, + { + "slug": "player-boards", + "title": "Player Boards" + }, + { + "slug": "player-meeples", + "title": "Player Meeples" + }, + { + "slug": "predator-meeples", + "title": "Predator Meeples" + }, + { + "slug": "prepare-phase", + "title": "Prepare Phase" + }, + { + "slug": "press-on", + "title": "Press On" + }, + { + "slug": "prey-meeples", + "title": "Prey Meeples" + }, + { + "slug": "progress", + "title": "Progress" + }, + { + "slug": "ranger-badges", + "title": "Ranger Badges" + }, + { + "slug": "ranger-meeples", + "title": "Ranger Meeples" + }, + { + "slug": "research-station", + "title": "Research Station" + }, + { + "slug": "rest", + "title": "Rest" + }, + { + "slug": "role-cards", + "title": "Role Cards" + }, + { + "slug": "round", + "title": "Round" + }, + { + "slug": "ruins-map", + "title": "Ruins Map", + "category": "Gear" + }, + { + "slug": "scout", + "title": "Scout" + }, + { + "slug": "shaper", + "title": "Shaper", + "category": "Role" + }, + { + "slug": "shepard", + "title": "Shepard", + "category": "Role" + }, + { + "slug": "story", + "title": "Story" + }, + { + "slug": "supply", + "title": "Supply" + }, + { + "slug": "terrain-2", + "title": "Terrain 2", + "category": "Terrain" + }, + { + "slug": "terrain-3", + "title": "Terrain 3", + "category": "Terrain" + }, + { + "slug": "terrain-cards", + "title": "Terrain Cards" + }, + { + "slug": "terrain", + "title": "Terrain", + "category": "Terrain" + }, + { + "slug": "totem-of-the-irix", + "title": "Totem of the Irix", + "category": "Gear" + }, + { + "slug": "trade", + "title": "Trade" + }, + { + "slug": "trader", + "title": "Trader", + "category": "Role" + }, + { + "slug": "trail-markers", + "title": "Trail Markers" + }, + { + "slug": "travel-phase", + "title": "Travel Phase" + }, + { + "slug": "traverse", + "title": "Traverse" + }, + { + "slug": "unmaintained-roads", + "title": "Unmaintained Roads" + }, + { + "slug": "valley", + "title": "Valley" + }, + { + "slug": "victory-condition", + "title": "Victory Condition" + }, + { + "slug": "wasteland-1", + "title": "Wasteland 1", + "category": "Region" + }, + { + "slug": "wasteland", + "title": "Wasteland" + }, + { + "slug": "water-1", + "title": "Water 1", + "category": "Region" + }, + { + "slug": "water-2", + "title": "Water 2", + "category": "Region" + }, + { + "slug": "water-3", + "title": "Water 3", + "category": "Region" + }, + { + "slug": "water-4", + "title": "Water 4", + "category": "Region" + }, + { + "slug": "water-5", + "title": "Water 5", + "category": "Region" + }, + { + "slug": "weather", + "title": "Weather" + }, + { + "slug": "white-sky", + "title": "White Sky" + }, + { + "slug": "xp-cubes", + "title": "XP Cubes" + }, + { + "slug": "xp", + "title": "XP" + } + ] +} \ No newline at end of file