11 Commits

Author SHA1 Message Date
c2b9d0b3c2 tmp
Some checks failed
Main Build Process / Build & Test (pull_request) Failing after 1m15s
2026-02-23 22:58:44 +01:00
5ef3be8cc8 mise en place de la structure de base du plateau (#40)
All checks were successful
Merging on main / Build & Test (push) Successful in 1m10s
Co-authored-by: mcmuzzle <vezuvan@hotmail.com>
Reviewed-on: #40
2026-02-23 10:21:01 +00:00
eea8bc69e1 creation des projets blazor pour le GUI
All checks were successful
Merging on main / Build & Test (push) Successful in 1m8s
2026-02-09 21:32:41 +00:00
1f98c9806b init de la documentation
All checks were successful
Main Build Process / Build & Test (pull_request) Successful in 1m10s
Merging on main / Build & Test (push) Successful in 1m1s
2026-02-09 16:54:06 +01:00
5c208ea408 ajout d'un pipeline pour la branche main
All checks were successful
Main Build Process / Build & Test (pull_request) Successful in 1m9s
Merging on main / Build & Test (push) Successful in 1m0s
2026-02-09 14:40:34 +01:00
f089f436a7 Finalisation de la CI pour la qualité de code (#34)
Co-authored-by: mcmuzzle <vezuvan@hotmail.com>
Reviewed-on: #34
2026-02-09 10:08:52 +00:00
Renovate Bot
a400758b8d Update dependency xunit.runner.visualstudio to v3
All checks were successful
Main Build Process / Build & Test (pull_request) Successful in 1m25s
2026-02-07 15:25:09 +00:00
Renovate Bot
296a16651e Update vstest monorepo to v18
All checks were successful
Main Build Process / Build & Test (pull_request) Successful in 1m12s
2026-02-07 14:33:58 +00:00
Renovate Bot
bf85247b41 Update dependency Swashbuckle.AspNetCore to v10
All checks were successful
Main Build Process / Build & Test (pull_request) Successful in 1m10s
2026-02-06 22:38:17 +00:00
Renovate Bot
7a3d0a97d8 Update dependency NUnit3TestAdapter to v6
All checks were successful
Main Build Process / Build & Test (pull_request) Successful in 1m12s
2026-02-06 22:26:15 +00:00
Renovate Bot
63f1a467c0 Update actions/setup-dotnet action to v5
All checks were successful
Main Build Process / Build & Test (pull_request) Successful in 1m27s
2026-02-06 22:15:58 +00:00
108 changed files with 61751 additions and 45 deletions

View File

@@ -0,0 +1,32 @@
name: "Merging on main"
on:
push:
branches: ["main"]
env:
REGISTRY: gitea.borealian.ovh
IMAGE_NAME: ${{ github.repository }}
jobs:
build:
name: "Build & Test"
# Permissions this GitHub Action needs for other things in GitHub
permissions: write-all
runs-on: ubuntu-latest
steps:
- name: Check out the code
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Setup .NET
uses: actions/setup-dotnet@v5
with:
dotnet-version: |
10.x
- name: "Restore/Build/Test"
run: |
dotnet test ./src/LittleTown.sln --configuration Release --verbosity normal --logger trx --collect:"XPlat Code Coverage" --logger:"trx;LogFileName=test_results.xml"

View File

@@ -24,7 +24,7 @@ jobs:
with:
fetch-depth: 0
- name: Setup .NET
uses: actions/setup-dotnet@v4
uses: actions/setup-dotnet@v5
with:
dotnet-version: |
10.x
@@ -49,14 +49,9 @@ jobs:
username: "gitea"
password: "password"
port: 2222
source: "${{ github.workspace }}/coverage-report/*"
target: "/config/test"
- name: Upload Combined Coverage XML
uses: actions/upload-artifact@v3
with:
name: coverage
path: ${{ github.workspace }}/coverage-report/Cobertura.xml
retention-days: 5
source: "coverage-report/*"
target: "/config/docs/workspace/${{ github.repository_owner }}/${{ github.event.repository.name }}/${{ github.event.pull_request.number }}/coverage-report/"
strip_components: 1
- name: Publish Code Coverage Report
uses: irongut/CodeCoverageSummary@v1.3.0
with:
@@ -79,7 +74,7 @@ jobs:
run: |
COMMENT_CONTENT=$(cat code-coverage-results.md)
COMMENT_CONTENT="${COMMENT_CONTENT}\n\n📊 [Rapport HTML complet](https://docs.borealian.ovh/workspace/mcmuzzle/LittleTown/coverage-report/index.html)"
COMMENT_CONTENT="${COMMENT_CONTENT}\n\n📊 [Rapport HTML complet](https://docs.borealian.ovh/workspace/${{ github.repository_owner }}//${{ github.event.repository.name }}/${{ github.event.pull_request.number }}/coverage-report/index.html)"
curl -X POST \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/json" \

3
.gitignore vendored
View File

@@ -16,4 +16,5 @@ bin
obj
report
tools/
TestResults
TestResults
/src/.vs

9
docker-dev/compose.yml Normal file
View File

@@ -0,0 +1,9 @@
services:
kroki:
container_name: kroki
image: yuzutech/kroki
ports:
- 8000:8000
volumes:
- ../.docker/kroki/data:/data

View File

@@ -0,0 +1,17 @@
== Installation de Poste
==== rendu de la documentation
pour visualiser la documentation il faut ajouter au `settings.json` :
[source,json]
----
"asciidoc.extensions.enableKroki": true,
"plantuml.includepaths": ["<chemin vers le dossier contenant les themes PlantUML>"],
"plantuml.jar": "<Chemin vers le jar de PlantUML>",
----
de plus, il faut que le container kroki soit lancé, pour cela il faut exécuter la commande suivante à la racine du projet :
[source,bash]
docker compose -f docker-dev/compose.yml up -d kroki

View File

@@ -0,0 +1,49 @@
= Structure du projet LittleTown
== Arborescence des dossiers
[source,text]
----
LittleTown/
├── .gitea/
│ └── workflows/ # Pipelines CI/CD pour Gitea
├── docker-dev/
│ └── compose.yml # Configuration Docker pour l'environnemenet de développement
├── documentation/ # Documentation du projet
├── scripts/ # Scripts d'automatisation (build, tests, déploiement)
└── src/ # Code source de l'application
├── LittleTown.Api/ # API backend (non utilisé actuellement)
├── LittleTown.Blazor/ # Application Blazor Server et WebAssembly
│ ├── LittleTown.Blazor/ # Application Blazor Server
│ └── LittleTown.Blazor.Client/ # Application Blazor WebAssembly
├── LittleTown.Core/ # Classes de domaine et modèles
├── LittleTown.Core.Tests/ # Tests unitaires
└── LittleTown.StaticDataAccess/ # Accès aux données statiques
----
== Description des projets
=== LittleTown.Api
Projet de l'API backend. Pour le moment, il n'est pas utilisé dans l'architecture actuelle.
=== LittleTown.Blazor
Application principale utilisant Blazor Server et Blazor WebAssembly en mode interactif.
* *LittleTown.Blazor* : Composant serveur, il inclut le projet .client
* *LittleTown.Blazor.Client* : Composant client WebAssembly
=== LittleTown.Core
Cœur du domaine métier, contient :
* *Aggregates* : Les agrégats DDD (Board, Match, PlayerZone)
* *Enumerations* : Types énumérés du domaine
* *Exceptions* : Exceptions métier
* *PortInterfaces* : Interfaces des ports (architecture hexagonale)
* *UseCases* : Cas d'utilisation métier
* *ValueTypes* : Objets valeur du domaine
=== LittleTown.Core.Tests
Tests unitaires du domaine métier.
=== LittleTown.StaticDataAccess
Gestion de l'accès aux données statiques du jeu (cartes, ressources, configuration).

View File

@@ -9,7 +9,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.2" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.9.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="10.1.2" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ActiveDebugProfile>https</ActiveDebugProfile>
</PropertyGroup>
</Project>

View File

@@ -23,4 +23,4 @@ app.MapGet("/helloWorld", () =>
.WithName("HelloWorld")
.WithOpenApi();
app.Run();
await app.RunAsync();

View File

@@ -0,0 +1,3 @@
<div class="rootTile">
@Building.Name
</div>

View File

@@ -0,0 +1,11 @@
using LittleTown.Core;
using Microsoft.AspNetCore.Components;
namespace LittleTown.Blazor.Client.Components
{
public partial class AvailibleBuildingTile
{
[Parameter, EditorRequired]
public Building Building { get; set; }
}
}

View File

@@ -0,0 +1,4 @@
root {
width: 100%;
height: 100%;
}

View File

@@ -0,0 +1,76 @@
<div class="board">
<div class="terrain">
@for(int y = Board.Height-1; y >= 0; y--)
{
<div class="terrain-row">
@for (int x = 0; x < Board.Width; x++)
{
<span class="terrain-cell">
<TerrainCell Tile="Board.GetTile(x,y)" />
</span>
}
</div>
}
</div>
<div class="footer">
<div class="topScore">
@for(int i = 0; i < 25; i++)
{
<div class="scoreItem">@i</div>
}
</div>
<div class="centerFooter">
<div class="leftScore">
@for (int i = 59; i >= 55; i--)
{
<div class="scoreItem">@i</div>
}
</div>
<div class="infos">
<div class="infos-row">
<div class="turns">
@for(int i = 0; i < 4; i++)
{
<div class="turn">@i</div>
}
</div>
@for (int i = 0; i < 6; i++)
{
<div class="building">
@if (!Match.BuildingInfos.ElementAt(i).IsBuilt)
{
<AvailibleBuildingTile Building="Match.BuildingInfos.ElementAt(i).Building"></AvailibleBuildingTile>
}
</div>
}
</div>
<div class="infos-row">
<div class="construction">constructions</div>
<div class="fields">fields</div>
@for (int i = 0; i < 6; i++)
{
<div class="building">
@if (!Match.BuildingInfos.ElementAt(i+6).IsBuilt)
{
<AvailibleBuildingTile Building="Match.BuildingInfos.ElementAt(i+6).Building"></AvailibleBuildingTile>
}
</div>
}
</div>
</div>
<div class="rightScore">
@for (int i = 25; i < 30; i++)
{
<div class="scoreItem">@i</div>
}
</div>
</div>
<div class="bottomScore">
@for (int i = 54; i >= 30; i--)
{
<div class="scoreItem">@i</div>
}
</div>
</div>
</div>

View File

@@ -0,0 +1,23 @@
using LittleTown.Core;
using LittleTown.Core.Ports;
using Microsoft.AspNetCore.Components;
namespace LittleTown.Blazor.Client.Components
{
public partial class MatchBoard
{
[Inject]
private IStaticDataGetter StaticDataGetter { get; set; } = default!;
[Parameter, EditorRequired]
public Core.Match Match { get; init; }
public Board Board { get; private set; }
protected override void OnParametersSet()
{
Board = StaticDataGetter.GetBoard(1);
}
}
}

View File

@@ -0,0 +1,110 @@
.board {
display: flex;
flex-direction: column;
width: 100%;
height: 100%;
}
.terrain {
width: 100%;
background-color: red;
box-sizing: border-box;
}
.terrain-row {
display: grid;
grid-auto-flow: column;
grid-auto-columns: 1fr; /* toutes les cellules même largeur */
}
.terrain-cell {
aspect-ratio: 1 / 1; /* largeur = hauteur */
display: flex;
align-items: center;
justify-content: center;
border: 1px solid black;
}
.footer {
width: 100%;
background-color: gray;
display: flex;
flex-direction: column;
}
.topScore {
flex: 1;
display: flex;
flex-direction: row;
}
.bottomScore {
flex: 1;
display: flex;
flex-direction: row;
}
.centerFooter{
flex:5;
display: flex;
flex-direction:row;
}
.scoreItem {
flex: 1;
border: 1px solid;
aspect-ratio: 1 / 1;
}
.leftScore {
flex:1;
display:flex;
flex-direction:column;
}
.rightScore {
flex: 1;
display: flex;
flex-direction: column;
}
.infos {
flex:23;
display: flex;
flex-wrap:wrap;
}
.building {
height: 100%;
aspect-ratio: 1 / 1;
flex: 0 0 auto;
background: lightblue;
border: 1px solid black;
}
.turns {
flex: 1;
display: flex;
flex-direction: row;
}
.turn {
flex: 1;
}
.infos-row{
height:50%;
width:100%;
background-color:blue;
display:flex;
flex-direction:row;
}
.construction {
flex:1;
}
.fields {
height: 100%;
aspect-ratio: 1 / 1; /* largeur = hauteur */
flex: 0 0 auto;
background: lightblue;
border: 1px solid black;
}

View File

@@ -0,0 +1,5 @@
<div class="cell-root @CssTileType">
<h3>@Tile.ResourceType</h3>
</div>

View File

@@ -0,0 +1,28 @@
using LittleTown.Core;
using LittleTown.Core.Enums;
using Microsoft.AspNetCore.Components;
namespace LittleTown.Blazor.Client.Components
{
public partial class TerrainCell
{
[Parameter, EditorRequired]
public Tile Tile { get; init; }
private string CssTileType { get; set; } = string.Empty;
protected override void OnInitialized()
{
CssTileType = Tile.ResourceType switch
{
ResourceType.None => "",
ResourceType.Rock => "rock",
ResourceType.Wood => "forest",
ResourceType.Fish => "lake",
ResourceType.Cereal => "",
ResourceType.Piece => "",
_ => string.Empty
};
}
}
}

View File

@@ -0,0 +1,18 @@
.cell-root {
width: 100%;
height: 100%;
}
.rock {
background-color: gray;
}
.lake {
background-color: blue;
}
.forest
{
background-color: green;
}

View File

@@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<NoDefaultLaunchSettingsFile>true</NoDefaultLaunchSettingsFile>
<StaticWebAssetProjectMode>Default</StaticWebAssetProjectMode>
<BlazorDisableThrowNavigationException>true</BlazorDisableThrowNavigationException>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="10.0.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\LittleTown.Core\LittleTown.Core.csproj" />
<ProjectReference Include="..\..\LittleTown.StaticDataAccess\LittleTown.StaticDataAccess.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,21 @@
@page "/counter"
@rendermode InteractiveAuto
<PageTitle>Counter</PageTitle>
<h1>Counter</h1>
<p role="status">Current count: @currentCount</p>
<p> le rendu est en : @(RendererInfo.Name) - @(RendererInfo.IsInteractive ? "Interactive" : "Static")</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
private int currentCount = 0;
private void IncrementCount()
{
currentCount++;
}
}

View File

@@ -0,0 +1,8 @@
@page "/match"
@rendermode InteractiveAuto
<PageTitle>Match</PageTitle>
<div class="board">
<MatchBoard Match="match"/>
</div>

View File

@@ -0,0 +1,21 @@
using LittleTown.Blazor.Client.Services;
using Microsoft.AspNetCore.Components;
namespace LittleTown.Blazor.Client.Pages;
public partial class Match(IMatchService matchServices)
{
Core.Match match;
public async override Task SetParametersAsync(ParameterView parameters)
{
match = matchServices.GetMatch();
match.AddPlayer("Andre");
match.AddPlayer("Maelie");
match.AddPlayer("Liam");
await match.Init();
await base.SetParametersAsync(parameters);
}
}

View File

@@ -0,0 +1,4 @@
.board {
width: 100%;
height: 50vh;
}

View File

@@ -0,0 +1,12 @@
using LittleTown.Blazor.Client.Services;
using LittleTown.Core.Ports;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
builder.Services.AddScoped<IStaticDataGetter, WasmStaticDataGetter>();
builder.Services.AddScoped<IMatchService, MatchService>();
await builder.Build().RunAsync();

View File

@@ -0,0 +1,13 @@
using LittleTown.Core;
namespace LittleTown.Blazor.Client.Services
{
public interface IMatchService
{
/// <summary>
/// Temporaire, recupere l'instance d'un match par défaut
/// </summary>
/// <returns></returns>
Match GetMatch();
}
}

View File

@@ -0,0 +1,26 @@
using LittleTown.Core;
using LittleTown.Core.Ports;
namespace LittleTown.Blazor.Client.Services
{
public class MatchService : IMatchService
{
private readonly IStaticDataGetter _staticDataGetter;
private Match? _currentMatch;
public MatchService(IStaticDataGetter staticDataGetter)
{
_staticDataGetter = staticDataGetter;
}
/// <inheritdoc/>>
public Match GetMatch()
{
if (null == _currentMatch)
{
_currentMatch = new Match(_staticDataGetter);
}
return _currentMatch;
}
}
}

View File

@@ -0,0 +1,81 @@
using System.Text.Json;
using LittleTown.Core;
using LittleTown.Core.Ports;
namespace LittleTown.Blazor.Client.Services;
/// <summary>
/// Implementation du getter de données static pour WebAssembly via HttpClient
/// </summary>
public class WasmStaticDataGetter : IStaticDataGetter
{
private readonly HttpClient _httpClient;
private string _boardData = "";
private string _buildingData = "";
private string _objectivesData = "";
private bool _isInitialized = false;
public WasmStaticDataGetter(HttpClient httpClient)
{
_httpClient = httpClient;
}
private async Task InitializeAsync()
{
if (_isInitialized) return;
_boardData = await _httpClient.GetStringAsync("data/Board1.json");
_buildingData = await _httpClient.GetStringAsync("data/Buildings.json");
_objectivesData = await _httpClient.GetStringAsync("data/Objectives.json");
_isInitialized = true;
}
/// <inheritdoc/>
public async Task<Board> GetBoardAsync(int version)
{
// Synchronous wrapper - not ideal but matches interface
await InitializeAsync();
Board board = JsonSerializer.Deserialize<Board>(_boardData)
?? throw new JsonException("Cannot deserialize Board");
return board;
}
public Board GetBoard(int version)
{
throw new NotImplementedException("Use the asynchronous version of GetBoard for WebAssembly.");
}
/// <inheritdoc/>
public async Task<ICollection<Building>> GetBuildingsAsync()
{
await InitializeAsync();
List<Building> buildings = JsonSerializer.Deserialize<List<Building>>(_buildingData)
?? throw new JsonException("Cannot deserialize Buildings");
return buildings;
}
/// <inheritdoc/>
public ICollection<Building> GetBuildings()
{
throw new NotImplementedException("Use the asynchronous version of GetBuildings for WebAssembly.");
}
/// <inheritdoc/>
public ICollection<Objective> GetObjectives()
{
throw new NotImplementedException("Use the asynchronous version of GetObjectives for WebAssembly.");
}
/// <inheritdoc/>
public async Task<ICollection<Objective>> GetObjectivesAsync()
{
await InitializeAsync();
List<Objective> objectives = JsonSerializer.Deserialize<List<Objective>>(_objectivesData)
?? throw new JsonException("Cannot deserialize Objectives");
return objectives;
}
}

View File

@@ -0,0 +1,11 @@
@using System.Net.Http
@using System.Net.Http.Json
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using static Microsoft.AspNetCore.Components.Web.RenderMode
@using Microsoft.AspNetCore.Components.Web.Virtualization
@using Microsoft.JSInterop
@using LittleTown.Blazor.Client
@using LittleTown.Blazor.Client.Components
@using LittleTown.StaticDataAcces;

View File

@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}

View File

@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}

View File

@@ -0,0 +1,276 @@
{
"Width": 9,
"Height": 6,
"Tiles": [
{
"ResourceType": "None",
"PosX": 0,
"PosY": 0
},
{
"ResourceType": "Wood",
"PosX": 1,
"PosY": 0
},
{
"ResourceType": "Wood",
"PosX": 2,
"PosY": 0
},
{
"ResourceType": "None",
"PosX": 3,
"PosY": 0
},
{
"ResourceType": "Fish",
"PosX": 4,
"PosY": 0
},
{
"ResourceType": "Fish",
"PosX": 5,
"PosY": 0
},
{
"ResourceType": "None",
"PosX": 6,
"PosY": 0
},
{
"ResourceType": "None",
"PosX": 7,
"PosY": 0
},
{
"ResourceType": "Wood",
"PosX": 8,
"PosY": 0
},
{
"ResourceType": "None",
"PosX": 0,
"PosY": 1
},
{
"ResourceType": "None",
"PosX": 1,
"PosY": 1
},
{
"ResourceType": "None",
"PosX": 2,
"PosY": 1
},
{
"ResourceType": "None",
"PosX": 3,
"PosY": 1
},
{
"ResourceType": "None",
"PosX": 4,
"PosY": 1
},
{
"ResourceType": "None",
"PosX": 5,
"PosY": 1
},
{
"ResourceType": "Wood",
"PosX": 6,
"PosY": 1
},
{
"ResourceType": "None",
"PosX": 7,
"PosY": 1
},
{
"ResourceType": "None",
"PosX": 8,
"PosY": 1
},
{
"ResourceType": "Wood",
"PosX": 0,
"PosY": 2
},
{
"ResourceType": "None",
"PosX": 1,
"PosY": 2
},
{
"ResourceType": "None",
"PosX": 2,
"PosY": 2
},
{
"ResourceType": "None",
"PosX": 3,
"PosY": 2
},
{
"ResourceType": "None",
"PosX": 4,
"PosY": 2
},
{
"ResourceType": "None",
"PosX": 5,
"PosY": 2
},
{
"ResourceType": "None",
"PosX": 6,
"PosY": 2
},
{
"ResourceType": "None",
"PosX": 7,
"PosY": 2
},
{
"ResourceType": "Fish",
"PosX": 8,
"PosY": 2
},
{
"ResourceType": "Fish",
"PosX": 0,
"PosY": 3
},
{
"ResourceType": "None",
"PosX": 1,
"PosY": 3
},
{
"ResourceType": "None",
"PosX": 2,
"PosY": 3
},
{
"ResourceType": "None",
"PosX": 3,
"PosY": 3
},
{
"ResourceType": "None",
"PosX": 4,
"PosY": 3
},
{
"ResourceType": "None",
"PosX": 5,
"PosY": 3
},
{
"ResourceType": "None",
"PosX": 6,
"PosY": 3
},
{
"ResourceType": "None",
"PosX": 7,
"PosY": 3
},
{
"ResourceType": "None",
"PosX": 8,
"PosY": 3
},
{
"ResourceType": "None",
"PosX": 0,
"PosY": 4
},
{
"ResourceType": "None",
"PosX": 1,
"PosY": 4
},
{
"ResourceType": "None",
"PosX": 2,
"PosY": 4
},
{
"ResourceType": "None",
"PosX": 3,
"PosY": 4
},
{
"ResourceType": "None",
"PosX": 4,
"PosY": 4
},
{
"ResourceType": "None",
"PosX": 5,
"PosY": 4
},
{
"ResourceType": "None",
"PosX": 6,
"PosY": 4
},
{
"ResourceType": "None",
"PosX": 7,
"PosY": 4
},
{
"ResourceType": "None",
"PosX": 8,
"PosY": 4
},
{
"ResourceType": "Rock",
"PosX": 0,
"PosY": 5
},
{
"ResourceType": "None",
"PosX": 1,
"PosY": 5
},
{
"ResourceType": "Wood",
"PosX": 2,
"PosY": 5
},
{
"ResourceType": "Rock",
"PosX": 3,
"PosY": 5
},
{
"ResourceType": "None",
"PosX": 4,
"PosY": 5
},
{
"ResourceType": "Fish",
"PosX": 5,
"PosY": 5
},
{
"ResourceType": "None",
"PosX": 6,
"PosY": 5
},
{
"ResourceType": "Rock",
"PosX": 7,
"PosY": 5
},
{
"ResourceType": "Rock",
"PosX": 8,
"PosY": 5
}
]
}

View File

@@ -0,0 +1,183 @@
[
{
"Name": "Atelier",
"Price": {
"Rock": 2
},
"BeginnerBuilding": true
},
{
"Name": "Bar",
"Price": {
"Rock": 2,
"Cereal": 2
},
"BeginnerBuilding": false
},
{
"Name": "Boulangerie",
"Price": {
"Wood": 2
},
"BeginnerBuilding": true
},
{
"Name": "Brasserie",
"Price": {
"Wood": 2
},
"BeginnerBuilding": false
},
{
"Name": "Carriere",
"Price": {
"Wood": 3
},
"BeginnerBuilding": true
},
{
"Name": "Champsdeble",
"Price": {
"Wood": 1
},
"BeginnerBuilding": false
},
{
"Name": "Charpentier",
"Price": {
"Wood": 2
},
"BeginnerBuilding": false
},
{
"Name": "Epicerie",
"Price": {
"Wood": 2
},
"BeginnerBuilding": true
},
{
"Name": "Entrepot",
"Price": {
"Rock": 4
},
"BeginnerBuilding": true
},
{
"Name": "Eglise",
"Price": {
"Rock": 4
},
"BeginnerBuilding": true
},
{
"Name": "Foire",
"Price": {
"Wood": 4
},
"BeginnerBuilding": false
},
{
"Name": "Fontaine",
"Price": {
"Rock": 2
},
"BeginnerBuilding": false
},
{
"Name": "Grenier",
"Price": {
"Wood": 4
},
"BeginnerBuilding": true
},
{
"Name": "Librairie",
"Price": {
"Rock": 4
},
"BeginnerBuilding": false
},
{
"Name": "minedor",
"Price": {
"Wood": 1,
"Rock": 1
},
"BeginnerBuilding": true
},
{
"Name": "poissonnier",
"Price": {
"Wood": 1,
"Rock": 1
},
"BeginnerBuilding": false
},
{
"Name": "Ponton",
"Price": {
"Wood": 3
},
"BeginnerBuilding": false
},
{
"Name": "Preteursurgage",
"Price": {
"Wood": 3
},
"BeginnerBuilding": true
},
{
"Name": "puits",
"Price": {
"Wood": 1,
"Rock": 1
},
"BeginnerBuilding": true
},
{
"Name": "Restaurant",
"Price": {
"Wood": 2,
"Rock": 2
},
"BeginnerBuilding": false
},
{
"Name": "Statue",
"Price": {
"Rock": 4
},
"BeginnerBuilding": true
},
{
"Name": "Cathedrale",
"Price": {
"Rock": 6
},
"BeginnerBuilding": false
},
{
"Name": "Residence",
"Price": {
"Piece": 6
},
"BeginnerBuilding": false
},
{
"Name": "Chateau",
"Price": {
"Rock": 6
},
"BeginnerBuilding": true
},
{
"Name": "Tourdegarde",
"Price": {
"Wood": 3,
"Rock": 3
},
"BeginnerBuilding": false
}
]

View File

@@ -0,0 +1,92 @@
[
{
"Description": "desc",
"Formula": "formula",
"Points": 2
},
{
"Description": "desc",
"Formula": "formula",
"Points": 2
},
{
"Description": "desc",
"Formula": "formula",
"Points": 2
},
{
"Description": "desc",
"Formula": "formula",
"Points": 2
},
{
"Description": "desc",
"Formula": "formula",
"Points": 2
},
{
"Description": "desc",
"Formula": "formula",
"Points": 2
},
{
"Description": "desc",
"Formula": "formula",
"Points": 2
},
{
"Description": "desc",
"Formula": "formula",
"Points": 2
},
{
"Description": "desc",
"Formula": "formula",
"Points": 2
},
{
"Description": "desc",
"Formula": "formula",
"Points": 2
},
{
"Description": "desc",
"Formula": "formula",
"Points": 2
},
{
"Description": "desc",
"Formula": "formula",
"Points": 3
},
{
"Description": "desc",
"Formula": "formula",
"Points": 3
},
{
"Description": "desc",
"Formula": "formula",
"Points": 3
},
{
"Description": "desc",
"Formula": "formula",
"Points": 3
},
{
"Description": "desc",
"Formula": "formula",
"Points": 3
},
{
"Description": "desc",
"Formula": "formula",
"Points": 3
},
{
"Description": "desc",
"Formula": "formula",
"Points": 3
}
]

View File

@@ -0,0 +1,23 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<base href="/" />
<ResourcePreloader />
<link rel="stylesheet" href="@Assets["lib/bootstrap/dist/css/bootstrap.min.css"]" />
<link rel="stylesheet" href="@Assets["app.css"]" />
<link rel="stylesheet" href="@Assets["LittleTown.Blazor.styles.css"]" />
<ImportMap />
<link rel="icon" type="image/png" href="favicon.png" />
<HeadOutlet />
</head>
<body>
<Routes />
<ReconnectModal />
<script src="@Assets["_framework/blazor.web.js"]"></script>
</body>
</html>

View File

@@ -0,0 +1,17 @@
@inherits LayoutComponentBase
<div class="page">
<div class="sidebar">
<NavMenu />
</div>
<main>
@Body
</main>
</div>
<div id="blazor-error-ui" data-nosnippet>
An unhandled error has occurred.
<a href="." class="reload">Reload</a>
<span class="dismiss">🗙</span>
</div>

View File

@@ -0,0 +1,98 @@
.page {
position: relative;
display: flex;
flex-direction: column;
}
main {
flex: 1;
}
.sidebar {
background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%);
}
.top-row {
background-color: #f7f7f7;
border-bottom: 1px solid #d6d5d5;
justify-content: flex-end;
height: 3.5rem;
display: flex;
align-items: center;
}
.top-row ::deep a, .top-row ::deep .btn-link {
white-space: nowrap;
margin-left: 1.5rem;
text-decoration: none;
}
.top-row ::deep a:hover, .top-row ::deep .btn-link:hover {
text-decoration: underline;
}
.top-row ::deep a:first-child {
overflow: hidden;
text-overflow: ellipsis;
}
@media (max-width: 640.98px) {
.top-row {
justify-content: space-between;
}
.top-row ::deep a, .top-row ::deep .btn-link {
margin-left: 0;
}
}
@media (min-width: 641px) {
.page {
flex-direction: row;
}
.sidebar {
width: 250px;
height: 100vh;
position: sticky;
top: 0;
}
.top-row {
position: sticky;
top: 0;
z-index: 1;
}
.top-row.auth ::deep a:first-child {
flex: 1;
text-align: right;
width: 0;
}
.top-row, article {
padding-left: 2rem !important;
padding-right: 1.5rem !important;
}
}
#blazor-error-ui {
color-scheme: light only;
background: lightyellow;
bottom: 0;
box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
box-sizing: border-box;
display: none;
left: 0;
padding: 0.6rem 1.25rem 0.7rem 1.25rem;
position: fixed;
width: 100%;
z-index: 1000;
}
#blazor-error-ui .dismiss {
cursor: pointer;
position: absolute;
right: 0.75rem;
top: 0.5rem;
}

View File

@@ -0,0 +1,28 @@
<div class="top-row ps-3 navbar navbar-dark">
<div class="container-fluid">
<a class="navbar-brand" href="">LittleTown.Blazor</a>
</div>
</div>
<input type="checkbox" title="Navigation menu" class="navbar-toggler" />
<div class="nav-scrollable" onclick="document.querySelector('.navbar-toggler').click()">
<nav class="nav flex-column">
<div class="nav-item px-3">
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
<span class="bi bi-house-door-fill-nav-menu" aria-hidden="true"></span> Home
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="counter">
<span class="bi bi-plus-square-fill-nav-menu" aria-hidden="true"></span> Counter
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="match">
<span class="bi bi-plus-square-fill-nav-menu" aria-hidden="true"></span> Match
</NavLink>
</div>
</nav>
</div>

View File

@@ -0,0 +1,105 @@
.navbar-toggler {
appearance: none;
cursor: pointer;
width: 3.5rem;
height: 2.5rem;
color: white;
position: absolute;
top: 0.5rem;
right: 1rem;
border: 1px solid rgba(255, 255, 255, 0.1);
background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e") no-repeat center/1.75rem rgba(255, 255, 255, 0.1);
}
.navbar-toggler:checked {
background-color: rgba(255, 255, 255, 0.5);
}
.top-row {
min-height: 3.5rem;
background-color: rgba(0,0,0,0.4);
}
.navbar-brand {
font-size: 1.1rem;
}
.bi {
display: inline-block;
position: relative;
width: 1.25rem;
height: 1.25rem;
margin-right: 0.75rem;
top: -1px;
background-size: cover;
}
.bi-house-door-fill-nav-menu {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-house-door-fill' viewBox='0 0 16 16'%3E%3Cpath d='M6.5 14.5v-3.505c0-.245.25-.495.5-.495h2c.25 0 .5.25.5.5v3.5a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5Z'/%3E%3C/svg%3E");
}
.bi-plus-square-fill-nav-menu {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-plus-square-fill' viewBox='0 0 16 16'%3E%3Cpath d='M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm6.5 4.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3a.5.5 0 0 1 1 0z'/%3E%3C/svg%3E");
}
.bi-list-nested-nav-menu {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-list-nested' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M4.5 11.5A.5.5 0 0 1 5 11h10a.5.5 0 0 1 0 1H5a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 3 7h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 1 3h10a.5.5 0 0 1 0 1H1a.5.5 0 0 1-.5-.5z'/%3E%3C/svg%3E");
}
.nav-item {
font-size: 0.9rem;
padding-bottom: 0.5rem;
}
.nav-item:first-of-type {
padding-top: 1rem;
}
.nav-item:last-of-type {
padding-bottom: 1rem;
}
.nav-item ::deep .nav-link {
color: #d7d7d7;
background: none;
border: none;
border-radius: 4px;
height: 3rem;
display: flex;
align-items: center;
line-height: 3rem;
width: 100%;
}
.nav-item ::deep a.active {
background-color: rgba(255,255,255,0.37);
color: white;
}
.nav-item ::deep .nav-link:hover {
background-color: rgba(255,255,255,0.1);
color: white;
}
.nav-scrollable {
display: none;
}
.navbar-toggler:checked ~ .nav-scrollable {
display: block;
}
@media (min-width: 641px) {
.navbar-toggler {
display: none;
}
.nav-scrollable {
/* Never collapse the sidebar for wide screens */
display: block;
/* Allow sidebar to scroll for tall menus */
height: calc(100vh - 3.5rem);
overflow-y: auto;
}
}

View File

@@ -0,0 +1,31 @@
<script type="module" src="@Assets["Components/Layout/ReconnectModal.razor.js"]"></script>
<dialog id="components-reconnect-modal" data-nosnippet>
<div class="components-reconnect-container">
<div class="components-rejoining-animation" aria-hidden="true">
<div></div>
<div></div>
</div>
<p class="components-reconnect-first-attempt-visible">
Rejoining the server...
</p>
<p class="components-reconnect-repeated-attempt-visible">
Rejoin failed... trying again in <span id="components-seconds-to-next-attempt"></span> seconds.
</p>
<p class="components-reconnect-failed-visible">
Failed to rejoin.<br />Please retry or reload the page.
</p>
<button id="components-reconnect-button" class="components-reconnect-failed-visible">
Retry
</button>
<p class="components-pause-visible">
The session has been paused by the server.
</p>
<p class="components-resume-failed-visible">
Failed to resume the session.<br />Please retry or reload the page.
</p>
<button id="components-resume-button" class="components-pause-visible components-resume-failed-visible">
Resume
</button>
</div>
</dialog>

View File

@@ -0,0 +1,157 @@
.components-reconnect-first-attempt-visible,
.components-reconnect-repeated-attempt-visible,
.components-reconnect-failed-visible,
.components-pause-visible,
.components-resume-failed-visible,
.components-rejoining-animation {
display: none;
}
#components-reconnect-modal.components-reconnect-show .components-reconnect-first-attempt-visible,
#components-reconnect-modal.components-reconnect-show .components-rejoining-animation,
#components-reconnect-modal.components-reconnect-paused .components-pause-visible,
#components-reconnect-modal.components-reconnect-resume-failed .components-resume-failed-visible,
#components-reconnect-modal.components-reconnect-retrying,
#components-reconnect-modal.components-reconnect-retrying .components-reconnect-repeated-attempt-visible,
#components-reconnect-modal.components-reconnect-retrying .components-rejoining-animation,
#components-reconnect-modal.components-reconnect-failed,
#components-reconnect-modal.components-reconnect-failed .components-reconnect-failed-visible {
display: block;
}
#components-reconnect-modal {
background-color: white;
width: 20rem;
margin: 20vh auto;
padding: 2rem;
border: 0;
border-radius: 0.5rem;
box-shadow: 0 3px 6px 2px rgba(0, 0, 0, 0.3);
opacity: 0;
transition: display 0.5s allow-discrete, overlay 0.5s allow-discrete;
animation: components-reconnect-modal-fadeOutOpacity 0.5s both;
&[open]
{
animation: components-reconnect-modal-slideUp 1.5s cubic-bezier(.05, .89, .25, 1.02) 0.3s, components-reconnect-modal-fadeInOpacity 0.5s ease-in-out 0.3s;
animation-fill-mode: both;
}
}
#components-reconnect-modal::backdrop {
background-color: rgba(0, 0, 0, 0.4);
animation: components-reconnect-modal-fadeInOpacity 0.5s ease-in-out;
opacity: 1;
}
@keyframes components-reconnect-modal-slideUp {
0% {
transform: translateY(30px) scale(0.95);
}
100% {
transform: translateY(0);
}
}
@keyframes components-reconnect-modal-fadeInOpacity {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
@keyframes components-reconnect-modal-fadeOutOpacity {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}
.components-reconnect-container {
display: flex;
flex-direction: column;
align-items: center;
gap: 1rem;
}
#components-reconnect-modal p {
margin: 0;
text-align: center;
}
#components-reconnect-modal button {
border: 0;
background-color: #6b9ed2;
color: white;
padding: 4px 24px;
border-radius: 4px;
}
#components-reconnect-modal button:hover {
background-color: #3b6ea2;
}
#components-reconnect-modal button:active {
background-color: #6b9ed2;
}
.components-rejoining-animation {
position: relative;
width: 80px;
height: 80px;
}
.components-rejoining-animation div {
position: absolute;
border: 3px solid #0087ff;
opacity: 1;
border-radius: 50%;
animation: components-rejoining-animation 1.5s cubic-bezier(0, 0.2, 0.8, 1) infinite;
}
.components-rejoining-animation div:nth-child(2) {
animation-delay: -0.5s;
}
@keyframes components-rejoining-animation {
0% {
top: 40px;
left: 40px;
width: 0;
height: 0;
opacity: 0;
}
4.9% {
top: 40px;
left: 40px;
width: 0;
height: 0;
opacity: 0;
}
5% {
top: 40px;
left: 40px;
width: 0;
height: 0;
opacity: 1;
}
100% {
top: 0px;
left: 0px;
width: 80px;
height: 80px;
opacity: 0;
}
}

View File

@@ -0,0 +1,63 @@
// Set up event handlers
const reconnectModal = document.getElementById("components-reconnect-modal");
reconnectModal.addEventListener("components-reconnect-state-changed", handleReconnectStateChanged);
const retryButton = document.getElementById("components-reconnect-button");
retryButton.addEventListener("click", retry);
const resumeButton = document.getElementById("components-resume-button");
resumeButton.addEventListener("click", resume);
function handleReconnectStateChanged(event) {
if (event.detail.state === "show") {
reconnectModal.showModal();
} else if (event.detail.state === "hide") {
reconnectModal.close();
} else if (event.detail.state === "failed") {
document.addEventListener("visibilitychange", retryWhenDocumentBecomesVisible);
} else if (event.detail.state === "rejected") {
location.reload();
}
}
async function retry() {
document.removeEventListener("visibilitychange", retryWhenDocumentBecomesVisible);
try {
// Reconnect will asynchronously return:
// - true to mean success
// - false to mean we reached the server, but it rejected the connection (e.g., unknown circuit ID)
// - exception to mean we didn't reach the server (this can be sync or async)
const successful = await Blazor.reconnect();
if (!successful) {
// We have been able to reach the server, but the circuit is no longer available.
// We'll reload the page so the user can continue using the app as quickly as possible.
const resumeSuccessful = await Blazor.resumeCircuit();
if (!resumeSuccessful) {
location.reload();
} else {
reconnectModal.close();
}
}
} catch (err) {
// We got an exception, server is currently unavailable
document.addEventListener("visibilitychange", retryWhenDocumentBecomesVisible);
}
}
async function resume() {
try {
const successful = await Blazor.resumeCircuit();
if (!successful) {
location.reload();
}
} catch {
reconnectModal.classList.replace("components-reconnect-paused", "components-reconnect-resume-failed");
}
}
async function retryWhenDocumentBecomesVisible() {
if (document.visibilityState === "visible") {
await retry();
}
}

View File

@@ -0,0 +1,36 @@
@page "/Error"
@using System.Diagnostics
<PageTitle>Error</PageTitle>
<h1 class="text-danger">Error.</h1>
<h2 class="text-danger">An error occurred while processing your request.</h2>
@if (ShowRequestId)
{
<p>
<strong>Request ID:</strong> <code>@RequestId</code>
</p>
}
<h3>Development Mode</h3>
<p>
Swapping to <strong>Development</strong> environment will display more detailed information about the error that occurred.
</p>
<p>
<strong>The Development environment shouldn't be enabled for deployed applications.</strong>
It can result in displaying sensitive information from exceptions to end users.
For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
and restarting the app.
</p>
@code{
[CascadingParameter]
private HttpContext? HttpContext { get; set; }
private string? RequestId { get; set; }
private bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
protected override void OnInitialized() =>
RequestId = Activity.Current?.Id ?? HttpContext?.TraceIdentifier;
}

View File

@@ -0,0 +1,7 @@
@page "/"
<PageTitle>Home</PageTitle>
<h1>Hello, world!</h1>
Welcome to your new app.

View File

@@ -0,0 +1,5 @@
@page "/not-found"
@layout MainLayout
<h3>Not Found</h3>
<p>Sorry, the content you are looking for does not exist.</p>

View File

@@ -0,0 +1,6 @@
<Router AppAssembly="typeof(Program).Assembly" AdditionalAssemblies="new[] { typeof(Client._Imports).Assembly }" NotFoundPage="typeof(Pages.NotFound)">
<Found Context="routeData">
<RouteView RouteData="routeData" DefaultLayout="typeof(Layout.MainLayout)" />
<FocusOnNavigate RouteData="routeData" Selector="h1" />
</Found>
</Router>

View File

@@ -0,0 +1,12 @@
@using System.Net.Http
@using System.Net.Http.Json
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using static Microsoft.AspNetCore.Components.Web.RenderMode
@using Microsoft.AspNetCore.Components.Web.Virtualization
@using Microsoft.JSInterop
@using LittleTown.Blazor
@using LittleTown.Blazor.Client
@using LittleTown.Blazor.Components
@using LittleTown.Blazor.Components.Layout

View File

@@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<BlazorDisableThrowNavigationException>true</BlazorDisableThrowNavigationException>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\LittleTown.Blazor.Client\LittleTown.Blazor.Client.csproj" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="10.0.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\LittleTown.Core\LittleTown.Core.csproj" />
<ProjectReference Include="..\..\LittleTown.StaticDataAccess\LittleTown.StaticDataAccess.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ActiveDebugProfile>https</ActiveDebugProfile>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,40 @@
using LittleTown.Blazor.Client.Services;
using LittleTown.Blazor.Components;
using LittleTown.Core.Ports;
using LittleTown.StaticDataAcces;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents()
.AddInteractiveWebAssemblyComponents();
builder.Services.AddScoped<IStaticDataGetter, StaticDataGetter>();
builder.Services.AddScoped<IMatchService, MatchService>();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseWebAssemblyDebugging();
}
else
{
app.UseExceptionHandler("/Error", createScopeForErrors: true);
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseStatusCodePagesWithReExecute("/not-found", createScopeForStatusCodePages: true);
app.UseHttpsRedirection();
app.UseAntiforgery();
app.MapStaticAssets();
app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode()
.AddInteractiveWebAssemblyRenderMode()
.AddAdditionalAssemblies(typeof(LittleTown.Blazor.Client._Imports).Assembly);
await app.RunAsync();

View File

@@ -0,0 +1,25 @@
{
"$schema": "https://json.schemastore.org/launchsettings.json",
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
"applicationUrl": "http://localhost:5298",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
"applicationUrl": "https://localhost:7058;http://localhost:5298",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

View File

@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}

View File

@@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}

View File

@@ -0,0 +1,60 @@
html, body {
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
}
a, .btn-link {
color: #006bb7;
}
.btn-primary {
color: #fff;
background-color: #1b6ec2;
border-color: #1861ac;
}
.btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus {
box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb;
}
.content {
padding-top: 1.1rem;
}
h1:focus {
outline: none;
}
.valid.modified:not([type=checkbox]) {
outline: 1px solid #26b050;
}
.invalid {
outline: 1px solid #e50000;
}
.validation-message {
color: #e50000;
}
.blazor-error-boundary {
background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
padding: 1rem 1rem 1rem 3.7rem;
color: white;
}
.blazor-error-boundary::after {
content: "An error has occurred."
}
.darker-border-checkbox.form-check-input {
border-color: #929292;
}
.form-floating > .form-control-plaintext::placeholder, .form-floating > .form-control::placeholder {
color: var(--bs-secondary-color);
text-align: end;
}
.form-floating > .form-control-plaintext:focus::placeholder, .form-floating > .form-control:focus::placeholder {
text-align: start;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,597 @@
/*!
* Bootstrap Reboot v5.3.3 (https://getbootstrap.com/)
* Copyright 2011-2024 The Bootstrap Authors
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
:root,
[data-bs-theme=light] {
--bs-blue: #0d6efd;
--bs-indigo: #6610f2;
--bs-purple: #6f42c1;
--bs-pink: #d63384;
--bs-red: #dc3545;
--bs-orange: #fd7e14;
--bs-yellow: #ffc107;
--bs-green: #198754;
--bs-teal: #20c997;
--bs-cyan: #0dcaf0;
--bs-black: #000;
--bs-white: #fff;
--bs-gray: #6c757d;
--bs-gray-dark: #343a40;
--bs-gray-100: #f8f9fa;
--bs-gray-200: #e9ecef;
--bs-gray-300: #dee2e6;
--bs-gray-400: #ced4da;
--bs-gray-500: #adb5bd;
--bs-gray-600: #6c757d;
--bs-gray-700: #495057;
--bs-gray-800: #343a40;
--bs-gray-900: #212529;
--bs-primary: #0d6efd;
--bs-secondary: #6c757d;
--bs-success: #198754;
--bs-info: #0dcaf0;
--bs-warning: #ffc107;
--bs-danger: #dc3545;
--bs-light: #f8f9fa;
--bs-dark: #212529;
--bs-primary-rgb: 13, 110, 253;
--bs-secondary-rgb: 108, 117, 125;
--bs-success-rgb: 25, 135, 84;
--bs-info-rgb: 13, 202, 240;
--bs-warning-rgb: 255, 193, 7;
--bs-danger-rgb: 220, 53, 69;
--bs-light-rgb: 248, 249, 250;
--bs-dark-rgb: 33, 37, 41;
--bs-primary-text-emphasis: #052c65;
--bs-secondary-text-emphasis: #2b2f32;
--bs-success-text-emphasis: #0a3622;
--bs-info-text-emphasis: #055160;
--bs-warning-text-emphasis: #664d03;
--bs-danger-text-emphasis: #58151c;
--bs-light-text-emphasis: #495057;
--bs-dark-text-emphasis: #495057;
--bs-primary-bg-subtle: #cfe2ff;
--bs-secondary-bg-subtle: #e2e3e5;
--bs-success-bg-subtle: #d1e7dd;
--bs-info-bg-subtle: #cff4fc;
--bs-warning-bg-subtle: #fff3cd;
--bs-danger-bg-subtle: #f8d7da;
--bs-light-bg-subtle: #fcfcfd;
--bs-dark-bg-subtle: #ced4da;
--bs-primary-border-subtle: #9ec5fe;
--bs-secondary-border-subtle: #c4c8cb;
--bs-success-border-subtle: #a3cfbb;
--bs-info-border-subtle: #9eeaf9;
--bs-warning-border-subtle: #ffe69c;
--bs-danger-border-subtle: #f1aeb5;
--bs-light-border-subtle: #e9ecef;
--bs-dark-border-subtle: #adb5bd;
--bs-white-rgb: 255, 255, 255;
--bs-black-rgb: 0, 0, 0;
--bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", "Liberation Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
--bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
--bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));
--bs-body-font-family: var(--bs-font-sans-serif);
--bs-body-font-size: 1rem;
--bs-body-font-weight: 400;
--bs-body-line-height: 1.5;
--bs-body-color: #212529;
--bs-body-color-rgb: 33, 37, 41;
--bs-body-bg: #fff;
--bs-body-bg-rgb: 255, 255, 255;
--bs-emphasis-color: #000;
--bs-emphasis-color-rgb: 0, 0, 0;
--bs-secondary-color: rgba(33, 37, 41, 0.75);
--bs-secondary-color-rgb: 33, 37, 41;
--bs-secondary-bg: #e9ecef;
--bs-secondary-bg-rgb: 233, 236, 239;
--bs-tertiary-color: rgba(33, 37, 41, 0.5);
--bs-tertiary-color-rgb: 33, 37, 41;
--bs-tertiary-bg: #f8f9fa;
--bs-tertiary-bg-rgb: 248, 249, 250;
--bs-heading-color: inherit;
--bs-link-color: #0d6efd;
--bs-link-color-rgb: 13, 110, 253;
--bs-link-decoration: underline;
--bs-link-hover-color: #0a58ca;
--bs-link-hover-color-rgb: 10, 88, 202;
--bs-code-color: #d63384;
--bs-highlight-color: #212529;
--bs-highlight-bg: #fff3cd;
--bs-border-width: 1px;
--bs-border-style: solid;
--bs-border-color: #dee2e6;
--bs-border-color-translucent: rgba(0, 0, 0, 0.175);
--bs-border-radius: 0.375rem;
--bs-border-radius-sm: 0.25rem;
--bs-border-radius-lg: 0.5rem;
--bs-border-radius-xl: 1rem;
--bs-border-radius-xxl: 2rem;
--bs-border-radius-2xl: var(--bs-border-radius-xxl);
--bs-border-radius-pill: 50rem;
--bs-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
--bs-box-shadow-sm: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
--bs-box-shadow-lg: 0 1rem 3rem rgba(0, 0, 0, 0.175);
--bs-box-shadow-inset: inset 0 1px 2px rgba(0, 0, 0, 0.075);
--bs-focus-ring-width: 0.25rem;
--bs-focus-ring-opacity: 0.25;
--bs-focus-ring-color: rgba(13, 110, 253, 0.25);
--bs-form-valid-color: #198754;
--bs-form-valid-border-color: #198754;
--bs-form-invalid-color: #dc3545;
--bs-form-invalid-border-color: #dc3545;
}
[data-bs-theme=dark] {
color-scheme: dark;
--bs-body-color: #dee2e6;
--bs-body-color-rgb: 222, 226, 230;
--bs-body-bg: #212529;
--bs-body-bg-rgb: 33, 37, 41;
--bs-emphasis-color: #fff;
--bs-emphasis-color-rgb: 255, 255, 255;
--bs-secondary-color: rgba(222, 226, 230, 0.75);
--bs-secondary-color-rgb: 222, 226, 230;
--bs-secondary-bg: #343a40;
--bs-secondary-bg-rgb: 52, 58, 64;
--bs-tertiary-color: rgba(222, 226, 230, 0.5);
--bs-tertiary-color-rgb: 222, 226, 230;
--bs-tertiary-bg: #2b3035;
--bs-tertiary-bg-rgb: 43, 48, 53;
--bs-primary-text-emphasis: #6ea8fe;
--bs-secondary-text-emphasis: #a7acb1;
--bs-success-text-emphasis: #75b798;
--bs-info-text-emphasis: #6edff6;
--bs-warning-text-emphasis: #ffda6a;
--bs-danger-text-emphasis: #ea868f;
--bs-light-text-emphasis: #f8f9fa;
--bs-dark-text-emphasis: #dee2e6;
--bs-primary-bg-subtle: #031633;
--bs-secondary-bg-subtle: #161719;
--bs-success-bg-subtle: #051b11;
--bs-info-bg-subtle: #032830;
--bs-warning-bg-subtle: #332701;
--bs-danger-bg-subtle: #2c0b0e;
--bs-light-bg-subtle: #343a40;
--bs-dark-bg-subtle: #1a1d20;
--bs-primary-border-subtle: #084298;
--bs-secondary-border-subtle: #41464b;
--bs-success-border-subtle: #0f5132;
--bs-info-border-subtle: #087990;
--bs-warning-border-subtle: #997404;
--bs-danger-border-subtle: #842029;
--bs-light-border-subtle: #495057;
--bs-dark-border-subtle: #343a40;
--bs-heading-color: inherit;
--bs-link-color: #6ea8fe;
--bs-link-hover-color: #8bb9fe;
--bs-link-color-rgb: 110, 168, 254;
--bs-link-hover-color-rgb: 139, 185, 254;
--bs-code-color: #e685b5;
--bs-highlight-color: #dee2e6;
--bs-highlight-bg: #664d03;
--bs-border-color: #495057;
--bs-border-color-translucent: rgba(255, 255, 255, 0.15);
--bs-form-valid-color: #75b798;
--bs-form-valid-border-color: #75b798;
--bs-form-invalid-color: #ea868f;
--bs-form-invalid-border-color: #ea868f;
}
*,
*::before,
*::after {
box-sizing: border-box;
}
@media (prefers-reduced-motion: no-preference) {
:root {
scroll-behavior: smooth;
}
}
body {
margin: 0;
font-family: var(--bs-body-font-family);
font-size: var(--bs-body-font-size);
font-weight: var(--bs-body-font-weight);
line-height: var(--bs-body-line-height);
color: var(--bs-body-color);
text-align: var(--bs-body-text-align);
background-color: var(--bs-body-bg);
-webkit-text-size-adjust: 100%;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
hr {
margin: 1rem 0;
color: inherit;
border: 0;
border-top: var(--bs-border-width) solid;
opacity: 0.25;
}
h6, h5, h4, h3, h2, h1 {
margin-top: 0;
margin-bottom: 0.5rem;
font-weight: 500;
line-height: 1.2;
color: var(--bs-heading-color);
}
h1 {
font-size: calc(1.375rem + 1.5vw);
}
@media (min-width: 1200px) {
h1 {
font-size: 2.5rem;
}
}
h2 {
font-size: calc(1.325rem + 0.9vw);
}
@media (min-width: 1200px) {
h2 {
font-size: 2rem;
}
}
h3 {
font-size: calc(1.3rem + 0.6vw);
}
@media (min-width: 1200px) {
h3 {
font-size: 1.75rem;
}
}
h4 {
font-size: calc(1.275rem + 0.3vw);
}
@media (min-width: 1200px) {
h4 {
font-size: 1.5rem;
}
}
h5 {
font-size: 1.25rem;
}
h6 {
font-size: 1rem;
}
p {
margin-top: 0;
margin-bottom: 1rem;
}
abbr[title] {
-webkit-text-decoration: underline dotted;
text-decoration: underline dotted;
cursor: help;
-webkit-text-decoration-skip-ink: none;
text-decoration-skip-ink: none;
}
address {
margin-bottom: 1rem;
font-style: normal;
line-height: inherit;
}
ol,
ul {
padding-left: 2rem;
}
ol,
ul,
dl {
margin-top: 0;
margin-bottom: 1rem;
}
ol ol,
ul ul,
ol ul,
ul ol {
margin-bottom: 0;
}
dt {
font-weight: 700;
}
dd {
margin-bottom: 0.5rem;
margin-left: 0;
}
blockquote {
margin: 0 0 1rem;
}
b,
strong {
font-weight: bolder;
}
small {
font-size: 0.875em;
}
mark {
padding: 0.1875em;
color: var(--bs-highlight-color);
background-color: var(--bs-highlight-bg);
}
sub,
sup {
position: relative;
font-size: 0.75em;
line-height: 0;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
a {
color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 1));
text-decoration: underline;
}
a:hover {
--bs-link-color-rgb: var(--bs-link-hover-color-rgb);
}
a:not([href]):not([class]), a:not([href]):not([class]):hover {
color: inherit;
text-decoration: none;
}
pre,
code,
kbd,
samp {
font-family: var(--bs-font-monospace);
font-size: 1em;
}
pre {
display: block;
margin-top: 0;
margin-bottom: 1rem;
overflow: auto;
font-size: 0.875em;
}
pre code {
font-size: inherit;
color: inherit;
word-break: normal;
}
code {
font-size: 0.875em;
color: var(--bs-code-color);
word-wrap: break-word;
}
a > code {
color: inherit;
}
kbd {
padding: 0.1875rem 0.375rem;
font-size: 0.875em;
color: var(--bs-body-bg);
background-color: var(--bs-body-color);
border-radius: 0.25rem;
}
kbd kbd {
padding: 0;
font-size: 1em;
}
figure {
margin: 0 0 1rem;
}
img,
svg {
vertical-align: middle;
}
table {
caption-side: bottom;
border-collapse: collapse;
}
caption {
padding-top: 0.5rem;
padding-bottom: 0.5rem;
color: var(--bs-secondary-color);
text-align: left;
}
th {
text-align: inherit;
text-align: -webkit-match-parent;
}
thead,
tbody,
tfoot,
tr,
td,
th {
border-color: inherit;
border-style: solid;
border-width: 0;
}
label {
display: inline-block;
}
button {
border-radius: 0;
}
button:focus:not(:focus-visible) {
outline: 0;
}
input,
button,
select,
optgroup,
textarea {
margin: 0;
font-family: inherit;
font-size: inherit;
line-height: inherit;
}
button,
select {
text-transform: none;
}
[role=button] {
cursor: pointer;
}
select {
word-wrap: normal;
}
select:disabled {
opacity: 1;
}
[list]:not([type=date]):not([type=datetime-local]):not([type=month]):not([type=week]):not([type=time])::-webkit-calendar-picker-indicator {
display: none !important;
}
button,
[type=button],
[type=reset],
[type=submit] {
-webkit-appearance: button;
}
button:not(:disabled),
[type=button]:not(:disabled),
[type=reset]:not(:disabled),
[type=submit]:not(:disabled) {
cursor: pointer;
}
::-moz-focus-inner {
padding: 0;
border-style: none;
}
textarea {
resize: vertical;
}
fieldset {
min-width: 0;
padding: 0;
margin: 0;
border: 0;
}
legend {
float: left;
width: 100%;
padding: 0;
margin-bottom: 0.5rem;
font-size: calc(1.275rem + 0.3vw);
line-height: inherit;
}
@media (min-width: 1200px) {
legend {
font-size: 1.5rem;
}
}
legend + * {
clear: left;
}
::-webkit-datetime-edit-fields-wrapper,
::-webkit-datetime-edit-text,
::-webkit-datetime-edit-minute,
::-webkit-datetime-edit-hour-field,
::-webkit-datetime-edit-day-field,
::-webkit-datetime-edit-month-field,
::-webkit-datetime-edit-year-field {
padding: 0;
}
::-webkit-inner-spin-button {
height: auto;
}
[type=search] {
-webkit-appearance: textfield;
outline-offset: -2px;
}
/* rtl:raw:
[type="tel"],
[type="url"],
[type="email"],
[type="number"] {
direction: ltr;
}
*/
::-webkit-search-decoration {
-webkit-appearance: none;
}
::-webkit-color-swatch-wrapper {
padding: 0;
}
::-webkit-file-upload-button {
font: inherit;
-webkit-appearance: button;
}
::file-selector-button {
font: inherit;
-webkit-appearance: button;
}
output {
display: inline-block;
}
iframe {
border: 0;
}
summary {
display: list-item;
cursor: pointer;
}
progress {
vertical-align: baseline;
}
[hidden] {
display: none !important;
}
/*# sourceMappingURL=bootstrap-reboot.css.map */

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,594 @@
/*!
* Bootstrap Reboot v5.3.3 (https://getbootstrap.com/)
* Copyright 2011-2024 The Bootstrap Authors
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
:root,
[data-bs-theme=light] {
--bs-blue: #0d6efd;
--bs-indigo: #6610f2;
--bs-purple: #6f42c1;
--bs-pink: #d63384;
--bs-red: #dc3545;
--bs-orange: #fd7e14;
--bs-yellow: #ffc107;
--bs-green: #198754;
--bs-teal: #20c997;
--bs-cyan: #0dcaf0;
--bs-black: #000;
--bs-white: #fff;
--bs-gray: #6c757d;
--bs-gray-dark: #343a40;
--bs-gray-100: #f8f9fa;
--bs-gray-200: #e9ecef;
--bs-gray-300: #dee2e6;
--bs-gray-400: #ced4da;
--bs-gray-500: #adb5bd;
--bs-gray-600: #6c757d;
--bs-gray-700: #495057;
--bs-gray-800: #343a40;
--bs-gray-900: #212529;
--bs-primary: #0d6efd;
--bs-secondary: #6c757d;
--bs-success: #198754;
--bs-info: #0dcaf0;
--bs-warning: #ffc107;
--bs-danger: #dc3545;
--bs-light: #f8f9fa;
--bs-dark: #212529;
--bs-primary-rgb: 13, 110, 253;
--bs-secondary-rgb: 108, 117, 125;
--bs-success-rgb: 25, 135, 84;
--bs-info-rgb: 13, 202, 240;
--bs-warning-rgb: 255, 193, 7;
--bs-danger-rgb: 220, 53, 69;
--bs-light-rgb: 248, 249, 250;
--bs-dark-rgb: 33, 37, 41;
--bs-primary-text-emphasis: #052c65;
--bs-secondary-text-emphasis: #2b2f32;
--bs-success-text-emphasis: #0a3622;
--bs-info-text-emphasis: #055160;
--bs-warning-text-emphasis: #664d03;
--bs-danger-text-emphasis: #58151c;
--bs-light-text-emphasis: #495057;
--bs-dark-text-emphasis: #495057;
--bs-primary-bg-subtle: #cfe2ff;
--bs-secondary-bg-subtle: #e2e3e5;
--bs-success-bg-subtle: #d1e7dd;
--bs-info-bg-subtle: #cff4fc;
--bs-warning-bg-subtle: #fff3cd;
--bs-danger-bg-subtle: #f8d7da;
--bs-light-bg-subtle: #fcfcfd;
--bs-dark-bg-subtle: #ced4da;
--bs-primary-border-subtle: #9ec5fe;
--bs-secondary-border-subtle: #c4c8cb;
--bs-success-border-subtle: #a3cfbb;
--bs-info-border-subtle: #9eeaf9;
--bs-warning-border-subtle: #ffe69c;
--bs-danger-border-subtle: #f1aeb5;
--bs-light-border-subtle: #e9ecef;
--bs-dark-border-subtle: #adb5bd;
--bs-white-rgb: 255, 255, 255;
--bs-black-rgb: 0, 0, 0;
--bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", "Liberation Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
--bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
--bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));
--bs-body-font-family: var(--bs-font-sans-serif);
--bs-body-font-size: 1rem;
--bs-body-font-weight: 400;
--bs-body-line-height: 1.5;
--bs-body-color: #212529;
--bs-body-color-rgb: 33, 37, 41;
--bs-body-bg: #fff;
--bs-body-bg-rgb: 255, 255, 255;
--bs-emphasis-color: #000;
--bs-emphasis-color-rgb: 0, 0, 0;
--bs-secondary-color: rgba(33, 37, 41, 0.75);
--bs-secondary-color-rgb: 33, 37, 41;
--bs-secondary-bg: #e9ecef;
--bs-secondary-bg-rgb: 233, 236, 239;
--bs-tertiary-color: rgba(33, 37, 41, 0.5);
--bs-tertiary-color-rgb: 33, 37, 41;
--bs-tertiary-bg: #f8f9fa;
--bs-tertiary-bg-rgb: 248, 249, 250;
--bs-heading-color: inherit;
--bs-link-color: #0d6efd;
--bs-link-color-rgb: 13, 110, 253;
--bs-link-decoration: underline;
--bs-link-hover-color: #0a58ca;
--bs-link-hover-color-rgb: 10, 88, 202;
--bs-code-color: #d63384;
--bs-highlight-color: #212529;
--bs-highlight-bg: #fff3cd;
--bs-border-width: 1px;
--bs-border-style: solid;
--bs-border-color: #dee2e6;
--bs-border-color-translucent: rgba(0, 0, 0, 0.175);
--bs-border-radius: 0.375rem;
--bs-border-radius-sm: 0.25rem;
--bs-border-radius-lg: 0.5rem;
--bs-border-radius-xl: 1rem;
--bs-border-radius-xxl: 2rem;
--bs-border-radius-2xl: var(--bs-border-radius-xxl);
--bs-border-radius-pill: 50rem;
--bs-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
--bs-box-shadow-sm: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
--bs-box-shadow-lg: 0 1rem 3rem rgba(0, 0, 0, 0.175);
--bs-box-shadow-inset: inset 0 1px 2px rgba(0, 0, 0, 0.075);
--bs-focus-ring-width: 0.25rem;
--bs-focus-ring-opacity: 0.25;
--bs-focus-ring-color: rgba(13, 110, 253, 0.25);
--bs-form-valid-color: #198754;
--bs-form-valid-border-color: #198754;
--bs-form-invalid-color: #dc3545;
--bs-form-invalid-border-color: #dc3545;
}
[data-bs-theme=dark] {
color-scheme: dark;
--bs-body-color: #dee2e6;
--bs-body-color-rgb: 222, 226, 230;
--bs-body-bg: #212529;
--bs-body-bg-rgb: 33, 37, 41;
--bs-emphasis-color: #fff;
--bs-emphasis-color-rgb: 255, 255, 255;
--bs-secondary-color: rgba(222, 226, 230, 0.75);
--bs-secondary-color-rgb: 222, 226, 230;
--bs-secondary-bg: #343a40;
--bs-secondary-bg-rgb: 52, 58, 64;
--bs-tertiary-color: rgba(222, 226, 230, 0.5);
--bs-tertiary-color-rgb: 222, 226, 230;
--bs-tertiary-bg: #2b3035;
--bs-tertiary-bg-rgb: 43, 48, 53;
--bs-primary-text-emphasis: #6ea8fe;
--bs-secondary-text-emphasis: #a7acb1;
--bs-success-text-emphasis: #75b798;
--bs-info-text-emphasis: #6edff6;
--bs-warning-text-emphasis: #ffda6a;
--bs-danger-text-emphasis: #ea868f;
--bs-light-text-emphasis: #f8f9fa;
--bs-dark-text-emphasis: #dee2e6;
--bs-primary-bg-subtle: #031633;
--bs-secondary-bg-subtle: #161719;
--bs-success-bg-subtle: #051b11;
--bs-info-bg-subtle: #032830;
--bs-warning-bg-subtle: #332701;
--bs-danger-bg-subtle: #2c0b0e;
--bs-light-bg-subtle: #343a40;
--bs-dark-bg-subtle: #1a1d20;
--bs-primary-border-subtle: #084298;
--bs-secondary-border-subtle: #41464b;
--bs-success-border-subtle: #0f5132;
--bs-info-border-subtle: #087990;
--bs-warning-border-subtle: #997404;
--bs-danger-border-subtle: #842029;
--bs-light-border-subtle: #495057;
--bs-dark-border-subtle: #343a40;
--bs-heading-color: inherit;
--bs-link-color: #6ea8fe;
--bs-link-hover-color: #8bb9fe;
--bs-link-color-rgb: 110, 168, 254;
--bs-link-hover-color-rgb: 139, 185, 254;
--bs-code-color: #e685b5;
--bs-highlight-color: #dee2e6;
--bs-highlight-bg: #664d03;
--bs-border-color: #495057;
--bs-border-color-translucent: rgba(255, 255, 255, 0.15);
--bs-form-valid-color: #75b798;
--bs-form-valid-border-color: #75b798;
--bs-form-invalid-color: #ea868f;
--bs-form-invalid-border-color: #ea868f;
}
*,
*::before,
*::after {
box-sizing: border-box;
}
@media (prefers-reduced-motion: no-preference) {
:root {
scroll-behavior: smooth;
}
}
body {
margin: 0;
font-family: var(--bs-body-font-family);
font-size: var(--bs-body-font-size);
font-weight: var(--bs-body-font-weight);
line-height: var(--bs-body-line-height);
color: var(--bs-body-color);
text-align: var(--bs-body-text-align);
background-color: var(--bs-body-bg);
-webkit-text-size-adjust: 100%;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
hr {
margin: 1rem 0;
color: inherit;
border: 0;
border-top: var(--bs-border-width) solid;
opacity: 0.25;
}
h6, h5, h4, h3, h2, h1 {
margin-top: 0;
margin-bottom: 0.5rem;
font-weight: 500;
line-height: 1.2;
color: var(--bs-heading-color);
}
h1 {
font-size: calc(1.375rem + 1.5vw);
}
@media (min-width: 1200px) {
h1 {
font-size: 2.5rem;
}
}
h2 {
font-size: calc(1.325rem + 0.9vw);
}
@media (min-width: 1200px) {
h2 {
font-size: 2rem;
}
}
h3 {
font-size: calc(1.3rem + 0.6vw);
}
@media (min-width: 1200px) {
h3 {
font-size: 1.75rem;
}
}
h4 {
font-size: calc(1.275rem + 0.3vw);
}
@media (min-width: 1200px) {
h4 {
font-size: 1.5rem;
}
}
h5 {
font-size: 1.25rem;
}
h6 {
font-size: 1rem;
}
p {
margin-top: 0;
margin-bottom: 1rem;
}
abbr[title] {
-webkit-text-decoration: underline dotted;
text-decoration: underline dotted;
cursor: help;
-webkit-text-decoration-skip-ink: none;
text-decoration-skip-ink: none;
}
address {
margin-bottom: 1rem;
font-style: normal;
line-height: inherit;
}
ol,
ul {
padding-right: 2rem;
}
ol,
ul,
dl {
margin-top: 0;
margin-bottom: 1rem;
}
ol ol,
ul ul,
ol ul,
ul ol {
margin-bottom: 0;
}
dt {
font-weight: 700;
}
dd {
margin-bottom: 0.5rem;
margin-right: 0;
}
blockquote {
margin: 0 0 1rem;
}
b,
strong {
font-weight: bolder;
}
small {
font-size: 0.875em;
}
mark {
padding: 0.1875em;
color: var(--bs-highlight-color);
background-color: var(--bs-highlight-bg);
}
sub,
sup {
position: relative;
font-size: 0.75em;
line-height: 0;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
a {
color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 1));
text-decoration: underline;
}
a:hover {
--bs-link-color-rgb: var(--bs-link-hover-color-rgb);
}
a:not([href]):not([class]), a:not([href]):not([class]):hover {
color: inherit;
text-decoration: none;
}
pre,
code,
kbd,
samp {
font-family: var(--bs-font-monospace);
font-size: 1em;
}
pre {
display: block;
margin-top: 0;
margin-bottom: 1rem;
overflow: auto;
font-size: 0.875em;
}
pre code {
font-size: inherit;
color: inherit;
word-break: normal;
}
code {
font-size: 0.875em;
color: var(--bs-code-color);
word-wrap: break-word;
}
a > code {
color: inherit;
}
kbd {
padding: 0.1875rem 0.375rem;
font-size: 0.875em;
color: var(--bs-body-bg);
background-color: var(--bs-body-color);
border-radius: 0.25rem;
}
kbd kbd {
padding: 0;
font-size: 1em;
}
figure {
margin: 0 0 1rem;
}
img,
svg {
vertical-align: middle;
}
table {
caption-side: bottom;
border-collapse: collapse;
}
caption {
padding-top: 0.5rem;
padding-bottom: 0.5rem;
color: var(--bs-secondary-color);
text-align: right;
}
th {
text-align: inherit;
text-align: -webkit-match-parent;
}
thead,
tbody,
tfoot,
tr,
td,
th {
border-color: inherit;
border-style: solid;
border-width: 0;
}
label {
display: inline-block;
}
button {
border-radius: 0;
}
button:focus:not(:focus-visible) {
outline: 0;
}
input,
button,
select,
optgroup,
textarea {
margin: 0;
font-family: inherit;
font-size: inherit;
line-height: inherit;
}
button,
select {
text-transform: none;
}
[role=button] {
cursor: pointer;
}
select {
word-wrap: normal;
}
select:disabled {
opacity: 1;
}
[list]:not([type=date]):not([type=datetime-local]):not([type=month]):not([type=week]):not([type=time])::-webkit-calendar-picker-indicator {
display: none !important;
}
button,
[type=button],
[type=reset],
[type=submit] {
-webkit-appearance: button;
}
button:not(:disabled),
[type=button]:not(:disabled),
[type=reset]:not(:disabled),
[type=submit]:not(:disabled) {
cursor: pointer;
}
::-moz-focus-inner {
padding: 0;
border-style: none;
}
textarea {
resize: vertical;
}
fieldset {
min-width: 0;
padding: 0;
margin: 0;
border: 0;
}
legend {
float: right;
width: 100%;
padding: 0;
margin-bottom: 0.5rem;
font-size: calc(1.275rem + 0.3vw);
line-height: inherit;
}
@media (min-width: 1200px) {
legend {
font-size: 1.5rem;
}
}
legend + * {
clear: right;
}
::-webkit-datetime-edit-fields-wrapper,
::-webkit-datetime-edit-text,
::-webkit-datetime-edit-minute,
::-webkit-datetime-edit-hour-field,
::-webkit-datetime-edit-day-field,
::-webkit-datetime-edit-month-field,
::-webkit-datetime-edit-year-field {
padding: 0;
}
::-webkit-inner-spin-button {
height: auto;
}
[type=search] {
-webkit-appearance: textfield;
outline-offset: -2px;
}
[type="tel"],
[type="url"],
[type="email"],
[type="number"] {
direction: ltr;
}
::-webkit-search-decoration {
-webkit-appearance: none;
}
::-webkit-color-swatch-wrapper {
padding: 0;
}
::-webkit-file-upload-button {
font: inherit;
-webkit-appearance: button;
}
::file-selector-button {
font: inherit;
-webkit-appearance: button;
}
output {
display: inline-block;
}
iframe {
border: 0;
}
summary {
display: list-item;
cursor: pointer;
}
progress {
vertical-align: baseline;
}
[hidden] {
display: none !important;
}
/*# sourceMappingURL=bootstrap-reboot.rtl.css.map */

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -17,7 +17,7 @@ public class BoardTesting
[Fact]
public void BoardGetTile()
{
Assert.Equal(ResourceType.Fish, _board.GetTile(0, 3)?.ResourceType);
Assert.Equal(ResourceType.Fish, _board.GetTile(0, 3).ResourceType);
}
[Fact]

View File

@@ -11,11 +11,11 @@
<ItemGroup>
<PackageReference Include="AutoMapper" Version="16.0.0" />
<PackageReference Include="Microsoft.CodeCoverage" Version="17.14.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
<PackageReference Include="NUnit3TestAdapter" Version="4.6.0" />
<PackageReference Include="Microsoft.CodeCoverage" Version="18.0.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
<PackageReference Include="NUnit3TestAdapter" Version="6.1.0" />
<PackageReference Include="xunit" Version="2.9.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.5">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>

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