@namespace Shared.Pages @page "/decks/{Name}" @deck?.Name
Back to Decks @if (deck == null) {

Deck not found.

} else {

@deck.Name

Deck Code:
@deck.DeckCode
@if (deck.Factions.Count > 0) {
@foreach (var f in deck.Factions) { @f }
}
@deck.Cards.Count cards @(deck.IsValid ? "Valid" : "Invalid") @if (!deck.IsValid) { @deck.ValidationMessage }
@{ var distribution = GetManaDistribution(); var maxCount = distribution.Max(x => x.Count); }
@foreach (var entry in distribution) { var height = maxCount > 0 ? (entry.Count * 100 / maxCount) : 0;
@entry.Count
@(entry.Cost >= 10 ? "10+" : entry.Cost.ToString())
}
@if (deck.Keycards.Count > 0) {

Keycards

@foreach (var cardName in deck.Keycards) { var card = LookupCard(cardName); }
} @if (deck.Divers.Count > 0) {

Divers

@foreach (var cardName in deck.Divers) { var card = LookupCard(cardName); }
}

Cards

@foreach (var group in deck.Cards.GroupBy(x => x)) { var cardName = group.Key; var count = group.Count(); var card = LookupCard(cardName); }
@if (deck.Description != null) {

Description

@RenderDescription(deck.Description)
}
}
@if (selectedCard != null) {
@if (HasImage) {
@selectedCard.Name
}

@selectedCard.Name

@selectedCard.Category @if (selectedCard.Cost.HasValue) { @selectedCard.Cost } @if (selectedCard.Attack.HasValue) { @selectedCard.Attack } @if (selectedCard.Health.HasValue) { @selectedCard.Health } @if (selectedCard.Speed != null) { @selectedCard.Speed }
@if (selectedCard.Faction != null) {
Faction @selectedCard.Faction
} @if (selectedCard.Description != null) {
@RenderCardDescription(selectedCard.Description)
} @if (selectedCard.Set != null) {
Set @selectedCard.Set
} @if (selectedCard.Archetypes is { Count: > 0 }) {
Archetypes @string.Join(", ", selectedCard.Archetypes)
} @if (selectedCard.ImmortalizeWhen != null) {
Immortalize When @selectedCard.ImmortalizeWhen
} @if (selectedCard.HasImmortalize) {
Immortalizes To @foreach (var name in selectedCard.ImmortalizeTo!) { var targetName = name; var target = LookupCard(targetName); if (target != null) { } else { @targetName } }
} @if (selectedCard.ImmortalizeFrom != null) {
Immortalizes From @{ var fromCard = LookupCard(selectedCard.ImmortalizeFrom); if (fromCard != null) { } else { @selectedCard.ImmortalizeFrom } }
}
} @code { [Parameter] public string Name { get; set; } = ""; private DeckData? deck; private CardData? selectedCard; private bool HasImage => selectedCard?.ImageFile is { Length: > 0 } && selectedCard.ImageFile != "placeholder.png"; protected override void OnParametersSet() { var decoded = Uri.UnescapeDataString(Name); deck = DeckDatabase.Decks.FirstOrDefault(d => d.IsVisible && d.Name == decoded); } private CardData? LookupCard(string cardName) { return CardDatabase.Cards.FirstOrDefault(c => string.Equals(c.Name, cardName, StringComparison.OrdinalIgnoreCase)); } private void SelectCard(CardData? card) { selectedCard = card; } private void CloseDetail() { selectedCard = null; } private List<(int Cost, int Count)> GetManaDistribution() { if (deck == null) return []; var counts = new Dictionary(); for (int i = 0; i <= 10; i++) counts[i] = 0; foreach (var cardName in deck.Cards) { var card = LookupCard(cardName); if (card?.Cost != null) { var cost = card.Cost.Value; if (cost >= 10) cost = 10; counts[cost]++; } } return counts.OrderBy(x => x.Key).Select(x => (x.Key, x.Value)).ToList(); } private RenderFragment RenderDescription(string description) { return builder => { var cardNames = CardDatabase.Cards .Select(c => c.Name) .Where(n => !string.IsNullOrWhiteSpace(n)) .OrderByDescending(n => n.Length) .ToList(); int sequence = 0; var paragraphs = description.Split('\n', StringSplitOptions.RemoveEmptyEntries); foreach (var paragraph in paragraphs) { builder.OpenElement(sequence++, "p"); var currentText = paragraph; while (!string.IsNullOrEmpty(currentText)) { int bestMatchIndex = -1; string? bestMatchName = null; foreach (var name in cardNames) { int index = currentText.IndexOf(name, StringComparison.OrdinalIgnoreCase); if (index != -1 && (bestMatchIndex == -1 || index < bestMatchIndex)) { bestMatchIndex = index; bestMatchName = name; } } if (bestMatchIndex != -1 && bestMatchName != null) { if (bestMatchIndex > 0) { builder.AddContent(sequence++, currentText.Substring(0, bestMatchIndex)); } var card = LookupCard(bestMatchName); builder.OpenElement(sequence++, "button"); builder.AddAttribute(sequence++, "class", "inline-card-btn"); builder.AddAttribute(sequence++, "onclick", EventCallback.Factory.Create(this, () => SelectCard(card))); builder.AddContent(sequence++, bestMatchName); builder.CloseElement(); currentText = currentText.Substring(bestMatchIndex + bestMatchName.Length); } else { builder.AddContent(sequence++, currentText); currentText = string.Empty; } } builder.CloseElement(); } }; } private RenderFragment RenderCardDescription(string description) => builder => { var cardNames = CardDatabase.Cards .Select(c => c.Name) .Where(n => !string.IsNullOrWhiteSpace(n)) .OrderByDescending(n => n.Length) .ToList(); int seq = 0; var remaining = description; while (!string.IsNullOrEmpty(remaining)) { int bestIdx = -1; string? bestName = null; foreach (var name in cardNames) { int idx = remaining.IndexOf(name, StringComparison.OrdinalIgnoreCase); if (idx != -1 && (bestIdx == -1 || idx < bestIdx)) { bestIdx = idx; bestName = name; } } if (bestIdx != -1 && bestName != null) { if (bestIdx > 0) builder.AddContent(seq++, remaining[..bestIdx]); var card = LookupCard(bestName); if (card != null) { builder.OpenElement(seq++, "button"); builder.AddAttribute(seq++, "class", "inline-card-btn"); builder.AddAttribute(seq++, "onclick", EventCallback.Factory.Create(this, () => SelectCard(card))); builder.AddContent(seq++, bestName); builder.CloseElement(); } else { builder.AddContent(seq++, bestName); } remaining = remaining[(bestIdx + bestName.Length)..]; } else { builder.AddContent(seq++, remaining); remaining = ""; } } }; }