4 Commits

Author SHA1 Message Date
JonathanMcCaffrey 2625992014 ... 2026-06-04 11:29:24 -04:00
JonathanMcCaffrey 377a041afa ... 2026-06-04 11:12:23 -04:00
JonathanMcCaffrey 6e58c63082 ... 2026-06-04 11:05:21 -04:00
JonathanMcCaffrey 0feac0f0a0 Agent Tests for API, MAUI, and Slop Features 2026-06-04 10:43:01 -04:00
218 changed files with 4286 additions and 1528 deletions
+18
View File
@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<RootNamespace>API</RootNamespace>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="HotChocolate.AspNetCore" Version="14.0.0"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Model\Model.csproj"/>
</ItemGroup>
</Project>
+254
View File
@@ -0,0 +1,254 @@
using Model.Entity;
using Model.Entity.Parts;
namespace API.GraphQL.Types;
public class EntityGraphType : ObjectType<EntityModel>
{
protected override void Configure(IObjectTypeDescriptor<EntityModel> descriptor)
{
descriptor.Name("Entity");
descriptor.Field(e => e.DataType).Name("id");
descriptor.Field(e => e.EntityType).Name("type");
descriptor.Field(e => e.IsSpeculative);
descriptor.Field(e => e.Descriptive).Name("descriptiveType");
descriptor.Ignore(e => e.EntityParts);
descriptor.Field("name")
.Resolve(ctx => ctx.Parent<EntityModel>().GetName());
descriptor.Field("factionName")
.Resolve(ctx => ctx.Parent<EntityModel>().GetFaction());
descriptor.Field("info")
.Type<EntityInfoGraphType>()
.Resolve(ctx => ctx.Parent<EntityModel>().Info());
descriptor.Field("production")
.Type<EntityProductionGraphType>()
.Resolve(ctx => ctx.Parent<EntityModel>().Production());
descriptor.Field("supply")
.Type<EntitySupplyGraphType>()
.Resolve(ctx => ctx.Parent<EntityModel>().Supply());
descriptor.Field("tier")
.Type<EntityTierGraphType>()
.Resolve(ctx => ctx.Parent<EntityModel>().Tier());
descriptor.Field("movement")
.Type<EntityMovementGraphType>()
.Resolve(ctx => ctx.Parent<EntityModel>().Movement());
descriptor.Field("vitality")
.Type<EntityVitalityGraphType>()
.Resolve(ctx => ctx.Parent<EntityModel>().Vitality());
descriptor.Field("requirements")
.Type<ListType<EntityRequirementGraphType>>()
.Resolve(ctx => ctx.Parent<EntityModel>().Requirements());
descriptor.Field("weapons")
.Type<ListType<EntityWeaponGraphType>>()
.Resolve(ctx => ctx.Parent<EntityModel>().Weapons());
descriptor.Field("hotkey")
.Type<EntityHotkeyGraphType>()
.Resolve(ctx => ctx.Parent<EntityModel>().Hotkey());
descriptor.Field("faction")
.Type<EntityFactionGraphType>()
.Resolve(ctx => ctx.Parent<EntityModel>().Faction());
descriptor.Field("harvest")
.Type<EntityHarvestGraphType>()
.Resolve(ctx => ctx.Parent<EntityModel>().Harvest());
descriptor.Field("idAbilities")
.Type<ListType<EntityIdReferenceGraphType>>()
.Resolve(ctx => ctx.Parent<EntityModel>().IdAbilities());
descriptor.Field("idArmies")
.Type<ListType<EntityIdReferenceGraphType>>()
.Resolve(ctx => ctx.Parent<EntityModel>().IdArmies());
descriptor.Field("idPassives")
.Type<ListType<EntityIdReferenceGraphType>>()
.Resolve(ctx => ctx.Parent<EntityModel>().IdPassives());
descriptor.Field("idUpgrades")
.Type<ListType<EntityIdReferenceGraphType>>()
.Resolve(ctx => ctx.Parent<EntityModel>().IdUpgrades());
descriptor.Field("idVanguards")
.Type<ListType<EntityIdReferenceGraphType>>()
.Resolve(ctx => ctx.Parent<EntityModel>().IdVanguards());
descriptor.Field("idPyreSpells")
.Type<ListType<EntityIdReferenceGraphType>>()
.Resolve(ctx => ctx.Parent<EntityModel>().IdPyreSpells());
descriptor.Field("mechanics")
.Type<ListType<EntityMechanicGraphType>>()
.Resolve(ctx => ctx.Parent<EntityModel>().Mechanics());
descriptor.Field("passives")
.Type<ListType<EntityNamedDescGraphType>>()
.Resolve(ctx => ctx.Parent<EntityModel>().Passives());
descriptor.Field("strategies")
.Type<ListType<EntityStrategyGraphType>>()
.Resolve(ctx => ctx.Parent<EntityModel>().Strategies());
descriptor.Field("replaceds")
.Type<ListType<EntityVanguardReplacedGraphType>>()
.Resolve(ctx => ctx.Parent<EntityModel>().Replaceds());
descriptor.Field("vanguardAdded")
.Type<EntityVanguardAddedGraphType>()
.Resolve(ctx => ctx.Parent<EntityModel>().VanguardAdded());
}
}
public class EntityInfoGraphType : ObjectType<EntityInfoModel>
{
protected override void Configure(IObjectTypeDescriptor<EntityInfoModel> descriptor)
{
descriptor.Ignore(e => e.Parent);
descriptor.Field(e => e.Name);
descriptor.Field(e => e.Descriptive).Name("descriptiveType");
descriptor.Field(e => e.Description);
descriptor.Field(e => e.Notes);
descriptor.Field(e => e.FlavorText);
}
}
public class EntityProductionGraphType : ObjectType<EntityProductionModel>
{
protected override void Configure(IObjectTypeDescriptor<EntityProductionModel> descriptor)
{
descriptor.Ignore(e => e.Parent);
}
}
public class EntitySupplyGraphType : ObjectType<EntitySupplyModel>
{
protected override void Configure(IObjectTypeDescriptor<EntitySupplyModel> descriptor)
{
descriptor.Ignore(e => e.Parent);
}
}
public class EntityTierGraphType : ObjectType<EntityTierModel>
{
protected override void Configure(IObjectTypeDescriptor<EntityTierModel> descriptor)
{
descriptor.Ignore(e => e.Parent);
}
}
public class EntityMovementGraphType : ObjectType<EntityMovementModel>
{
protected override void Configure(IObjectTypeDescriptor<EntityMovementModel> descriptor)
{
descriptor.Ignore(e => e.Parent);
}
}
public class EntityVitalityGraphType : ObjectType<EntityVitalityModel>
{
protected override void Configure(IObjectTypeDescriptor<EntityVitalityModel> descriptor)
{
descriptor.Ignore(e => e.Parent);
}
}
public class EntityRequirementGraphType : ObjectType<EntityRequirementModel>
{
protected override void Configure(IObjectTypeDescriptor<EntityRequirementModel> descriptor)
{
descriptor.Ignore(e => e.Parent);
}
}
public class EntityWeaponGraphType : ObjectType<EntityWeaponModel>
{
protected override void Configure(IObjectTypeDescriptor<EntityWeaponModel> descriptor)
{
descriptor.Ignore(e => e.Parent);
}
}
public class EntityHotkeyGraphType : ObjectType<EntityHotkeyModel>
{
protected override void Configure(IObjectTypeDescriptor<EntityHotkeyModel> descriptor)
{
descriptor.Ignore(e => e.Parent);
}
}
public class EntityFactionGraphType : ObjectType<EntityFactionModel>
{
protected override void Configure(IObjectTypeDescriptor<EntityFactionModel> descriptor)
{
descriptor.Ignore(e => e.Parent);
}
}
public class EntityHarvestGraphType : ObjectType<EntityHarvestModel>
{
protected override void Configure(IObjectTypeDescriptor<EntityHarvestModel> descriptor)
{
descriptor.Ignore(e => e.Parent);
}
}
public class EntityIdReferenceGraphType : ObjectType<EntityIdAbilityModel>
{
protected override void Configure(IObjectTypeDescriptor<EntityIdAbilityModel> descriptor)
{
descriptor.Ignore(e => e.Parent);
}
}
public class EntityMechanicGraphType : ObjectType<EntityMechanicModel>
{
protected override void Configure(IObjectTypeDescriptor<EntityMechanicModel> descriptor)
{
descriptor.Ignore(e => e.Parent);
}
}
public class EntityNamedDescGraphType : ObjectType<EntityPassiveModel>
{
protected override void Configure(IObjectTypeDescriptor<EntityPassiveModel> descriptor)
{
descriptor.Ignore(e => e.Parent);
}
}
public class EntityStrategyGraphType : ObjectType<EntityStrategyModel>
{
protected override void Configure(IObjectTypeDescriptor<EntityStrategyModel> descriptor)
{
descriptor.Ignore(e => e.Parent);
}
}
public class EntityVanguardReplacedGraphType : ObjectType<EntityVanguardReplacedModel>
{
protected override void Configure(IObjectTypeDescriptor<EntityVanguardReplacedModel> descriptor)
{
descriptor.Ignore(e => e.Parent);
}
}
public class EntityVanguardAddedGraphType : ObjectType<EntityVanguardAddedModel>
{
protected override void Configure(IObjectTypeDescriptor<EntityVanguardAddedModel> descriptor)
{
descriptor.Ignore(e => e.Parent);
}
}
+32
View File
@@ -0,0 +1,32 @@
using API.GraphQL.Types;
using Model.Entity.Data;
var builder = WebApplication.CreateBuilder(args);
builder.Services
.AddGraphQLServer()
.AddQueryType(d =>
{
d.Name("Query");
d.Field("entities")
.Type<NonNullType<ListType<NonNullType<EntityGraphType>>>>()
.Resolve(ctx => EntityData.Get().Values.ToList());
d.Field("entity")
.Type<EntityGraphType>()
.Argument("id", a => a.Type<NonNullType<StringType>>())
.Resolve(ctx =>
{
var id = ctx.ArgumentValue<string>("id");
EntityData.Get().TryGetValue(id, out var entity);
return entity;
});
})
.AddType<EntityGraphType>();
var app = builder.Build();
app.MapGraphQL();
app.Run();
+2 -2
View File
@@ -9,8 +9,8 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Services\Services.csproj" /> <ProjectReference Include="..\Services\Services.csproj"/>
<ProjectReference Include="..\Model\Model.csproj" /> <ProjectReference Include="..\Model\Model.csproj"/>
</ItemGroup> </ItemGroup>
</Project> </Project>
+2 -2
View File
@@ -30,8 +30,8 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Model\Model.csproj" /> <ProjectReference Include="..\Model\Model.csproj"/>
<ProjectReference Include="..\Services\Services.csproj" /> <ProjectReference Include="..\Services\Services.csproj"/>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Remove="Inputs\"/> <None Remove="Inputs\"/>
@@ -0,0 +1,87 @@
@inject IGlossaryService glossaryService
@if (TermId != null)
{
var term = glossaryService.GetTerm(TermId);
if (term != null)
{
<div class="glossaryTooltipWrapper">
<div class="glossaryTooltipContent">
<div class="glossaryTooltipHeader">
<span class="glossaryTooltipTerm">@term.Term</span>
<span class="glossaryTooltipCategory">@term.Category</span>
</div>
<div class="glossaryTooltipBody">
@term.ShortDefinition
</div>
</div>
@ChildContent
</div>
}
}
<style>
.glossaryTooltipWrapper {
position: relative;
display: inline-block;
}
.glossaryTooltipContent {
visibility: hidden;
position: absolute;
width: 400px;
max-width: 93vw;
bottom: 100%;
margin-bottom: 8px;
padding: 12px;
z-index: 2147483647;
background-color: var(--info-secondary);
border: 1px solid var(--info-secondary-border);
border-radius: 4px;
box-shadow: 0 3px 8px rgba(0, 0, 0, 0.5);
left: 50%;
transform: translateX(-50%);
}
.glossaryTooltipWrapper:hover .glossaryTooltipContent {
visibility: visible;
}
.glossaryTooltipHeader {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 8px;
}
.glossaryTooltipTerm {
font-weight: 800;
font-size: 1.1rem;
}
.glossaryTooltipCategory {
font-size: 0.8rem;
opacity: 0.7;
}
.glossaryTooltipBody {
font-size: 0.9rem;
line-height: 1.4;
}
@@media only screen and (max-width: 1025px) {
.glossaryTooltipContent {
margin: auto;
margin-bottom: 20px;
position: absolute;
}
}
</style>
@code {
[Parameter] public RenderFragment ChildContent { get; set; } = default!;
[Parameter] public string TermId { get; set; } = default!;
}
-1
View File
@@ -1,5 +1,4 @@
@using Model.MemoryTester @using Model.MemoryTester
@using Services.Immortal
@implements IDisposable @implements IDisposable
@inject IMemoryTesterService MemoryTesterService @inject IMemoryTesterService MemoryTesterService
@@ -0,0 +1,29 @@
@inject IGlossaryService glossaryService
@inject IGlossaryDialogService glossaryDialogService
@if (TermId == null)
{
<span>Missing term</span>
}
else
{
var term = glossaryService.GetTerm(TermId);
if (term != null)
{
<button class="glossaryLabel @term.Category.ToLowerInvariant()" @onclick="TermLabelClicked"
title="@term.ShortDefinition">
@term.Term
</button>
}
}
@code {
[Parameter] public string TermId { get; set; } = default!;
void TermLabelClicked()
{
glossaryDialogService.AddDialog(TermId);
}
}
@@ -0,0 +1,33 @@
.glossaryLabel {
font-weight: bolder;
box-shadow: 1px 1px 0 0 rgba(0, 0, 0, 0.2);
padding-right: 4px;
border: none;
background: none;
cursor: pointer;
font-family: inherit;
font-size: inherit;
text-decoration: underline;
text-decoration-style: dotted;
text-underline-offset: 2px;
}
.glossaryLabel:hover {
background-color: var(--primary-hover);
}
.resource {
color: gold;
}
.mechanic {
color: #8fc5ff;
}
.faction {
color: #da4e4e;
}
.role {
color: #87aa87;
}
@@ -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) + "...";
}
}
+4
View File
@@ -4,9 +4,13 @@
@using Microsoft.AspNetCore.Components.Web.Virtualization @using Microsoft.AspNetCore.Components.Web.Virtualization
@using Microsoft.JSInterop @using Microsoft.JSInterop
@using Model.Feedback @using Model.Feedback
@using Model.Glossary
@using Model.TechTree
@using Model.Website @using Model.Website
@using Model.Website.Enums @using Model.Website.Enums
@using Services @using Services
@using Services.Immortal
@using Services.Website
@using System.Net.Http @using System.Net.Http
@using System.Net.Http.Json @using System.Net.Http.Json
@using System.Text @using System.Text
+14
View File
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<Application xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Device.App">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Resources/Styles/Colors.xaml" />
<ResourceDictionary Source="Resources/Styles/Styles.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
+14
View File
@@ -0,0 +1,14 @@
namespace Device;
public partial class App : Application
{
public App()
{
InitializeComponent();
}
protected override Window CreateWindow(IActivationState? activationState)
{
return new Window(new AppShell());
}
}
+15
View File
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<Shell
x:Class="Device.AppShell"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Device"
Title="Device">
<ShellContent
Title="Home"
ContentTemplate="{DataTemplate local:MainPage}"
Route="MainPage" />
</Shell>
+9
View File
@@ -0,0 +1,9 @@
namespace Device;
public partial class AppShell : Shell
{
public AppShell()
{
InitializeComponent();
}
}
+83
View File
@@ -0,0 +1,83 @@
<Project Sdk="Microsoft.NET.Sdk.Razor">
<PropertyGroup>
<TargetFrameworks>net10.0-android</TargetFrameworks>
<TargetFrameworks Condition="!$([MSBuild]::IsOSPlatform('linux'))">$(TargetFrameworks);net10.0-ios;net10.0-maccatalyst</TargetFrameworks>
<TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('windows'))">$(TargetFrameworks);net10.0-windows10.0.19041.0</TargetFrameworks>
<ProjectRuntimeIdentifier Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">win-x64</ProjectRuntimeIdentifier>
<!-- Note for MacCatalyst:
The default runtime is maccatalyst-x64, except in Release config, in which case the default is maccatalyst-x64;maccatalyst-arm64.
When specifying both architectures, use the plural <RuntimeIdentifiers> instead of the singular <RuntimeIdentifier>.
The Mac App Store will NOT accept apps with ONLY maccatalyst-arm64 indicated;
either BOTH runtimes must be indicated or ONLY macatalyst-x64. -->
<!-- For example: <RuntimeIdentifiers>maccatalyst-x64;maccatalyst-arm64</RuntimeIdentifiers> -->
<OutputType>Exe</OutputType>
<RootNamespace>Device</RootNamespace>
<UseMaui>true</UseMaui>
<SingleProject>true</SingleProject>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<!-- Enable XAML source generation for faster build times and improved performance.
This generates C# code from XAML at compile time instead of runtime inflation.
To disable, remove this line.
For individual files, you can override by setting Inflator metadata:
<MauiXaml Update="MyPage.xaml" Inflator="Default" /> (reverts to defaults: Runtime for Debug, XamlC for Release)
<MauiXaml Update="MyPage.xaml" Inflator="Runtime" /> (force runtime inflation) -->
<MauiXamlInflator>SourceGen</MauiXamlInflator>
<!-- Display name -->
<ApplicationTitle>Device</ApplicationTitle>
<!-- App Identifier -->
<ApplicationId>com.companyname.device</ApplicationId>
<!-- Versions -->
<ApplicationDisplayVersion>1.0</ApplicationDisplayVersion>
<ApplicationVersion>1</ApplicationVersion>
<!-- To develop, package, and publish an app to the Microsoft Store, see: https://aka.ms/MauiTemplateUnpackaged -->
<WindowsPackageType>None</WindowsPackageType>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">15.0</SupportedOSPlatformVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'maccatalyst'">15.0</SupportedOSPlatformVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'">21.0</SupportedOSPlatformVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">10.0.17763.0</SupportedOSPlatformVersion>
<TargetPlatformMinVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">10.0.17763.0</TargetPlatformMinVersion>
</PropertyGroup>
<ItemGroup>
<!-- App Icon -->
<MauiIcon Include="Resources\AppIcon\appicon.svg" ForegroundFile="Resources\AppIcon\appiconfg.svg" Color="#512BD4"/>
<!-- Splash Screen -->
<MauiSplashScreen Include="Resources\Splash\splash.svg" Color="#512BD4" BaseSize="128,128"/>
<!-- Images -->
<MauiImage Include="Resources\Images\*"/>
<MauiImage Update="Resources\Images\dotnet_bot.png" Resize="True" BaseSize="300,185"/>
<!-- Custom Fonts -->
<MauiFont Include="Resources\Fonts\*"/>
<!-- Raw Assets (also remove the "Resources\Raw" prefix) -->
<MauiAsset Include="Resources\Raw\**" LogicalName="%(RecursiveDir)%(Filename)%(Extension)"/>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Maui.Controls" Version="$(MauiVersion)"/>
<PackageReference Include="Microsoft.AspNetCore.Components.WebView.Maui" Version="$(MauiVersion)"/>
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="10.0.0"/>
<PackageReference Include="MudBlazor" Version="9.2.0"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Components\Components.csproj"/>
<ProjectReference Include="..\Model\Model.csproj"/>
<ProjectReference Include="..\Pages\Pages.csproj"/>
<ProjectReference Include="..\Services\Services.csproj"/>
</ItemGroup>
</Project>
+14
View File
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Device"
x:Class="Device.MainPage">
<BlazorWebView x:Name="blazorWebView" HostPage="wwwroot/device.html">
<BlazorWebView.RootComponents>
<RootComponent Selector="#app" ComponentType="{x:Type local:MainRazor}" />
</BlazorWebView.RootComponents>
</BlazorWebView>
</ContentPage>
+9
View File
@@ -0,0 +1,9 @@
namespace Device;
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
}
}
+22
View File
@@ -0,0 +1,22 @@
<MudThemeProvider IsDarkMode="true"/>
<MudDialogProvider/>
<MudSnackbarProvider/>
<Router AppAssembly="@typeof(IGP.Pages.Home.HomePage).Assembly">
<Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)"/>
<FocusOnNavigate RouteData="@routeData" Selector="h1"/>
</Found>
<NotFound>
<PageTitle>Not found</PageTitle>
<LayoutView Layout="@typeof(MainLayout)">
<p role="alert">Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>
<EntityDialogPortal/>
<GlossaryDialogPortal/>
<ToastPortal/>
<SearchPortal/>
<ConfirmationDialogPortal/>
+77
View File
@@ -0,0 +1,77 @@
using System.Text.Json;
using System.Text.Json.Serialization;
using Blazored.LocalStorage;
using Microsoft.Extensions.Logging;
using MudBlazor.Services;
using Services;
using Services.Development;
using Services.Immortal;
using Services.Website;
namespace Device;
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
});
builder.Services.AddMauiBlazorWebView();
#if DEBUG
builder.Services.AddBlazorWebViewDeveloperTools();
builder.Logging.AddDebug();
#endif
builder.Services.AddLocalization();
builder.Services.AddBlazoredLocalStorage(config =>
{
config.JsonSerializerOptions.DictionaryKeyPolicy = JsonNamingPolicy.CamelCase;
config.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
config.JsonSerializerOptions.IgnoreReadOnlyProperties = true;
config.JsonSerializerOptions.PropertyNameCaseInsensitive = true;
config.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
config.JsonSerializerOptions.ReadCommentHandling = JsonCommentHandling.Skip;
config.JsonSerializerOptions.WriteIndented = false;
});
builder.Services.AddScoped<INavigationService, NavigationService>();
builder.Services.AddScoped<IKeyService, KeyService>();
builder.Services.AddScoped<IImmortalSelectionService, ImmortalSelectionService>();
builder.Services.AddScoped<IBuildComparisonService, DeprecatedBuildComparisionService>();
builder.Services.AddScoped<IBuildOrderService, BuildOrderService>();
builder.Services.AddScoped<IEconomyService, EconomyService>();
builder.Services.AddScoped<ITimingService, TimingService>();
builder.Services.AddScoped<IMemoryTesterService, MemoryTesterService>();
builder.Services.AddScoped<IEntityFilterService, EntityFilterService>();
builder.Services.AddScoped<IEntityDisplayService, EntityDisplayService>();
builder.Services.AddScoped<IEntityDialogService, EntityDialogService>();
builder.Services.AddScoped<IGlossaryService, GlossaryService>();
builder.Services.AddScoped<IGlossaryDialogService, GlossaryDialogService>();
builder.Services.AddScoped<IToastService, ToastService>();
builder.Services.AddScoped<INoteService, NoteService>();
builder.Services.AddScoped<ISearchService, SearchService>();
builder.Services.AddScoped<IStorageService, StorageService>();
builder.Services.AddScoped<IPermissionService, PermissionService>();
builder.Services.AddScoped<IEconomyComparisonService, EconomyComparisionService>();
builder.Services.AddScoped<IDataCollectionService, DataCollectionService>();
builder.Services.AddScoped<IMyDialogService, MyDialogService>();
builder.Services.AddScoped<TechTreeService>();
builder.Services.AddMudServices();
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri("https://0.0.0.0") });
return builder.Build();
}
}
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application android:allowBackup="true" android:icon="@mipmap/appicon" android:roundIcon="@mipmap/appicon_round"
android:supportsRtl="true"></application>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>
+11
View File
@@ -0,0 +1,11 @@
using Android.App;
using Android.Content.PM;
namespace Device;
[Activity(Theme = "@style/Maui.SplashTheme", MainLauncher = true, LaunchMode = LaunchMode.SingleTop,
ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode |
ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density)]
public class MainActivity : MauiAppCompatActivity
{
}
@@ -0,0 +1,18 @@
using Android.App;
using Android.Runtime;
namespace Device;
[Application]
public class MainApplication : MauiApplication
{
public MainApplication(IntPtr handle, JniHandleOwnership ownership)
: base(handle, ownership)
{
}
protected override MauiApp CreateMauiApp()
{
return MauiProgram.CreateMauiApp();
}
}
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#512BD4</color>
<color name="colorPrimaryDark">#2B0B98</color>
<color name="colorAccent">#2B0B98</color>
</resources>
@@ -0,0 +1,9 @@
using Foundation;
namespace Device;
[Register("AppDelegate")]
public class AppDelegate : MauiUIApplicationDelegate
{
protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
}
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<!-- See https://aka.ms/maui-publish-app-store#add-entitlements for more information about adding entitlements.-->
<dict>
<!-- App Sandbox must be enabled to distribute a MacCatalyst app through the Mac App Store. -->
<key>com.apple.security.app-sandbox</key>
<true/>
<!-- When App Sandbox is enabled, this value is required to open outgoing network connections. -->
<key>com.apple.security.network.client</key>
<true/>
</dict>
</plist>
+40
View File
@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<!-- The Mac App Store requires you specify if the app uses encryption. -->
<!-- Please consult https://developer.apple.com/documentation/bundleresources/information_property_list/itsappusesnonexemptencryption -->
<!-- <key>ITSAppUsesNonExemptEncryption</key> -->
<!-- Please indicate <true/> or <false/> here. -->
<!-- Specify the category for your app here. -->
<!-- Please consult https://developer.apple.com/documentation/bundleresources/information_property_list/lsapplicationcategorytype -->
<!-- <key>LSApplicationCategoryType</key> -->
<!-- <string>public.app-category.YOUR-CATEGORY-HERE</string> -->
<key>UIDeviceFamily</key>
<array>
<integer>2</integer>
</array>
<key>LSApplicationCategoryType</key>
<string>public.app-category.lifestyle</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>arm64</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>XSAppIconAssets</key>
<string>Assets.xcassets/appicon.appiconset</string>
</dict>
</plist>
+15
View File
@@ -0,0 +1,15 @@
using ObjCRuntime;
using UIKit;
namespace Device;
public class Program
{
// This is the main entry point of the application.
static void Main(string[] args)
{
// if you want to use a different Application Delegate class from "AppDelegate"
// you can specify it here.
UIApplication.Main(args, null, typeof(AppDelegate));
}
}
+8
View File
@@ -0,0 +1,8 @@
<maui:MauiWinUIApplication
x:Class="Device.WinUI.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:maui="using:Microsoft.Maui"
xmlns:local="using:Device.WinUI">
</maui:MauiWinUIApplication>
+23
View File
@@ -0,0 +1,23 @@
using Microsoft.UI.Xaml;
// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.
namespace Device.WinUI;
/// <summary>
/// Provides application-specific behavior to supplement the default Application class.
/// </summary>
public partial class App : MauiWinUIApplication
{
/// <summary>
/// Initializes the singleton application object. This is the first line of authored code
/// executed, and as such is the logical equivalent of main() or WinMain().
/// </summary>
public App()
{
this.InitializeComponent();
}
protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
}
@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<Package
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
IgnorableNamespaces="uap rescap">
<Identity Name="maui-package-name-placeholder" Publisher="CN=User Name" Version="0.0.0.0"/>
<mp:PhoneIdentity PhoneProductId="034A3819-8402-48FB-850F-9B134D4D5787"
PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
<Properties>
<DisplayName>$placeholder$</DisplayName>
<PublisherDisplayName>User Name</PublisherDisplayName>
<Logo>$placeholder$.png</Logo>
</Properties>
<Dependencies>
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0"/>
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0"/>
</Dependencies>
<Resources>
<Resource Language="x-generate"/>
</Resources>
<Applications>
<Application Id="App" Executable="$targetnametoken$.exe" EntryPoint="$targetentrypoint$">
<uap:VisualElements
DisplayName="$placeholder$"
Description="$placeholder$"
Square150x150Logo="$placeholder$.png"
Square44x44Logo="$placeholder$.png"
BackgroundColor="transparent">
<uap:DefaultTile Square71x71Logo="$placeholder$.png" Wide310x150Logo="$placeholder$.png"
Square310x310Logo="$placeholder$.png"/>
<uap:SplashScreen Image="$placeholder$.png"/>
</uap:VisualElements>
</Application>
</Applications>
<Capabilities>
<rescap:Capability Name="runFullTrust"/>
</Capabilities>
</Package>
+18
View File
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<assemblyIdentity version="1.0.0.0" name="Device.WinUI.app"/>
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<!-- The combination of below two tags have the following effect:
1) Per-Monitor for >= Windows 10 Anniversary Update
2) System < Windows 10 Anniversary Update
-->
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/PM</dpiAware>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2, PerMonitor
</dpiAwareness>
<longPathAware xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">true</longPathAware>
</windowsSettings>
</application>
</assembly>
+9
View File
@@ -0,0 +1,9 @@
using Foundation;
namespace Device;
[Register("AppDelegate")]
public class AppDelegate : MauiUIApplicationDelegate
{
protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
}
+32
View File
@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UIDeviceFamily</key>
<array>
<integer>1</integer>
<integer>2</integer>
</array>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>arm64</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>XSAppIconAssets</key>
<string>Assets.xcassets/appicon.appiconset</string>
</dict>
</plist>
+15
View File
@@ -0,0 +1,15 @@
using ObjCRuntime;
using UIKit;
namespace Device;
public class Program
{
// This is the main entry point of the application.
static void Main(string[] args)
{
// if you want to use a different Application Delegate class from "AppDelegate"
// you can specify it here.
UIApplication.Main(args, null, typeof(AppDelegate));
}
}
@@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
This is the minimum required version of the Apple Privacy Manifest for .NET MAUI apps.
The contents below are needed because of APIs that are used in the .NET framework and .NET MAUI SDK.
You are responsible for adding extra entries as needed for your application.
More information: https://aka.ms/maui-privacy-manifest
-->
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSPrivacyAccessedAPITypes</key>
<array>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryFileTimestamp</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>C617.1</string>
</array>
</dict>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategorySystemBootTime</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>35F9.1</string>
</array>
</dict>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryDiskSpace</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>E174.1</string>
</array>
</dict>
<!--
The entry below is only needed when you're using the Preferences API in your app.
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>CA92.1</string>
</array>
</dict> -->
</array>
</dict>
</plist>
+8
View File
@@ -0,0 +1,8 @@
{
"profiles": {
"Windows Machine": {
"commandName": "Project",
"nativeDebugging": false
}
}
}
+4
View File
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="456" height="456" viewBox="0 0 456 456" version="1.1" xmlns="http://www.w3.org/2000/svg">
<rect x="0" y="0" width="456" height="456" fill="#512BD4"/>
</svg>

After

Width:  |  Height:  |  Size: 227 B

+14
View File
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="456" height="456" viewBox="0 0 456 456" version="1.1" xmlns="http://www.w3.org/2000/svg"
xml:space="preserve"
style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<path d="m 105.50037,281.60863 c -2.70293,0 -5.00091,-0.90042 -6.893127,-2.70209 -1.892214,-1.84778 -2.837901,-4.04181 -2.837901,-6.58209 0,-2.58722 0.945687,-4.80389 2.837901,-6.65167 1.892217,-1.84778 4.190197,-2.77167 6.893127,-2.77167 2.74819,0 5.06798,0.92389 6.96019,2.77167 1.93749,1.84778 2.90581,4.06445 2.90581,6.65167 0,2.54028 -0.96832,4.73431 -2.90581,6.58209 -1.89221,1.80167 -4.212,2.70209 -6.96019,2.70209 z"
style="fill:#ffffff;fill-rule:nonzero;stroke-width:0.838376"/>
<path d="M 213.56111,280.08446 H 195.99044 L 149.69953,207.0544 c -1.17121,-1.84778 -2.14037,-3.76515 -2.90581,-5.75126 h -0.40578 c 0.36051,2.12528 0.54076,6.67515 0.54076,13.6496 v 65.13172 h -15.54349 v -99.36009 h 18.71925 l 44.7374,71.29798 c 1.89222,2.95695 3.1087,4.98917 3.64945,6.09751 h 0.26996 c -0.45021,-2.6325 -0.67573,-7.09015 -0.67573,-13.37293 v -64.02256 h 15.47557 z"
style="fill:#ffffff;fill-rule:nonzero;stroke-width:0.838376"/>
<path d="m 289.25134,280.08446 h -54.40052 v -99.36009 h 52.23835 v 13.99669 h -36.15411 v 28.13085 h 33.31621 v 13.9271 h -33.31621 v 29.37835 h 38.31628 z"
style="fill:#ffffff;fill-rule:nonzero;stroke-width:0.838376"/>
<path d="M 366.56466,194.72106 H 338.7222 v 85.3634 h -16.08423 v -85.3634 h -27.77455 v -13.99669 h 71.70124 z"
style="fill:#ffffff;fill-rule:nonzero;stroke-width:0.838376"/>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

+15
View File
@@ -0,0 +1,15 @@
Any raw assets you want to be deployed with your application can be placed in
this directory (and child directories). Deployment of the asset to your application
is automatically handled by the following `MauiAsset` Build Action within your `.csproj`.
<MauiAsset Include="Resources\Raw\**" LogicalName="%(RecursiveDir)%(Filename)%(Extension)" />
These files will be deployed with your package and will be accessible using Essentials:
async Task LoadMauiAsset()
{
using var stream = await FileSystem.OpenAppPackageFileAsync("AboutAssets.txt");
using var reader = new StreamReader(stream);
var contents = reader.ReadToEnd();
}
+14
View File
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="456" height="456" viewBox="0 0 456 456" version="1.1" xmlns="http://www.w3.org/2000/svg"
xml:space="preserve"
style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<path d="m 105.50037,281.60863 c -2.70293,0 -5.00091,-0.90042 -6.893127,-2.70209 -1.892214,-1.84778 -2.837901,-4.04181 -2.837901,-6.58209 0,-2.58722 0.945687,-4.80389 2.837901,-6.65167 1.892217,-1.84778 4.190197,-2.77167 6.893127,-2.77167 2.74819,0 5.06798,0.92389 6.96019,2.77167 1.93749,1.84778 2.90581,4.06445 2.90581,6.65167 0,2.54028 -0.96832,4.73431 -2.90581,6.58209 -1.89221,1.80167 -4.212,2.70209 -6.96019,2.70209 z"
style="fill:#ffffff;fill-rule:nonzero;stroke-width:0.838376"/>
<path d="M 213.56111,280.08446 H 195.99044 L 149.69953,207.0544 c -1.17121,-1.84778 -2.14037,-3.76515 -2.90581,-5.75126 h -0.40578 c 0.36051,2.12528 0.54076,6.67515 0.54076,13.6496 v 65.13172 h -15.54349 v -99.36009 h 18.71925 l 44.7374,71.29798 c 1.89222,2.95695 3.1087,4.98917 3.64945,6.09751 h 0.26996 c -0.45021,-2.6325 -0.67573,-7.09015 -0.67573,-13.37293 v -64.02256 h 15.47557 z"
style="fill:#ffffff;fill-rule:nonzero;stroke-width:0.838376"/>
<path d="m 289.25134,280.08446 h -54.40052 v -99.36009 h 52.23835 v 13.99669 h -36.15411 v 28.13085 h 33.31621 v 13.9271 h -33.31621 v 29.37835 h 38.31628 z"
style="fill:#ffffff;fill-rule:nonzero;stroke-width:0.838376"/>
<path d="M 366.56466,194.72106 H 338.7222 v 85.3634 h -16.08423 v -85.3634 h -27.77455 v -13.99669 h 71.70124 z"
style="fill:#ffffff;fill-rule:nonzero;stroke-width:0.838376"/>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

+45
View File
@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<ResourceDictionary
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">
<!-- Note: For Android please see also Platforms\Android\Resources\values\colors.xml -->
<Color x:Key="Primary">#512BD4</Color>
<Color x:Key="PrimaryDark">#ac99ea</Color>
<Color x:Key="PrimaryDarkText">#242424</Color>
<Color x:Key="Secondary">#DFD8F7</Color>
<Color x:Key="SecondaryDarkText">#9880e5</Color>
<Color x:Key="Tertiary">#2B0B98</Color>
<Color x:Key="White">White</Color>
<Color x:Key="Black">Black</Color>
<Color x:Key="Magenta">#D600AA</Color>
<Color x:Key="MidnightBlue">#190649</Color>
<Color x:Key="OffBlack">#1f1f1f</Color>
<Color x:Key="Gray100">#E1E1E1</Color>
<Color x:Key="Gray200">#C8C8C8</Color>
<Color x:Key="Gray300">#ACACAC</Color>
<Color x:Key="Gray400">#919191</Color>
<Color x:Key="Gray500">#6E6E6E</Color>
<Color x:Key="Gray600">#404040</Color>
<Color x:Key="Gray900">#212121</Color>
<Color x:Key="Gray950">#141414</Color>
<SolidColorBrush x:Key="PrimaryBrush" Color="{StaticResource Primary}" />
<SolidColorBrush x:Key="SecondaryBrush" Color="{StaticResource Secondary}" />
<SolidColorBrush x:Key="TertiaryBrush" Color="{StaticResource Tertiary}" />
<SolidColorBrush x:Key="WhiteBrush" Color="{StaticResource White}" />
<SolidColorBrush x:Key="BlackBrush" Color="{StaticResource Black}" />
<SolidColorBrush x:Key="Gray100Brush" Color="{StaticResource Gray100}" />
<SolidColorBrush x:Key="Gray200Brush" Color="{StaticResource Gray200}" />
<SolidColorBrush x:Key="Gray300Brush" Color="{StaticResource Gray300}" />
<SolidColorBrush x:Key="Gray400Brush" Color="{StaticResource Gray400}" />
<SolidColorBrush x:Key="Gray500Brush" Color="{StaticResource Gray500}" />
<SolidColorBrush x:Key="Gray600Brush" Color="{StaticResource Gray600}" />
<SolidColorBrush x:Key="Gray900Brush" Color="{StaticResource Gray900}" />
<SolidColorBrush x:Key="Gray950Brush" Color="{StaticResource Gray950}" />
</ResourceDictionary>
+503
View File
@@ -0,0 +1,503 @@
<?xml version="1.0" encoding="UTF-8"?>
<ResourceDictionary
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">
<Style TargetType="ActivityIndicator">
<Setter Property="Color" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
</Style>
<Style TargetType="IndicatorView">
<Setter Property="IndicatorColor"
Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray500}}" />
<Setter Property="SelectedIndicatorColor"
Value="{AppThemeBinding Light={StaticResource Gray950}, Dark={StaticResource Gray100}}" />
</Style>
<Style TargetType="Border">
<Setter Property="Stroke"
Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray500}}" />
<Setter Property="StrokeShape" Value="Rectangle" />
<Setter Property="StrokeThickness" Value="1" />
</Style>
<Style TargetType="BoxView">
<Setter Property="BackgroundColor"
Value="{AppThemeBinding Light={StaticResource Gray950}, Dark={StaticResource Gray200}}" />
</Style>
<Style TargetType="Button">
<Setter Property="TextColor"
Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource PrimaryDarkText}}" />
<Setter Property="BackgroundColor"
Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource PrimaryDark}}" />
<Setter Property="FontFamily" Value="OpenSansRegular" />
<Setter Property="FontSize" Value="14" />
<Setter Property="BorderWidth" Value="0" />
<Setter Property="CornerRadius" Value="8" />
<Setter Property="Padding" Value="14,10" />
<Setter Property="MinimumHeightRequest" Value="44" />
<Setter Property="MinimumWidthRequest" Value="44" />
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor"
Value="{AppThemeBinding Light={StaticResource Gray950}, Dark={StaticResource Gray200}}" />
<Setter Property="BackgroundColor"
Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="PointerOver" />
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="CheckBox">
<Setter Property="Color" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
<Setter Property="MinimumHeightRequest" Value="44" />
<Setter Property="MinimumWidthRequest" Value="44" />
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="Color"
Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="DatePicker">
<Setter Property="TextColor"
Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource White}}" />
<Setter Property="BackgroundColor" Value="Transparent" />
<Setter Property="FontFamily" Value="OpenSansRegular" />
<Setter Property="FontSize" Value="14" />
<Setter Property="MinimumHeightRequest" Value="44" />
<Setter Property="MinimumWidthRequest" Value="44" />
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor"
Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray500}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="Editor">
<Setter Property="TextColor"
Value="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource White}}" />
<Setter Property="BackgroundColor" Value="Transparent" />
<Setter Property="FontFamily" Value="OpenSansRegular" />
<Setter Property="FontSize" Value="14" />
<Setter Property="PlaceholderColor"
Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray500}}" />
<Setter Property="MinimumHeightRequest" Value="44" />
<Setter Property="MinimumWidthRequest" Value="44" />
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor"
Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="Entry">
<Setter Property="TextColor"
Value="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource White}}" />
<Setter Property="BackgroundColor" Value="Transparent" />
<Setter Property="FontFamily" Value="OpenSansRegular" />
<Setter Property="FontSize" Value="14" />
<Setter Property="PlaceholderColor"
Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray500}}" />
<Setter Property="MinimumHeightRequest" Value="44" />
<Setter Property="MinimumWidthRequest" Value="44" />
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor"
Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="ImageButton">
<Setter Property="Opacity" Value="1" />
<Setter Property="BorderColor" Value="Transparent" />
<Setter Property="BorderWidth" Value="0" />
<Setter Property="CornerRadius" Value="0" />
<Setter Property="MinimumHeightRequest" Value="44" />
<Setter Property="MinimumWidthRequest" Value="44" />
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="Opacity" Value="0.5" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="PointerOver" />
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="Label">
<Setter Property="TextColor"
Value="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource White}}" />
<Setter Property="BackgroundColor" Value="Transparent" />
<Setter Property="FontFamily" Value="OpenSansRegular" />
<Setter Property="FontSize" Value="14" />
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor"
Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="Label" x:Key="Headline">
<Setter Property="TextColor"
Value="{AppThemeBinding Light={StaticResource MidnightBlue}, Dark={StaticResource White}}" />
<Setter Property="FontSize" Value="32" />
<Setter Property="HorizontalOptions" Value="Center" />
<Setter Property="HorizontalTextAlignment" Value="Center" />
</Style>
<Style TargetType="Label" x:Key="SubHeadline">
<Setter Property="TextColor"
Value="{AppThemeBinding Light={StaticResource MidnightBlue}, Dark={StaticResource White}}" />
<Setter Property="FontSize" Value="24" />
<Setter Property="HorizontalOptions" Value="Center" />
<Setter Property="HorizontalTextAlignment" Value="Center" />
</Style>
<Style TargetType="Picker">
<Setter Property="TextColor"
Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource White}}" />
<Setter Property="TitleColor"
Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource Gray200}}" />
<Setter Property="BackgroundColor" Value="Transparent" />
<Setter Property="FontFamily" Value="OpenSansRegular" />
<Setter Property="FontSize" Value="14" />
<Setter Property="MinimumHeightRequest" Value="44" />
<Setter Property="MinimumWidthRequest" Value="44" />
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor"
Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
<Setter Property="TitleColor"
Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="ProgressBar">
<Setter Property="ProgressColor"
Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="ProgressColor"
Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="RadioButton">
<Setter Property="BackgroundColor" Value="Transparent" />
<Setter Property="TextColor"
Value="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource White}}" />
<Setter Property="FontFamily" Value="OpenSansRegular" />
<Setter Property="FontSize" Value="14" />
<Setter Property="MinimumHeightRequest" Value="44" />
<Setter Property="MinimumWidthRequest" Value="44" />
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor"
Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="RefreshView">
<Setter Property="RefreshColor"
Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource Gray200}}" />
</Style>
<Style TargetType="SearchBar">
<Setter Property="TextColor"
Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource White}}" />
<Setter Property="PlaceholderColor" Value="{StaticResource Gray500}" />
<Setter Property="CancelButtonColor" Value="{StaticResource Gray500}" />
<Setter Property="BackgroundColor" Value="Transparent" />
<Setter Property="FontFamily" Value="OpenSansRegular" />
<Setter Property="FontSize" Value="14" />
<Setter Property="MinimumHeightRequest" Value="44" />
<Setter Property="MinimumWidthRequest" Value="44" />
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor"
Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
<Setter Property="PlaceholderColor"
Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="SearchHandler">
<Setter Property="TextColor"
Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource White}}" />
<Setter Property="PlaceholderColor" Value="{StaticResource Gray500}" />
<Setter Property="BackgroundColor" Value="Transparent" />
<Setter Property="FontFamily" Value="OpenSansRegular" />
<Setter Property="FontSize" Value="14" />
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor"
Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
<Setter Property="PlaceholderColor"
Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="Shadow">
<Setter Property="Radius" Value="15" />
<Setter Property="Opacity" Value="0.5" />
<Setter Property="Brush" Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource White}}" />
<Setter Property="Offset" Value="10,10" />
</Style>
<Style TargetType="Slider">
<Setter Property="MinimumTrackColor"
Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
<Setter Property="MaximumTrackColor"
Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray600}}" />
<Setter Property="ThumbColor"
Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="MinimumTrackColor"
Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
<Setter Property="MaximumTrackColor"
Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
<Setter Property="ThumbColor"
Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="SwipeItem">
<Setter Property="BackgroundColor"
Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource Black}}" />
</Style>
<Style TargetType="Switch">
<Setter Property="OnColor"
Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
<Setter Property="ThumbColor" Value="{StaticResource White}" />
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="OnColor"
Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
<Setter Property="ThumbColor"
Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="On">
<VisualState.Setters>
<Setter Property="OnColor"
Value="{AppThemeBinding Light={StaticResource Secondary}, Dark={StaticResource Gray200}}" />
<Setter Property="ThumbColor"
Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Off">
<VisualState.Setters>
<Setter Property="ThumbColor"
Value="{AppThemeBinding Light={StaticResource Gray400}, Dark={StaticResource Gray500}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="TimePicker">
<Setter Property="TextColor"
Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource White}}" />
<Setter Property="BackgroundColor" Value="Transparent" />
<Setter Property="FontFamily" Value="OpenSansRegular" />
<Setter Property="FontSize" Value="14" />
<Setter Property="MinimumHeightRequest" Value="44" />
<Setter Property="MinimumWidthRequest" Value="44" />
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor"
Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<!--
<Style TargetType="TitleBar">
<Setter Property="MinimumHeightRequest" Value="32"/>
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="TitleActiveStates">
<VisualState x:Name="TitleBarTitleActive">
<VisualState.Setters>
<Setter Property="BackgroundColor" Value="Transparent" />
<Setter Property="ForegroundColor" Value="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource White}}" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="TitleBarTitleInactive">
<VisualState.Setters>
<Setter Property="BackgroundColor" Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource Black}}" />
<Setter Property="ForegroundColor" Value="{AppThemeBinding Light={StaticResource Gray400}, Dark={StaticResource Gray500}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
-->
<Style TargetType="Page" ApplyToDerivedTypes="True">
<Setter Property="Padding" Value="0" />
<Setter Property="BackgroundColor"
Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource OffBlack}}" />
</Style>
<Style TargetType="Shell" ApplyToDerivedTypes="True">
<Setter Property="Shell.BackgroundColor"
Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource OffBlack}}" />
<Setter Property="Shell.ForegroundColor"
Value="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource SecondaryDarkText}}" />
<Setter Property="Shell.TitleColor"
Value="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource SecondaryDarkText}}" />
<Setter Property="Shell.DisabledColor"
Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray950}}" />
<Setter Property="Shell.UnselectedColor"
Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray200}}" />
<Setter Property="Shell.NavBarHasShadow" Value="False" />
<Setter Property="Shell.TabBarBackgroundColor"
Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource Black}}" />
<Setter Property="Shell.TabBarForegroundColor"
Value="{AppThemeBinding Light={StaticResource Magenta}, Dark={StaticResource White}}" />
<Setter Property="Shell.TabBarTitleColor"
Value="{AppThemeBinding Light={StaticResource Magenta}, Dark={StaticResource White}}" />
<Setter Property="Shell.TabBarUnselectedColor"
Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource Gray200}}" />
</Style>
<Style TargetType="NavigationPage">
<Setter Property="BarBackgroundColor"
Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource OffBlack}}" />
<Setter Property="BarTextColor"
Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource White}}" />
<Setter Property="IconColor"
Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource White}}" />
</Style>
<Style TargetType="TabbedPage">
<Setter Property="BarBackgroundColor"
Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource Gray950}}" />
<Setter Property="BarTextColor"
Value="{AppThemeBinding Light={StaticResource Magenta}, Dark={StaticResource White}}" />
<Setter Property="UnselectedTabColor"
Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray950}}" />
<Setter Property="SelectedTabColor"
Value="{AppThemeBinding Light={StaticResource Gray950}, Dark={StaticResource Gray200}}" />
</Style>
</ResourceDictionary>
+58
View File
@@ -0,0 +1,58 @@
@using System.Net.Http
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.AspNetCore.Components.Web.Virtualization
@using Microsoft.JSInterop
@using Device
@using IGP.Pages
@using IGP.Pages.Home
@using IGP.Pages.Home.Parts
@using IGP.Dialog
@using IGP.Pages.BuildCalculator
@using IGP.Pages.BuildCalculator.Parts
@using IGP.Pages.Database.Entity
@using IGP.Pages.Database.Entity.Parts
@using IGP.Pages.Database.Parts
@using IGP.Pages.EconomyComparison
@using IGP.Pages.EconomyComparison.Parts
@using IGP.Pages.MemoryTester.Parts
@using IGP.Pages.Notes
@using IGP.Pages.Notes.Parts
@using IGP.Portals
@using IGP.Utils
@using Components.Display
@using Components.Feedback
@using Components.TechTree
@using Components.Form
@using Components.Info
@using Components.Inputs
@using Components.Layout
@using Components.Navigation
@using Components.Shared
@using Markdig
@using Microsoft.Extensions.Localization
@using Model.Chart
@using Model.Economy
@using Model.Entity
@using Model.Entity.Data
@using Model.Entity.Parts
@using Model.Feedback
@using Model.Glossary
@using Model.Hotkeys
@using Model.MemoryTester
@using Model.Notes
@using Model.RoadMap
@using Model.RoadMap.Enums
@using Model.TechTree
@using Model.Types
@using Model.Website
@using Services
@using Services.Immortal
@using System.Globalization
@using System.Reflection
@using System.Timers
@using MudBlazor
@using MudBlazor.Services
+38
View File
@@ -0,0 +1,38 @@
<!DOCTYPE html>
<html style="background-color: #2C2E33; color: white;">
<head>
<meta charset="utf-8"/>
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport"/>
<title>IGP Fan Reference</title>
<base href="/"/>
<link crossorigin="anonymous" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css"
integrity="sha384-zCbKRCUGaJDkqS1kPbPd7TveP5iyJE0EjAuZQTgFLD2ylzuqKfdKlfG/eSrtxUkn" rel="stylesheet">
<link href="https://use.fontawesome.com/releases/v5.15.4/css/all.css" rel="stylesheet">
<link href="css/app.css" rel="stylesheet"/>
<link href="Device.styles.css" rel="stylesheet"/>
<link href="_content/MudBlazor/MudBlazor.min.css" rel="stylesheet"/>
</head>
<body style="background-color: #161618; color: white;">
<div id="app">
<div style="width: 100vw; height: 100vh; background-color: black;"></div>
</div>
<div id="blazor-error-ui">
An unhandled error has occurred.
<a class="reload" href="">Reload</a>
<a class="dismiss">🗙</a>
</div>
<script src="_framework/blazor.webview.js"></script>
<script crossorigin="anonymous"
integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj"
src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.slim.min.js"></script>
<script crossorigin="anonymous"
integrity="sha384-VHvPCCyXqtD5DqJeNxl2dtTyhF78xXNXdkwX1CZeRusQfRKp+tA7hAShOK/B/fQ2"
src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.min.js"></script>
<script src="_content/MudBlazor/MudBlazor.min.js"></script>
</body>
</html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

+42
View File
@@ -15,6 +15,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Components", "Components\Co
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests\Tests.csproj", "{1460CB4C-7E8E-41F0-8DF2-174C69A3E366}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests\Tests.csproj", "{1460CB4C-7E8E-41F0-8DF2-174C69A3E366}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "API", "API\API.csproj", "{61576AEF-60F5-41E2-B834-A667205C83FF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Device", "Device\Device.csproj", "{FF07C814-9757-48B5-8450-CFA4958EC42D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Pages", "Pages\Pages.csproj", "{3BFCB341-D41D-4F23-B867-EB4A8F58BB14}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@@ -97,6 +103,42 @@ Global
{1460CB4C-7E8E-41F0-8DF2-174C69A3E366}.Release|x64.Build.0 = Release|Any CPU {1460CB4C-7E8E-41F0-8DF2-174C69A3E366}.Release|x64.Build.0 = Release|Any CPU
{1460CB4C-7E8E-41F0-8DF2-174C69A3E366}.Release|x86.ActiveCfg = Release|Any CPU {1460CB4C-7E8E-41F0-8DF2-174C69A3E366}.Release|x86.ActiveCfg = Release|Any CPU
{1460CB4C-7E8E-41F0-8DF2-174C69A3E366}.Release|x86.Build.0 = Release|Any CPU {1460CB4C-7E8E-41F0-8DF2-174C69A3E366}.Release|x86.Build.0 = Release|Any CPU
{61576AEF-60F5-41E2-B834-A667205C83FF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{61576AEF-60F5-41E2-B834-A667205C83FF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{61576AEF-60F5-41E2-B834-A667205C83FF}.Debug|x64.ActiveCfg = Debug|Any CPU
{61576AEF-60F5-41E2-B834-A667205C83FF}.Debug|x64.Build.0 = Debug|Any CPU
{61576AEF-60F5-41E2-B834-A667205C83FF}.Debug|x86.ActiveCfg = Debug|Any CPU
{61576AEF-60F5-41E2-B834-A667205C83FF}.Debug|x86.Build.0 = Debug|Any CPU
{61576AEF-60F5-41E2-B834-A667205C83FF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{61576AEF-60F5-41E2-B834-A667205C83FF}.Release|Any CPU.Build.0 = Release|Any CPU
{61576AEF-60F5-41E2-B834-A667205C83FF}.Release|x64.ActiveCfg = Release|Any CPU
{61576AEF-60F5-41E2-B834-A667205C83FF}.Release|x64.Build.0 = Release|Any CPU
{61576AEF-60F5-41E2-B834-A667205C83FF}.Release|x86.ActiveCfg = Release|Any CPU
{61576AEF-60F5-41E2-B834-A667205C83FF}.Release|x86.Build.0 = Release|Any CPU
{FF07C814-9757-48B5-8450-CFA4958EC42D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FF07C814-9757-48B5-8450-CFA4958EC42D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FF07C814-9757-48B5-8450-CFA4958EC42D}.Debug|x64.ActiveCfg = Debug|Any CPU
{FF07C814-9757-48B5-8450-CFA4958EC42D}.Debug|x64.Build.0 = Debug|Any CPU
{FF07C814-9757-48B5-8450-CFA4958EC42D}.Debug|x86.ActiveCfg = Debug|Any CPU
{FF07C814-9757-48B5-8450-CFA4958EC42D}.Debug|x86.Build.0 = Debug|Any CPU
{FF07C814-9757-48B5-8450-CFA4958EC42D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FF07C814-9757-48B5-8450-CFA4958EC42D}.Release|Any CPU.Build.0 = Release|Any CPU
{FF07C814-9757-48B5-8450-CFA4958EC42D}.Release|x64.ActiveCfg = Release|Any CPU
{FF07C814-9757-48B5-8450-CFA4958EC42D}.Release|x64.Build.0 = Release|Any CPU
{FF07C814-9757-48B5-8450-CFA4958EC42D}.Release|x86.ActiveCfg = Release|Any CPU
{FF07C814-9757-48B5-8450-CFA4958EC42D}.Release|x86.Build.0 = Release|Any CPU
{3BFCB341-D41D-4F23-B867-EB4A8F58BB14}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3BFCB341-D41D-4F23-B867-EB4A8F58BB14}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3BFCB341-D41D-4F23-B867-EB4A8F58BB14}.Debug|x64.ActiveCfg = Debug|Any CPU
{3BFCB341-D41D-4F23-B867-EB4A8F58BB14}.Debug|x64.Build.0 = Debug|Any CPU
{3BFCB341-D41D-4F23-B867-EB4A8F58BB14}.Debug|x86.ActiveCfg = Debug|Any CPU
{3BFCB341-D41D-4F23-B867-EB4A8F58BB14}.Debug|x86.Build.0 = Debug|Any CPU
{3BFCB341-D41D-4F23-B867-EB4A8F58BB14}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3BFCB341-D41D-4F23-B867-EB4A8F58BB14}.Release|Any CPU.Build.0 = Release|Any CPU
{3BFCB341-D41D-4F23-B867-EB4A8F58BB14}.Release|x64.ActiveCfg = Release|Any CPU
{3BFCB341-D41D-4F23-B867-EB4A8F58BB14}.Release|x64.Build.0 = Release|Any CPU
{3BFCB341-D41D-4F23-B867-EB4A8F58BB14}.Release|x86.ActiveCfg = Release|Any CPU
{3BFCB341-D41D-4F23-B867-EB4A8F58BB14}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
+3 -3
View File
@@ -21,10 +21,10 @@ public class EntityModel
private static Dictionary<string, List<EntityModel>>? _entityModelsByHotkey; private static Dictionary<string, List<EntityModel>>? _entityModelsByHotkey;
public EntityModel(string data, string entity, bool isSpeculative = false) public EntityModel(string dataType, string entityType, bool isSpeculative = false)
{ {
DataType = data; DataType = dataType;
EntityType = entity; EntityType = entityType;
IsSpeculative = isSpeculative; IsSpeculative = isSpeculative;
} }
+258
View File
@@ -0,0 +1,258 @@
using System.Collections.Generic;
using System.Linq;
namespace Model.Glossary;
public static class GlossaryData
{
public static Dictionary<string, GlossaryTermModel> GetTerms()
{
var terms = new List<GlossaryTermModel>
{
// Resources
new()
{
Id = "glossary_alloy", Term = "Alloy", Category = "Resource",
ShortDefinition = "Primary mineral resource used for constructing buildings and training units.",
LongDefinition =
"**Alloy** is the primary mineral resource in IMMORTAL: Gates of Pyre. It is harvested from Alloy nodes by workers and is required for nearly all buildings, units, and upgrades. Managing your Alloy income is fundamental to a strong economy.",
RelatedEntityIds = new List<string> { "STARTING_Bastion" }
},
new()
{
Id = "glossary_ether", Term = "Ether", Category = "Resource",
ShortDefinition = "Secondary resource used for advanced units, research, and abilities.",
LongDefinition =
"**Ether** is a secondary resource harvested from Ether nodes. It is used to train advanced units, research upgrades, and power certain abilities. Ether income typically comes online after establishing your economy.",
RelatedEntityIds = new List<string> { "STARTING_Bastion" }
},
new()
{
Id = "glossary_pyre", Term = "Pyre", Category = "Resource",
ShortDefinition =
"A powerful third resource earned through combat and map control, used for Immortals and spells.",
LongDefinition =
"**Pyre** is a unique resource earned by defeating enemy units, capturing Pyre Camps, and taking Pyre Miners. It is used to summon Immortals, cast Pyre Spells, and activate powerful global effects. Unlike Alloy and Ether, Pyre is not harvested from static nodes — it requires active map presence.",
RelatedTermIds = new List<string> { "glossary_immortal", "glossary_pyrespells" },
RelatedEntityIds = new List<string> { "NEUTRAL_PyreCamp", "NEUTRAL_PyreMiner" }
},
new()
{
Id = "glossary_energy", Term = "Energy", Category = "Resource",
ShortDefinition = "Resource used by certain units to activate abilities.",
LongDefinition =
"**Energy** is a resource that certain units (such as spellcasters) use to activate their abilities. Energy regenerates over time and can be boosted by specific upgrades or structures."
},
new()
{
Id = "glossary_supply", Term = "Supply", Category = "Mechanic",
ShortDefinition = "Population cap. Each unit takes Supply, each Stronghold grants Supply.",
LongDefinition =
"**Supply** represents your population capacity. Most units consume Supply when trained, while certain buildings (primarily Strongholds) grant additional Supply. Running out of Supply prevents further unit training until you build more Supply-generating structures.",
RelatedEntityIds = new List<string> { "BUILDING_Acropolis" }
},
// Core Mechanics
new()
{
Id = "glossary_immortal", Term = "Immortal", Category = "Mechanic",
ShortDefinition =
"Powerful hero units summoned using Pyre, each with unique abilities and vanguard units.",
LongDefinition =
"**Immortals** are powerful hero-like units summoned by spending Pyre. Each Immortal has a unique set of abilities and grants access to **Vanguard** units — upgraded versions of standard units. Immortals can also cast powerful Pyre Spells, making them centerpieces of late-game armies. Current Immortals include Orzum, Ajari, Atzlan, Mala, and Xol.",
RelatedTermIds = new List<string> { "glossary_pyre", "glossary_vanguard", "glossary_pyrespells" }
},
new()
{
Id = "glossary_vanguard", Term = "Vanguard", Category = "Mechanic",
ShortDefinition = "Elite unit variants unlocked by summoning a specific Immortal.",
LongDefinition =
"**Vanguard** units are enhanced variants of standard units that become available when a specific Immortal is summoned. For example, summoning Orzum upgrades Sipari into Zentari. Vanguard units retain the same hotkeys as their base versions but have improved stats and sometimes additional abilities.",
RelatedTermIds = new List<string> { "glossary_immortal" }
},
new()
{
Id = "glossary_pyrespells", Term = "Pyre Spells", Category = "Mechanic",
ShortDefinition = "Powerful global abilities unlocked by summoning an Immortal, cast using Pyre.",
LongDefinition =
"**Pyre Spells** are powerful global effects that can be cast after summoning an Immortal. Each Immortal has a unique Pyre Spell associated with them, and casting it consumes Pyre. Examples include Summon Citadel and other game-changing effects.",
RelatedTermIds = new List<string> { "glossary_pyre", "glossary_immortal" }
},
new()
{
Id = "glossary_morph", Term = "Morph", Category = "Mechanic",
ShortDefinition = "A building or unit conversion that transforms one entity into another.",
LongDefinition =
"**Morph** is a conversion mechanic where one building or unit transforms into a different entity. For example, a Mining Level 2 upgrade morphs from the Acropolis. Morphs typically have a cost and build time but allow upgrading existing structures rather than building new ones."
},
new()
{
Id = "glossary_harass", Term = "Harass", Category = "Mechanic",
ShortDefinition = "A strategy focused on disrupting the opponent's economy and early game.",
LongDefinition =
"**Harass** refers to early-game attacks aimed at disrupting the opponent's economy, worker count, or map control rather than delivering a killing blow. Successful harass can put the opponent behind by delaying their tech or denying resources.",
RelatedTermIds = new List<string> { "glossary_timing" }
},
new()
{
Id = "glossary_timing", Term = "Timing", Category = "Mechanic",
ShortDefinition = "A specific moment in the game where your army is stronger relative to the opponent.",
LongDefinition =
"A **timing** attack is a push designed to hit at a moment when your army composition, upgrades, or economy give you a temporary advantage. Timing attacks are often built around completing a key upgrade or reaching a critical mass of a specific unit type.",
RelatedTermIds = new List<string> { "glossary_harass" }
},
new()
{
Id = "glossary_tier", Term = "Tier", Category = "Mechanic",
ShortDefinition = "Progression level indicating how advanced a unit, building, or upgrade is.",
LongDefinition =
"**Tier** is a numeric progression level assigned to entities. Higher-tier units and buildings generally require more advanced infrastructure and resources. Tier 1 is the early game, Tier 2 is mid-game, and Tier 3 is late-game."
},
new()
{
Id = "glossary_tech", Term = "Tech", Category = "Mechanic",
ShortDefinition = "Upgrades and research that unlock new units, abilities, or improve existing ones.",
LongDefinition =
"**Tech** encompasses all upgrades and research available in the game. Tech upgrades can improve unit stats, unlock new abilities, or enable advanced buildings. Managing your tech progression efficiently is key to a successful build order."
},
// Role / Descriptive Types
new()
{
Id = "glossary_frontliner", Term = "Frontliner", Category = "Role",
ShortDefinition = "A durable unit designed to absorb damage and hold the front line.",
LongDefinition =
"**Frontliner** units are tough, high-health units designed to absorb enemy damage and protect more vulnerable units behind them. They typically have high armor and HP but deal moderate damage.",
RelatedTermIds = new List<string> { "glossary_skirmisher", "glossary_support" }
},
new()
{
Id = "glossary_skirmisher", Term = "Skirmisher", Category = "Role",
ShortDefinition = "A mobile unit that excels at hit-and-run tactics and flanking.",
LongDefinition =
"**Skirmisher** units are fast, mobile units that excel at hit-and-run tactics. They typically have moderate health and high damage output, and are effective at flanking, chasing down retreating units, and harassing worker lines.",
RelatedTermIds = new List<string> { "glossary_frontliner", "glossary_harass" }
},
new()
{
Id = "glossary_support", Term = "Support", Category = "Role",
ShortDefinition = "A unit that provides healing, buffs, or utility rather than direct damage.",
LongDefinition =
"**Support** units provide healing, buffs, debuffs, or other utility to allied units. They typically have low HP and damage but can swing fights with their abilities.",
RelatedTermIds = new List<string> { "glossary_frontliner" }
},
new()
{
Id = "glossary_generalist", Term = "Generalist", Category = "Role",
ShortDefinition = "A versatile unit with balanced stats, effective in many situations.",
LongDefinition =
"**Generalist** units have balanced stats and no extreme strengths or weaknesses. They are reliable in a variety of situations but may be outclassed by specialized units in their specific roles."
},
// Armor and Defense
new()
{
Id = "glossary_armor_light", Term = "Light Armor", Category = "Mechanic",
ShortDefinition = "Armor type with low HP but high speed; vulnerable to certain damage types.",
LongDefinition =
"**Light Armor** units are fast but fragile. They take reduced damage from some attack types but are vulnerable to others. Common on scout units and workers."
},
new()
{
Id = "glossary_armor_medium", Term = "Medium Armor", Category = "Mechanic",
ShortDefinition = "Balanced armor type with moderate HP and speed.",
LongDefinition =
"**Medium Armor** provides a balance of protection and mobility. Most front-line combat units have Medium Armor."
},
new()
{
Id = "glossary_armor_heavy", Term = "Heavy Armor", Category = "Mechanic",
ShortDefinition = "High-durability armor on slow, tough units and structures.",
LongDefinition =
"**Heavy Armor** is found on durable units and buildings. It provides significant damage reduction but units with Heavy Armor are typically slow. Structures like Strongholds have Heavy Armor.",
RelatedEntityIds = new List<string> { "BUILDING_Acropolis" }
},
new()
{
Id = "glossary_armor_etheric", Term = "Etheric Armor", Category = "Mechanic",
ShortDefinition = "Magical armor that provides resistance to spell-type damage.",
LongDefinition =
"**Etheric Armor** provides protection against magical and energy-based attacks. Units with Etheric Armor are typically spellcasters or high-tech units."
},
new()
{
Id = "glossary_shield", Term = "Shield", Category = "Mechanic",
ShortDefinition = "An additional HP layer that regenerates over time when not taking damage.",
LongDefinition =
"**Shields** provide an additional layer of hit points on top of base health. Shield HP regenerates over time when the unit has not taken damage recently, making shielded units strong in hit-and-run engagements."
},
new()
{
Id = "glossary_overgrowth", Term = "Overgrowth", Category = "Mechanic",
ShortDefinition = "A defensive mechanism that heals structures over time.",
LongDefinition =
"**Overgrowth** is a defensive layer unique to certain factions that slowly regenerates the health of structures, making base defenses more resilient over time."
},
// Factions
new()
{
Id = "glossary_faction_aru", Term = "Aru", Category = "Faction",
ShortDefinition = "A faction themed around fire, demons, and aggressive play.",
LongDefinition =
"The **Aru** are a demonic faction focused on aggressive, high-damage play. Their units emphasize raw firepower and mobility. Their Immortals include Atzlan, Mala, and Xol.",
RelatedTermIds = new List<string> { "glossary_immortal" },
RelatedEntityIds = new List<string> { "FACTION_Aru" }
},
new()
{
Id = "glossary_faction_qrath", Term = "Q'Rath", Category = "Faction",
ShortDefinition = "A faction themed around order, light, and defensive play.",
LongDefinition =
"The **Q'Rath** are an angelic/order-themed faction focused on defensive play and sustained engagements. Their units emphasize durability and support. Their Immortals include Orzum and Ajari.",
RelatedTermIds = new List<string> { "glossary_immortal" },
RelatedEntityIds = new List<string> { "FACTION_QRath" }
},
// Movement
new()
{
Id = "glossary_movement_ground", Term = "Ground", Category = "Mechanic",
ShortDefinition = "Units that move along the ground and can be blocked by terrain.",
LongDefinition =
"**Ground** units move along the terrain surface and can be blocked by obstacles, buildings, and other units. Most melee units are Ground units."
},
new()
{
Id = "glossary_movement_hover", Term = "Hover", Category = "Mechanic",
ShortDefinition = "Units that float above the ground and ignore terrain obstacles.",
LongDefinition =
"**Hover** units float above the ground, allowing them to pass over obstacles and terrain features that would block Ground units. They cannot be blocked by units."
},
new()
{
Id = "glossary_movement_air", Term = "Air", Category = "Mechanic",
ShortDefinition = "Flying units that ignore all terrain and can attack from above.",
LongDefinition =
"**Air** units fly over all terrain and obstacles. They can only be attacked by units that target Air. Air units provide excellent map control and mobility."
},
// Target Types
new()
{
Id = "glossary_target_ground", Term = "Ground Target", Category = "Mechanic",
ShortDefinition = "Attacks or abilities that only hit ground units.",
LongDefinition =
"**Ground Target** attacks and abilities can only affect units on the ground. Anti-air units require the ability to target Air units."
},
new()
{
Id = "glossary_target_air", Term = "Air Target", Category = "Mechanic",
ShortDefinition = "Attacks or abilities that only hit air units.",
LongDefinition = "**Air Target** attacks and abilities can only affect flying units."
}
};
return terms.ToDictionary(t => t.Id);
}
}
+14
View File
@@ -0,0 +1,14 @@
using System.Collections.Generic;
namespace Model.Glossary;
public class GlossaryTermModel
{
public string Id { get; set; } = "";
public string Term { get; set; } = "";
public string ShortDefinition { get; set; } = "";
public string LongDefinition { get; set; } = "";
public string Category { get; set; } = "";
public List<string> RelatedTermIds { get; set; } = new();
public List<string> RelatedEntityIds { get; set; } = new();
}
+13
View File
@@ -0,0 +1,13 @@
namespace Model.TechTree;
public static class TechTreeEdgeType
{
public static string Produces = "Produces";
public static string ProducedAt = "ProducedAt";
public static string RequiresProduction = "RequiresProduction";
public static string RequiresResearch = "RequiresResearch";
public static string Morph = "Morph";
public static string Upgrades = "Upgrades";
public static string UpgradedBy = "UpgradedBy";
public static string VanguardReplaces = "VanguardReplaces";
}
+29
View File
@@ -0,0 +1,29 @@
using System.Collections.Generic;
namespace Model.TechTree;
public class TechTreeNodeModel
{
public string Id { get; set; } = "";
public string Name { get; set; } = "";
public string EntityType { get; set; } = "";
public string Faction { get; set; } = "";
public string Descriptive { get; set; } = "";
public int Layer { get; set; }
public float X { get; set; }
public float Y { get; set; }
}
public class TechTreeEdgeModel
{
public string SourceId { get; set; } = "";
public string TargetId { get; set; } = "";
public string EdgeType { get; set; } = "";
}
public class TechTreeGraphModel
{
public List<TechTreeNodeModel> Nodes { get; set; } = new();
public List<TechTreeEdgeModel> Edges { get; set; } = new();
public Dictionary<string, List<string>> Unlocks { get; set; } = new();
}
+43 -7
View File
@@ -1,14 +1,28 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
namespace Model.Website.Data; namespace Model.Website.Data;
public class WebsiteData public class WebsiteData
{ {
/**
* Flag for content generated by the AI. Can contain UI that makes no sense, or more importantly,
* game data that is not real. Fun to look at, but needs to be thoroughly vetted before it can ever go live.
*/
public static bool allowSlopData { get; set; } = true;
private static bool IsPageAllowed(WebPageModel page)
{
if (allowSlopData) return true;
return page.Id != 5 && page.Id != 6;
}
public static List<WebPageModel> GetPages() public static List<WebPageModel> GetPages()
{ {
return var pages = new List<WebPageModel>
[ {
new WebPageModel new()
{ {
Id = 2, Id = 2,
WebSectionModelId = 2, WebSectionModelId = 2,
@@ -18,7 +32,7 @@ public class WebsiteData
IsPrivate = "False", IsPrivate = "False",
Icon = "fa-solid fa-helmet-battle" Icon = "fa-solid fa-helmet-battle"
}, },
new WebPageModel new()
{ {
Id = 1, Id = 1,
WebSectionModelId = 2, WebSectionModelId = 2,
@@ -28,7 +42,7 @@ public class WebsiteData
IsPrivate = "False", IsPrivate = "False",
Icon = "fa-solid fa-clipboard-list" Icon = "fa-solid fa-clipboard-list"
}, },
new WebPageModel new()
{ {
Id = 3, Id = 3,
WebSectionModelId = 2, WebSectionModelId = 2,
@@ -38,7 +52,7 @@ public class WebsiteData
IsPrivate = "False", IsPrivate = "False",
Icon = "fa-solid fa-bow-arrow" Icon = "fa-solid fa-bow-arrow"
}, },
new WebPageModel new()
{ {
Id = 4, Id = 4,
WebSectionModelId = 2, WebSectionModelId = 2,
@@ -47,7 +61,29 @@ public class WebsiteData
Href = "data-tables", Href = "data-tables",
IsPrivate = "False", IsPrivate = "False",
Icon = "fa-solid fa-table-list" Icon = "fa-solid fa-table-list"
},
new()
{
Id = 5,
WebSectionModelId = 2,
Name = "Glossary",
Description = "Reference for game terms and mechanics",
Href = "glossary",
IsPrivate = "False",
Icon = "fa-solid fa-book-open"
},
new()
{
Id = 6,
WebSectionModelId = 2,
Name = "Tech Tree",
Description = "Interactive tech tree visualization",
Href = "tech-tree",
IsPrivate = "False",
Icon = "fa-solid fa-diagram-project"
} }
]; };
return pages.Where(IsPageAllowed).ToList();
} }
} }
+226
View File
@@ -0,0 +1,226 @@
@implements IDisposable;
@inject IGlossaryService glossaryService
@inject IGlossaryDialogService glossaryDialogService
<div class="dialogBackground" onclick="@CloseDialog">
<div class="dialogContainer glossaryDialog"
@onclick:preventDefault="true"
@onclick:stopPropagation="true">
@if (term == null)
{
<div>Term is null</div>
}
else
{
<div class="dialogHeader">
@if (glossaryDialogService.HasHistory())
{
<button class="backButton" @onclick="glossaryDialogService.BackDialog">
<div class="backButtonIcon"></div>
</button>
}
<div class="dialogTitle">
@term.Term
</div>
<div class="glossaryDialogCategory">@term.Category</div>
</div>
<div class="dialogContent">
<div class="glossaryDefinition">@((MarkupString)RenderMarkdown(term.LongDefinition))</div>
@if (term.RelatedEntityIds.Count > 0)
{
<div class="glossaryRelatedSection">
<div class="glossaryRelatedTitle">Related Entities</div>
<div class="glossaryRelatedList">
@foreach (var entityId in term.RelatedEntityIds)
{
<EntityLabelComponent EntityId="@entityId"/>
}
</div>
</div>
}
@if (term.RelatedTermIds.Count > 0)
{
<div class="glossaryRelatedSection">
<div class="glossaryRelatedTitle">Related Terms</div>
<div class="glossaryRelatedList">
@foreach (var relatedId in term.RelatedTermIds)
{
<GlossaryLabelComponent TermId="@relatedId"/>
}
</div>
</div>
}
</div>
<div class="dialogFooter"></div>
}
</div>
</div>
<style>
.pageContents * {
filter: blur(2px);
}
.dialogBackground {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
}
.dialogContainer {
margin-left: auto;
margin-right: auto;
margin-top: 64px;
width: 800px;
height: 600px;
background-color: var(--background);
border-width: var(--dialog-border-width);
border-style: solid;
border-color: var(--dialog-border-color);
border-radius: var(--dialog-radius);
box-shadow: 1px 2px 2px black;
}
.dialogHeader {
width: 100%;
background-color: var(--accent);
border-top-left-radius: var(--dialog-radius);
border-top-right-radius: var(--dialog-radius);
border-bottom: 4px solid black;
display: flex;
align-items: center;
justify-content: flex-start;
}
.backButton {
margin-left: 16px;
padding: 12px;
border: 1px solid var(--accent);
}
.backButton:hover {
background-color: var(--primary-hover);
border: 1px solid var(--primary-border-hover);
}
.backButtonIcon {
height: 32px;
width: 32px;
border: solid white;
border-width: 0 9px 9px 0;
transform: rotate(135deg);
}
.dialogTitle {
padding: 16px;
font-size: 2rem;
font-weight: bold;
}
.dialogContent {
flex-grow: 1;
padding: 6px;
overflow-y: auto;
overflow-x: hidden;
height: 800px;
}
.dialogFooter {
width: 100%;
height: 6px;
background-color: var(--paper);
}
.glossaryDialog {
max-width: 600px;
}
.glossaryDialogCategory {
font-size: 0.8rem;
opacity: 0.7;
margin-left: 12px;
}
.glossaryDefinition {
line-height: 1.6;
}
.glossaryDefinition p {
margin-bottom: 12px;
}
.glossaryRelatedSection {
margin-top: 20px;
padding-top: 16px;
border-top: 1px solid var(--info-secondary-border);
}
.glossaryRelatedTitle {
font-weight: 800;
font-size: 1rem;
margin-bottom: 8px;
}
.glossaryRelatedList {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
</style>
@code {
private GlossaryTermModel? term;
protected override void OnInitialized()
{
base.OnInitialized();
glossaryDialogService.Subscribe(OnUpdate);
LoadTerm();
}
void IDisposable.Dispose()
{
glossaryDialogService.Unsubscribe(OnUpdate);
}
void OnUpdate()
{
LoadTerm();
StateHasChanged();
}
void LoadTerm()
{
var id = glossaryDialogService.GetTermId();
term = id != null ? glossaryService.GetTerm(id) : null;
}
void CloseDialog()
{
glossaryDialogService.CloseDialog();
}
string RenderMarkdown(string text)
{
return Markdown.ToHtml(text);
}
}
+42
View File
@@ -0,0 +1,42 @@
<Project Sdk="Microsoft.NET.Sdk.Razor">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<RootNamespace>IGP</RootNamespace>
</PropertyGroup>
<ItemGroup>
<SupportedPlatform Include="browser"/>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Markdig" Version="1.1.1"/>
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="10.0.5"/>
<PackageReference Include="Microsoft.Extensions.Localization" Version="10.0.5"/>
<PackageReference Include="MudBlazor" Version="9.2.0"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Components\Components.csproj"/>
<ProjectReference Include="..\Model\Model.csproj"/>
<ProjectReference Include="..\Services\Services.csproj"/>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Localizations.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Localizations.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<Compile Update="Localizations.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Localizations.resx</DependentUpon>
</Compile>
</ItemGroup>
</Project>
@@ -1,4 +1,3 @@
@layout PageLayout
@inherits BasePage @inherits BasePage
@@ -35,7 +34,7 @@
The game hasn't even been released. Isn't it a bit early for publishing community tools? The game hasn't even been released. Isn't it a bit early for publishing community tools?
</InfoQuestionComponent> </InfoQuestionComponent>
<InfoAnswerComponent> <InfoAnswerComponent>
Maybe 🙂 Maybe ??
</InfoAnswerComponent> </InfoAnswerComponent>
</InfoBodyComponent> </InfoBodyComponent>
<InfoBodyComponent> <InfoBodyComponent>
@@ -1,4 +1,3 @@
@layout PageLayout
@inherits BasePage @inherits BasePage
@@ -1,4 +1,3 @@
@layout PageLayout
@inject IDataCollectionService DataCollectionService @inject IDataCollectionService DataCollectionService
@inherits BasePage @inherits BasePage
@@ -1,9 +1,8 @@
@page "/data-collection" @page "/data-collection"
@inject IDataCollectionService DataCollectionService @inject IDataCollectionService DataCollectionService
@inherits BasePage @inherits BasePage
@layout PageLayout
<LayoutMediumContentComponent> <LayoutMediumContentComponent>
@@ -1,4 +1,3 @@
@layout PageLayout
@using IGP.Pages.DataTables.Parts @using IGP.Pages.DataTables.Parts
@inherits BasePage @inherits BasePage
@@ -1,4 +1,3 @@
@layout PageLayout
@inherits BasePage @inherits BasePage
@page "/database" @page "/database"
@using Model @using Model
@@ -1,4 +1,3 @@
@layout PageLayout
@inherits BasePage @inherits BasePage

Some files were not shown because too many files have changed in this diff Show More