Agent Tests for API, MAUI, and Slop Features

This commit is contained in:
2026-06-03 19:08:35 -04:00
parent 46150d3a69
commit 0feac0f0a0
142 changed files with 4156 additions and 1462 deletions
@@ -0,0 +1,180 @@
@inject IEntityDialogService entityDialogService
@inject NavigationManager NavigationManager
<div class="techTreeContainer" @ref="containerRef">
<svg class="techTreeSvg" width="@svgWidth" height="@svgHeight">
<!-- Edges -->
@foreach (var edge in Graph.Edges)
{
var source = Graph.Nodes.FirstOrDefault(n => n.Id == edge.SourceId);
var target = Graph.Nodes.FirstOrDefault(n => n.Id == edge.TargetId);
if (source != null && target != null)
{
var color = EdgeColor(edge.EdgeType);
var strokeWidth = edge.EdgeType == TechTreeEdgeType.Produces ? "2.5" : "1.5";
var strokeDash = edge.EdgeType == TechTreeEdgeType.RequiresProduction || edge.EdgeType == TechTreeEdgeType.RequiresResearch ? "6,3" : "";
strokeDash = edge.EdgeType == TechTreeEdgeType.Morph ? "2,2" : strokeDash;
<line x1="@(source.X + nodeWidth / 2)" y1="@(source.Y + nodeHeight / 2)"
x2="@(target.X + nodeWidth / 2)" y2="@(target.Y + nodeHeight / 2)"
stroke="@color" stroke-width="@strokeWidth"
stroke-dasharray="@strokeDash"
marker-end="url(#arrowhead-@(edge.EdgeType))"/>
}
}
<!-- Arrowhead markers -->
<defs>
@foreach (var edgeType in Graph.Edges.Select(e => e.EdgeType).Distinct())
{
<marker id="arrowhead-@edgeType" markerWidth="10" markerHeight="7" refX="10" refY="3.5" orient="auto">
<polygon points="0 0, 10 3.5, 0 7" fill="@EdgeColor(edgeType)"/>
</marker>
}
</defs>
<!-- Nodes -->
@foreach (var node in Graph.Nodes)
{
var color = NodeColor(node.Descriptive);
var isHighlighted = HighlightEntityId == node.Id;
<g class="techTreeNode" @onclick="() => OnNodeClick(node.Id)">
<rect x="@node.X" y="@node.Y" width="@nodeWidth" height="@nodeHeight" rx="6"
fill="var(--paper)" stroke="@color" stroke-width="@(isHighlighted ? 3 : 1.5)"
class="@(isHighlighted ? "highlighted" : "")"/>
<text x="@(node.X + nodeWidth / 2)" y="@(node.Y + nodeHeight / 2)"
text-anchor="middle" dominant-baseline="central"
fill="white" font-size="12" font-weight="600">
@TruncateText(node.Name, 20)
</text>
<title>@node.Name (@node.EntityType)</title>
</g>
}
</svg>
</div>
<style>
.techTreeContainer {
width: 100%;
overflow: auto;
border: 2px solid var(--paper-border);
border-radius: 8px;
background-color: var(--background);
}
.techTreeSvg {
display: block;
}
.techTreeNode {
cursor: pointer;
}
.techTreeNode rect {
transition: stroke-width 0.15s;
}
.techTreeNode rect.highlighted {
filter: drop-shadow(0 0 6px rgba(255, 255, 255, 0.5));
}
.techTreeNode:hover rect {
stroke-width: 3;
}
</style>
@code {
[Parameter] public TechTreeGraphModel Graph { get; set; } = new();
[Parameter] public string? HighlightEntityId { get; set; }
[Parameter] public EventCallback<string> OnEntitySelected { get; set; }
private ElementReference containerRef;
private const float nodeWidth = 140;
private const float nodeHeight = 36;
private const float horizontalSpacing = 60;
private const float verticalSpacing = 30;
private const float topPadding = 40;
private const float leftPadding = 40;
private float svgWidth = 800;
private float svgHeight = 600;
protected override void OnParametersSet()
{
ComputeLayout();
}
void ComputeLayout()
{
if (Graph.Nodes.Count == 0) return;
var layers = Graph.Nodes.GroupBy(n => n.Layer)
.OrderBy(g => g.Key)
.ToList();
// Simple layered layout: left to right (layers are columns)
for (var layerIdx = 0; layerIdx < layers.Count; layerIdx++)
{
var layerNodes = layers.ElementAt(layerIdx).OrderBy(n => n.Name).ToList();
var layerHeight = layerNodes.Count * (nodeHeight + verticalSpacing) - verticalSpacing;
for (var nodeIdx = 0; nodeIdx < layerNodes.Count; nodeIdx++)
{
var node = layerNodes[nodeIdx];
node.X = leftPadding + layerIdx * (nodeWidth + horizontalSpacing);
node.Y = topPadding + nodeIdx * (nodeHeight + verticalSpacing);
}
}
svgWidth = layers.Count * (nodeWidth + horizontalSpacing) + leftPadding + 100;
svgHeight = layers.Max(g => g.Count()) * (nodeHeight + verticalSpacing) + topPadding + 80;
if (svgWidth < 800) svgWidth = 800;
if (svgHeight < 400) svgHeight = 400;
}
void OnNodeClick(string entityId)
{
entityDialogService.AddDialog(entityId);
OnEntitySelected.InvokeAsync(entityId);
}
string NodeColor(string descriptive)
{
return descriptive.ToLowerInvariant() switch
{
"frontliner" => "#ff6b6b",
"skirmisher" => "#ffa502",
"support" => "#2ed573",
"generalist" => "#1e90ff",
"worker" => "#ffd43b",
"stronghold" => "#ff4757",
"upgrade" => "#a55eea",
"technology" => "#a55eea",
_ => "#8fc5ff"
};
}
string EdgeColor(string edgeType)
{
return edgeType switch
{
"Produces" => "#2ed573",
"RequiresProduction" => "#ffa502",
"RequiresResearch" => "#a55eea",
"Morph" => "#ff6b6b",
"Upgrades" => "#1e90ff",
_ => "#666"
};
}
string TruncateText(string text, int maxLength)
{
return text.Length <= maxLength ? text : text.Substring(0, maxLength - 3) + "...";
}
}