Vibe Google Analytics
This commit is contained in:
@@ -0,0 +1 @@
|
|||||||
|
[]
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
3.0
|
||||||
@@ -60,7 +60,7 @@ foreach (var file in mdFiles)
|
|||||||
ImmortalizeWhen = NullIfNa(StripWikiLinks(yaml.GetValueOrDefault("immortalizeWhen"))),
|
ImmortalizeWhen = NullIfNa(StripWikiLinks(yaml.GetValueOrDefault("immortalizeWhen"))),
|
||||||
ImageFile = imageFile,
|
ImageFile = imageFile,
|
||||||
Artist = yaml.GetValueOrDefault("artist"),
|
Artist = yaml.GetValueOrDefault("artist"),
|
||||||
Rarity = yaml.GetValueOrDefault("rarity"),
|
Rarity = yaml.GetValueOrDefault("rarity")
|
||||||
};
|
};
|
||||||
|
|
||||||
cards.Add(card);
|
cards.Add(card);
|
||||||
|
|||||||
@@ -8,8 +8,8 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Model\Model.csproj" />
|
<ProjectReference Include="..\Model\Model.csproj"/>
|
||||||
<ProjectReference Include="..\Shared\Shared.csproj" />
|
<ProjectReference Include="..\Shared\Shared.csproj"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -21,20 +21,20 @@ main {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.top-row ::deep a, .top-row ::deep .btn-link {
|
.top-row ::deep a, .top-row ::deep .btn-link {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
margin-left: 1.5rem;
|
margin-left: 1.5rem;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.top-row ::deep a:hover, .top-row ::deep .btn-link:hover {
|
.top-row ::deep a:hover, .top-row ::deep .btn-link:hover {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
.top-row ::deep a:first-child {
|
.top-row ::deep a:first-child {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 640.98px) {
|
@media (max-width: 640.98px) {
|
||||||
.top-row {
|
.top-row {
|
||||||
@@ -90,9 +90,9 @@ main {
|
|||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
#blazor-error-ui .dismiss {
|
#blazor-error-ui .dismiss {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 0.75rem;
|
right: 0.75rem;
|
||||||
top: 0.5rem;
|
top: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
.top-row {
|
.top-row {
|
||||||
min-height: 3.5rem;
|
min-height: 3.5rem;
|
||||||
background-color: rgba(0,0,0,0.4);
|
background-color: rgba(0, 0, 0, 0.4);
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar-brand {
|
.navbar-brand {
|
||||||
@@ -51,15 +51,15 @@
|
|||||||
padding-bottom: 0.5rem;
|
padding-bottom: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-item:first-of-type {
|
.nav-item:first-of-type {
|
||||||
padding-top: 1rem;
|
padding-top: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-item:last-of-type {
|
.nav-item:last-of-type {
|
||||||
padding-bottom: 1rem;
|
padding-bottom: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-item ::deep .nav-link {
|
.nav-item ::deep .nav-link {
|
||||||
color: #d7d7d7;
|
color: #d7d7d7;
|
||||||
background: none;
|
background: none;
|
||||||
border: none;
|
border: none;
|
||||||
@@ -69,15 +69,15 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
line-height: 3rem;
|
line-height: 3rem;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-item ::deep a.active {
|
.nav-item ::deep a.active {
|
||||||
background-color: rgba(255,255,255,0.37);
|
background-color: rgba(255, 255, 255, 0.37);
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-item ::deep .nav-link:hover {
|
.nav-item ::deep .nav-link:hover {
|
||||||
background-color: rgba(255,255,255,0.1);
|
background-color: rgba(255, 255, 255, 0.1);
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,12 +31,11 @@
|
|||||||
opacity: 0;
|
opacity: 0;
|
||||||
transition: display 0.5s allow-discrete, overlay 0.5s allow-discrete;
|
transition: display 0.5s allow-discrete, overlay 0.5s allow-discrete;
|
||||||
animation: components-reconnect-modal-fadeOutOpacity 0.5s both;
|
animation: components-reconnect-modal-fadeOutOpacity 0.5s both;
|
||||||
&[open]
|
|
||||||
|
|
||||||
{
|
&[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: 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;
|
animation-fill-mode: both;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,13 +95,13 @@
|
|||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#components-reconnect-modal button:hover {
|
#components-reconnect-modal button:hover {
|
||||||
background-color: #3b6ea2;
|
background-color: #3b6ea2;
|
||||||
}
|
}
|
||||||
|
|
||||||
#components-reconnect-modal button:active {
|
#components-reconnect-modal button:active {
|
||||||
background-color: #6b9ed2;
|
background-color: #6b9ed2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.components-rejoining-animation {
|
.components-rejoining-animation {
|
||||||
position: relative;
|
position: relative;
|
||||||
@@ -110,17 +109,17 @@
|
|||||||
height: 80px;
|
height: 80px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.components-rejoining-animation div {
|
.components-rejoining-animation div {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
border: 3px solid #0087ff;
|
border: 3px solid #0087ff;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
animation: components-rejoining-animation 1.5s cubic-bezier(0, 0.2, 0.8, 1) infinite;
|
animation: components-rejoining-animation 1.5s cubic-bezier(0, 0.2, 0.8, 1) infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
.components-rejoining-animation div:nth-child(2) {
|
.components-rejoining-animation div:nth-child(2) {
|
||||||
animation-delay: -0.5s;
|
animation-delay: -0.5s;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes components-rejoining-animation {
|
@keyframes components-rejoining-animation {
|
||||||
0% {
|
0% {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
|
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private int currentCount = 0;
|
private int currentCount;
|
||||||
|
|
||||||
private void IncrementCount()
|
private void IncrementCount()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -15,12 +15,14 @@
|
|||||||
|
|
||||||
<h3>Development Mode</h3>
|
<h3>Development Mode</h3>
|
||||||
<p>
|
<p>
|
||||||
Swapping to <strong>Development</strong> environment will display more detailed information about the error that occurred.
|
Swapping to <strong>Development</strong> environment will display more detailed information about the error that
|
||||||
|
occurred.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<strong>The Development environment shouldn't be enabled for deployed applications.</strong>
|
<strong>The Development environment shouldn't be enabled for deployed applications.</strong>
|
||||||
It can result in displaying sensitive information from exceptions to end users.
|
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>
|
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.
|
and restarting the app.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
@@ -30,7 +32,9 @@
|
|||||||
private string? RequestId { get; set; }
|
private string? RequestId { get; set; }
|
||||||
private bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
|
private bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
|
||||||
|
|
||||||
protected override void OnInitialized() =>
|
protected override void OnInitialized()
|
||||||
|
{
|
||||||
RequestId = Activity.Current?.Id ?? HttpContext?.TraceIdentifier;
|
RequestId = Activity.Current?.Id ?? HttpContext?.TraceIdentifier;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
@using Shared.Pages
|
@using Shared.Pages
|
||||||
<Router AppAssembly="typeof(Program).Assembly" AdditionalAssemblies="new[] { typeof(Home).Assembly }" NotFoundPage="typeof(Pages.NotFound)">
|
@using NotFound = Cloud.Components.Pages.NotFound
|
||||||
|
<Router AppAssembly="typeof(Program).Assembly" AdditionalAssemblies="new[] { typeof(Home).Assembly }"
|
||||||
|
NotFoundPage="typeof(NotFound)">
|
||||||
<Found Context="routeData">
|
<Found Context="routeData">
|
||||||
<RouteView RouteData="routeData" DefaultLayout="typeof(Layout.MainLayout)"/>
|
<RouteView RouteData="routeData" DefaultLayout="typeof(MainLayout)"/>
|
||||||
<FocusOnNavigate RouteData="routeData" Selector="h1"/>
|
<FocusOnNavigate RouteData="routeData" Selector="h1"/>
|
||||||
</Found>
|
</Found>
|
||||||
</Router>
|
</Router>
|
||||||
@@ -11,7 +11,7 @@ var app = builder.Build();
|
|||||||
// Configure the HTTP request pipeline.
|
// Configure the HTTP request pipeline.
|
||||||
if (!app.Environment.IsDevelopment())
|
if (!app.Environment.IsDevelopment())
|
||||||
{
|
{
|
||||||
app.UseExceptionHandler("/Error", createScopeForErrors: true);
|
app.UseExceptionHandler("/Error", true);
|
||||||
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
|
// 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.UseHsts();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,4 +20,4 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,9 +42,9 @@ h1:focus {
|
|||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
.blazor-error-boundary::after {
|
.blazor-error-boundary::after {
|
||||||
content: "An error has occurred."
|
content: "An error has occurred."
|
||||||
}
|
}
|
||||||
|
|
||||||
.darker-border-checkbox.form-check-input {
|
.darker-border-checkbox.form-check-input {
|
||||||
border-color: #929292;
|
border-color: #929292;
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -224,6 +224,7 @@ h6, h5, h4, h3, h2, h1 {
|
|||||||
h1 {
|
h1 {
|
||||||
font-size: calc(1.375rem + 1.5vw);
|
font-size: calc(1.375rem + 1.5vw);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 1200px) {
|
@media (min-width: 1200px) {
|
||||||
h1 {
|
h1 {
|
||||||
font-size: 2.5rem;
|
font-size: 2.5rem;
|
||||||
@@ -233,6 +234,7 @@ h1 {
|
|||||||
h2 {
|
h2 {
|
||||||
font-size: calc(1.325rem + 0.9vw);
|
font-size: calc(1.325rem + 0.9vw);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 1200px) {
|
@media (min-width: 1200px) {
|
||||||
h2 {
|
h2 {
|
||||||
font-size: 2rem;
|
font-size: 2rem;
|
||||||
@@ -242,6 +244,7 @@ h2 {
|
|||||||
h3 {
|
h3 {
|
||||||
font-size: calc(1.3rem + 0.6vw);
|
font-size: calc(1.3rem + 0.6vw);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 1200px) {
|
@media (min-width: 1200px) {
|
||||||
h3 {
|
h3 {
|
||||||
font-size: 1.75rem;
|
font-size: 1.75rem;
|
||||||
@@ -251,6 +254,7 @@ h3 {
|
|||||||
h4 {
|
h4 {
|
||||||
font-size: calc(1.275rem + 0.3vw);
|
font-size: calc(1.275rem + 0.3vw);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 1200px) {
|
@media (min-width: 1200px) {
|
||||||
h4 {
|
h4 {
|
||||||
font-size: 1.5rem;
|
font-size: 1.5rem;
|
||||||
@@ -351,6 +355,7 @@ a {
|
|||||||
color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 1));
|
color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 1));
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
a:hover {
|
a:hover {
|
||||||
--bs-link-color-rgb: var(--bs-link-hover-color-rgb);
|
--bs-link-color-rgb: var(--bs-link-hover-color-rgb);
|
||||||
}
|
}
|
||||||
@@ -375,6 +380,7 @@ pre {
|
|||||||
overflow: auto;
|
overflow: auto;
|
||||||
font-size: 0.875em;
|
font-size: 0.875em;
|
||||||
}
|
}
|
||||||
|
|
||||||
pre code {
|
pre code {
|
||||||
font-size: inherit;
|
font-size: inherit;
|
||||||
color: inherit;
|
color: inherit;
|
||||||
@@ -386,6 +392,7 @@ code {
|
|||||||
color: var(--bs-code-color);
|
color: var(--bs-code-color);
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
a > code {
|
a > code {
|
||||||
color: inherit;
|
color: inherit;
|
||||||
}
|
}
|
||||||
@@ -397,6 +404,7 @@ kbd {
|
|||||||
background-color: var(--bs-body-color);
|
background-color: var(--bs-body-color);
|
||||||
border-radius: 0.25rem;
|
border-radius: 0.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
kbd kbd {
|
kbd kbd {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
@@ -474,6 +482,7 @@ select {
|
|||||||
select {
|
select {
|
||||||
word-wrap: normal;
|
word-wrap: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
select:disabled {
|
select:disabled {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
@@ -488,6 +497,7 @@ button,
|
|||||||
[type=submit] {
|
[type=submit] {
|
||||||
-webkit-appearance: button;
|
-webkit-appearance: button;
|
||||||
}
|
}
|
||||||
|
|
||||||
button:not(:disabled),
|
button:not(:disabled),
|
||||||
[type=button]:not(:disabled),
|
[type=button]:not(:disabled),
|
||||||
[type=reset]:not(:disabled),
|
[type=reset]:not(:disabled),
|
||||||
@@ -519,11 +529,13 @@ legend {
|
|||||||
font-size: calc(1.275rem + 0.3vw);
|
font-size: calc(1.275rem + 0.3vw);
|
||||||
line-height: inherit;
|
line-height: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 1200px) {
|
@media (min-width: 1200px) {
|
||||||
legend {
|
legend {
|
||||||
font-size: 1.5rem;
|
font-size: 1.5rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
legend + * {
|
legend + * {
|
||||||
clear: left;
|
clear: left;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -224,6 +224,7 @@ h6, h5, h4, h3, h2, h1 {
|
|||||||
h1 {
|
h1 {
|
||||||
font-size: calc(1.375rem + 1.5vw);
|
font-size: calc(1.375rem + 1.5vw);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 1200px) {
|
@media (min-width: 1200px) {
|
||||||
h1 {
|
h1 {
|
||||||
font-size: 2.5rem;
|
font-size: 2.5rem;
|
||||||
@@ -233,6 +234,7 @@ h1 {
|
|||||||
h2 {
|
h2 {
|
||||||
font-size: calc(1.325rem + 0.9vw);
|
font-size: calc(1.325rem + 0.9vw);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 1200px) {
|
@media (min-width: 1200px) {
|
||||||
h2 {
|
h2 {
|
||||||
font-size: 2rem;
|
font-size: 2rem;
|
||||||
@@ -242,6 +244,7 @@ h2 {
|
|||||||
h3 {
|
h3 {
|
||||||
font-size: calc(1.3rem + 0.6vw);
|
font-size: calc(1.3rem + 0.6vw);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 1200px) {
|
@media (min-width: 1200px) {
|
||||||
h3 {
|
h3 {
|
||||||
font-size: 1.75rem;
|
font-size: 1.75rem;
|
||||||
@@ -251,6 +254,7 @@ h3 {
|
|||||||
h4 {
|
h4 {
|
||||||
font-size: calc(1.275rem + 0.3vw);
|
font-size: calc(1.275rem + 0.3vw);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 1200px) {
|
@media (min-width: 1200px) {
|
||||||
h4 {
|
h4 {
|
||||||
font-size: 1.5rem;
|
font-size: 1.5rem;
|
||||||
@@ -351,6 +355,7 @@ a {
|
|||||||
color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 1));
|
color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 1));
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
a:hover {
|
a:hover {
|
||||||
--bs-link-color-rgb: var(--bs-link-hover-color-rgb);
|
--bs-link-color-rgb: var(--bs-link-hover-color-rgb);
|
||||||
}
|
}
|
||||||
@@ -375,6 +380,7 @@ pre {
|
|||||||
overflow: auto;
|
overflow: auto;
|
||||||
font-size: 0.875em;
|
font-size: 0.875em;
|
||||||
}
|
}
|
||||||
|
|
||||||
pre code {
|
pre code {
|
||||||
font-size: inherit;
|
font-size: inherit;
|
||||||
color: inherit;
|
color: inherit;
|
||||||
@@ -386,6 +392,7 @@ code {
|
|||||||
color: var(--bs-code-color);
|
color: var(--bs-code-color);
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
a > code {
|
a > code {
|
||||||
color: inherit;
|
color: inherit;
|
||||||
}
|
}
|
||||||
@@ -397,6 +404,7 @@ kbd {
|
|||||||
background-color: var(--bs-body-color);
|
background-color: var(--bs-body-color);
|
||||||
border-radius: 0.25rem;
|
border-radius: 0.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
kbd kbd {
|
kbd kbd {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
@@ -474,6 +482,7 @@ select {
|
|||||||
select {
|
select {
|
||||||
word-wrap: normal;
|
word-wrap: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
select:disabled {
|
select:disabled {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
@@ -488,6 +497,7 @@ button,
|
|||||||
[type=submit] {
|
[type=submit] {
|
||||||
-webkit-appearance: button;
|
-webkit-appearance: button;
|
||||||
}
|
}
|
||||||
|
|
||||||
button:not(:disabled),
|
button:not(:disabled),
|
||||||
[type=button]:not(:disabled),
|
[type=button]:not(:disabled),
|
||||||
[type=reset]:not(:disabled),
|
[type=reset]:not(:disabled),
|
||||||
@@ -519,11 +529,13 @@ legend {
|
|||||||
font-size: calc(1.275rem + 0.3vw);
|
font-size: calc(1.275rem + 0.3vw);
|
||||||
line-height: inherit;
|
line-height: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 1200px) {
|
@media (min-width: 1200px) {
|
||||||
legend {
|
legend {
|
||||||
font-size: 1.5rem;
|
font-size: 1.5rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
legend + * {
|
legend + * {
|
||||||
clear: right;
|
clear: right;
|
||||||
}
|
}
|
||||||
@@ -553,6 +565,7 @@ legend + * {
|
|||||||
[type="number"] {
|
[type="number"] {
|
||||||
direction: ltr;
|
direction: ltr;
|
||||||
}
|
}
|
||||||
|
|
||||||
::-webkit-search-decoration {
|
::-webkit-search-decoration {
|
||||||
-webkit-appearance: none;
|
-webkit-appearance: none;
|
||||||
}
|
}
|
||||||
@@ -591,4 +604,5 @@ progress {
|
|||||||
[hidden] {
|
[hidden] {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*# sourceMappingURL=bootstrap-reboot.rtl.css.map */
|
/*# sourceMappingURL=bootstrap-reboot.rtl.css.map */
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+1677
-2
File diff suppressed because it is too large
Load Diff
+205
-9
File diff suppressed because it is too large
Load Diff
+195
-6
File diff suppressed because it is too large
Load Diff
+197
-7
File diff suppressed because it is too large
Load Diff
@@ -5,5 +5,5 @@ public class DocData
|
|||||||
public string Title { get; init; } = "";
|
public string Title { get; init; } = "";
|
||||||
public string Category { get; init; } = "";
|
public string Category { get; init; } = "";
|
||||||
public string Content { get; init; } = "";
|
public string Content { get; init; } = "";
|
||||||
public System.Collections.Generic.Dictionary<string, string> Frontmatter { get; init; } = [];
|
public Dictionary<string, string> Frontmatter { get; init; } = [];
|
||||||
}
|
}
|
||||||
@@ -20,8 +20,8 @@
|
|||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<Routes />
|
<Routes/>
|
||||||
<script src="_framework/blazor.web.js"></script>
|
<script src="_framework/blazor.web.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<Router AppAssembly="@typeof(Program).Assembly" AdditionalAssemblies="new[] { typeof(Shared.Pages.Home).Assembly }">
|
<Router AppAssembly="@typeof(Program).Assembly" AdditionalAssemblies="new[] { typeof(Home).Assembly }">
|
||||||
<Found Context="routeData">
|
<Found Context="routeData">
|
||||||
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)"/>
|
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)"/>
|
||||||
<FocusOnNavigate RouteData="@routeData" Selector="h1"/>
|
<FocusOnNavigate RouteData="@routeData" Selector="h1"/>
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Server;
|
using Server;
|
||||||
|
using Server.Components;
|
||||||
|
using Shared.Pages;
|
||||||
|
using Shared.Services;
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
@@ -7,7 +10,7 @@ builder.Services.AddControllers();
|
|||||||
builder.Services.AddEndpointsApiExplorer();
|
builder.Services.AddEndpointsApiExplorer();
|
||||||
builder.Services.AddRazorComponents().AddInteractiveServerComponents();
|
builder.Services.AddRazorComponents().AddInteractiveServerComponents();
|
||||||
builder.Services.AddTelerikBlazor();
|
builder.Services.AddTelerikBlazor();
|
||||||
builder.Services.AddSingleton<Shared.Services.CardRenderingService>();
|
builder.Services.AddSingleton<CardRenderingService>();
|
||||||
|
|
||||||
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
|
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
|
||||||
builder.Services.AddDbContext<AppDbContext>(options =>
|
builder.Services.AddDbContext<AppDbContext>(options =>
|
||||||
@@ -32,17 +35,14 @@ using (var scope = app.Services.CreateScope())
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!app.Environment.IsDevelopment())
|
if (!app.Environment.IsDevelopment()) app.UseExceptionHandler("/Error");
|
||||||
{
|
|
||||||
app.UseExceptionHandler("/Error");
|
|
||||||
}
|
|
||||||
|
|
||||||
app.UseStaticFiles();
|
app.UseStaticFiles();
|
||||||
app.UseAntiforgery();
|
app.UseAntiforgery();
|
||||||
|
|
||||||
app.MapControllers();
|
app.MapControllers();
|
||||||
app.MapRazorComponents<Server.Components.App>()
|
app.MapRazorComponents<App>()
|
||||||
.AddInteractiveServerRenderMode()
|
.AddInteractiveServerRenderMode()
|
||||||
.AddAdditionalAssemblies(typeof(Shared.Pages.Home).Assembly);
|
.AddAdditionalAssemblies(typeof(Home).Assembly);
|
||||||
|
|
||||||
app.Run();
|
app.Run();
|
||||||
@@ -257,9 +257,6 @@ a, .btn-link {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.inline-card-btn {
|
.inline-card-btn {
|
||||||
background: none;
|
background: none;
|
||||||
border: none;
|
border: none;
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
@namespace Shared.Components
|
@namespace Shared.Components
|
||||||
@using Chrono.Model
|
|
||||||
|
|
||||||
@if (Card != null)
|
@if (Card != null)
|
||||||
{
|
{
|
||||||
@@ -11,7 +10,7 @@
|
|||||||
{
|
{
|
||||||
<div class="detail-image">
|
<div class="detail-image">
|
||||||
<img src="@Card.ImagePath" alt="@Card.Name"
|
<img src="@Card.ImagePath" alt="@Card.Name"
|
||||||
onerror="this.style.display='none';this.parentElement.style.display='none'" />
|
onerror="this.style.display='none';this.parentElement.style.display='none'"/>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
<div class="detail-info">
|
<div class="detail-info">
|
||||||
@@ -105,7 +104,8 @@
|
|||||||
var target = LookupCard(targetName);
|
var target = LookupCard(targetName);
|
||||||
if (target != null)
|
if (target != null)
|
||||||
{
|
{
|
||||||
<button class="inline-card-btn" @onclick="() => SelectCard(target)">@targetName</button>
|
<button class="inline-card-btn"
|
||||||
|
@onclick="() => SelectCard(target)">@targetName</button>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -125,7 +125,8 @@
|
|||||||
var fromCard = LookupCard(Card.ImmortalizeFrom);
|
var fromCard = LookupCard(Card.ImmortalizeFrom);
|
||||||
if (fromCard != null)
|
if (fromCard != null)
|
||||||
{
|
{
|
||||||
<button class="inline-card-btn" @onclick="() => SelectCard(fromCard)">@Card.ImmortalizeFrom</button>
|
<button class="inline-card-btn"
|
||||||
|
@onclick="() => SelectCard(fromCard)">@Card.ImmortalizeFrom</button>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -148,9 +149,15 @@
|
|||||||
|
|
||||||
private bool HasImage => Card?.ImageFile is { Length: > 0 } && Card.ImageFile != "placeholder.png";
|
private bool HasImage => Card?.ImageFile is { Length: > 0 } && Card.ImageFile != "placeholder.png";
|
||||||
|
|
||||||
private async Task Close() => await OnClose.InvokeAsync();
|
private async Task Close()
|
||||||
|
{
|
||||||
|
await OnClose.InvokeAsync();
|
||||||
|
}
|
||||||
|
|
||||||
private async Task SelectCard(CardData card) => await OnSelectCard.InvokeAsync(card);
|
private async Task SelectCard(CardData card)
|
||||||
|
{
|
||||||
|
await OnSelectCard.InvokeAsync(card);
|
||||||
|
}
|
||||||
|
|
||||||
private RenderFragment RenderCardDescription(string description)
|
private RenderFragment RenderCardDescription(string description)
|
||||||
{
|
{
|
||||||
@@ -163,4 +170,5 @@
|
|||||||
return CardDatabase.Cards.FirstOrDefault(c =>
|
return CardDatabase.Cards.FirstOrDefault(c =>
|
||||||
string.Equals(c.Name, cardName, StringComparison.OrdinalIgnoreCase));
|
string.Equals(c.Name, cardName, StringComparison.OrdinalIgnoreCase));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,8 +10,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@keyframes fade-in {
|
@keyframes fade-in {
|
||||||
from { opacity: 0; }
|
from {
|
||||||
to { opacity: 1; }
|
opacity: 0;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-detail {
|
.card-detail {
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
@namespace Shared.Components
|
@namespace Shared.Components
|
||||||
@using Chrono.Model
|
|
||||||
|
|
||||||
<div class="deck-card-row">
|
<div class="deck-card-row">
|
||||||
@foreach (var group in Cards.GroupBy(x => x))
|
@foreach (var group in Cards.GroupBy(x => x))
|
||||||
@@ -29,10 +28,13 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
[Parameter, EditorRequired] public List<string> Cards { get; set; } = [];
|
[Parameter][EditorRequired] public List<string> Cards { get; set; } = [];
|
||||||
[Parameter] public EventCallback<CardData?> OnSelectCard { get; set; }
|
[Parameter] public EventCallback<CardData?> OnSelectCard { get; set; }
|
||||||
|
|
||||||
private async Task SelectCard(CardData? card) => await OnSelectCard.InvokeAsync(card);
|
private async Task SelectCard(CardData? card)
|
||||||
|
{
|
||||||
|
await OnSelectCard.InvokeAsync(card);
|
||||||
|
}
|
||||||
|
|
||||||
private static readonly Dictionary<string, CardData> CardLookup = CardDatabase.Cards
|
private static readonly Dictionary<string, CardData> CardLookup = CardDatabase.Cards
|
||||||
.Where(c => !string.IsNullOrWhiteSpace(c.Name))
|
.Where(c => !string.IsNullOrWhiteSpace(c.Name))
|
||||||
@@ -43,4 +45,5 @@
|
|||||||
{
|
{
|
||||||
return CardLookup.GetValueOrDefault(cardName);
|
return CardLookup.GetValueOrDefault(cardName);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<div class="mana-curve">
|
<div class="mana-curve">
|
||||||
@foreach (var entry in Distribution)
|
@foreach (var entry in Distribution)
|
||||||
{
|
{
|
||||||
var height = MaxCount > 0 ? (entry.Count * 100 / MaxCount) : 0;
|
var height = MaxCount > 0 ? entry.Count * 100 / MaxCount : 0;
|
||||||
<div class="mana-bar-container">
|
<div class="mana-bar-container">
|
||||||
<div class="mana-bar-label">@entry.Count</div>
|
<div class="mana-bar-label">@entry.Count</div>
|
||||||
<div class="mana-bar" style="height: @($"{Math.Max(height, 5)}%")"></div>
|
<div class="mana-bar" style="height: @($"{Math.Max(height, 5)}%")"></div>
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
[Parameter, EditorRequired] public List<(int Cost, int Count)> Distribution { get; set; } = [];
|
[Parameter][EditorRequired] public List<(int Cost, int Count)> Distribution { get; set; } = [];
|
||||||
[Parameter] public int MaxCostLabel { get; set; } = 10;
|
[Parameter] public int MaxCostLabel { get; set; } = 10;
|
||||||
|
|
||||||
private int MaxCount => Distribution.Count > 0 ? Distribution.Max(x => x.Count) : 0;
|
private int MaxCount => Distribution.Count > 0 ? Distribution.Max(x => x.Count) : 0;
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
@implements IDisposable
|
||||||
|
@inject NavigationManager NavigationManager
|
||||||
|
@inject AnalyticsService AnalyticsService
|
||||||
|
|
||||||
|
@code {
|
||||||
|
|
||||||
|
protected override void OnInitialized()
|
||||||
|
{
|
||||||
|
NavigationManager.LocationChanged += OnLocationChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void OnLocationChanged(object? sender, LocationChangedEventArgs e)
|
||||||
|
{
|
||||||
|
await TrackPageView(e.Location);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task TrackPageView(string url)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var relativePath = NavigationManager.ToBaseRelativePath(url);
|
||||||
|
if (!relativePath.StartsWith("/"))
|
||||||
|
{
|
||||||
|
relativePath = "/" + relativePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
await AnalyticsService.TrackPageView(relativePath);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Ignore errors during tracking
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
NavigationManager.LocationChanged -= OnLocationChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -154,7 +154,7 @@ public static class DocDatabase
|
|||||||
{
|
{
|
||||||
Title = "Lifeblood",
|
Title = "Lifeblood",
|
||||||
Category = "Faction",
|
Category = "Faction",
|
||||||
Content = "Concepts:\r\n- Wide Boards\r\n- Overpower",
|
Content = "Concepts:\r\n\r\n- Wide Boards\r\n- Overpower",
|
||||||
Frontmatter = new()
|
Frontmatter = new()
|
||||||
{
|
{
|
||||||
{ "category", "Faction" },
|
{ "category", "Faction" },
|
||||||
@@ -164,7 +164,7 @@ public static class DocDatabase
|
|||||||
{
|
{
|
||||||
Title = "Phasetide",
|
Title = "Phasetide",
|
||||||
Category = "Faction",
|
Category = "Faction",
|
||||||
Content = "Concepts:\r\n- Healing\r\n-",
|
Content = "Concepts:\r\n\r\n- Healing\r\n-",
|
||||||
Frontmatter = new()
|
Frontmatter = new()
|
||||||
{
|
{
|
||||||
{ "category", "Faction" },
|
{ "category", "Faction" },
|
||||||
@@ -174,7 +174,7 @@ public static class DocDatabase
|
|||||||
{
|
{
|
||||||
Title = "Silence",
|
Title = "Silence",
|
||||||
Category = "Faction",
|
Category = "Faction",
|
||||||
Content = "Concepts:\r\n- Activatable Agents\r\n-",
|
Content = "Concepts:\r\n\r\n- Activatable Agents\r\n-",
|
||||||
Frontmatter = new()
|
Frontmatter = new()
|
||||||
{
|
{
|
||||||
{ "category", "Faction" },
|
{ "category", "Faction" },
|
||||||
@@ -184,7 +184,7 @@ public static class DocDatabase
|
|||||||
{
|
{
|
||||||
Title = "Singularity",
|
Title = "Singularity",
|
||||||
Category = "Faction",
|
Category = "Faction",
|
||||||
Content = "Concepts:\r\n- Discard",
|
Content = "Concepts:\r\n\r\n- Discard",
|
||||||
Frontmatter = new()
|
Frontmatter = new()
|
||||||
{
|
{
|
||||||
{ "category", "Faction" },
|
{ "category", "Faction" },
|
||||||
@@ -194,7 +194,7 @@ public static class DocDatabase
|
|||||||
{
|
{
|
||||||
Title = "Splintergleam",
|
Title = "Splintergleam",
|
||||||
Category = "Faction",
|
Category = "Faction",
|
||||||
Content = "# Self Damage\r\n\r\nCan hurt it's own units for power. Has [[Bleed]] to make reoccurring damage on units very easy.\r\n\r\n# Core Damage \r\n\r\nCan hurt both cores easily. Has [[Breakdown]] to take advantage of having an injured core for more damage.",
|
Content = "# Self Damage\r\n\r\nCan hurt it's own units for power. Has [[Bleed]] to make reoccurring damage on units very easy.\r\n\r\n# Core Damage\r\n\r\nCan hurt both cores easily. Has [[Breakdown]] to take advantage of having an injured core for more damage.",
|
||||||
Frontmatter = new()
|
Frontmatter = new()
|
||||||
{
|
{
|
||||||
{ "category", "Faction" },
|
{ "category", "Faction" },
|
||||||
@@ -204,7 +204,7 @@ public static class DocDatabase
|
|||||||
{
|
{
|
||||||
Title = "Sungrace",
|
Title = "Sungrace",
|
||||||
Category = "Faction",
|
Category = "Faction",
|
||||||
Content = "# Energy Reserves\r\n\r\nCare about always having banked energy. Spells can refund themselves when using [[Energy Reserves]], or get stronger is you [[Overflow]] your energy reserve.\r\n\r\nConcepts:\r\n- Ramp\r\n- Using and Replenishing Reserved Energy\r\n-",
|
Content = "# Energy Reserves\r\n\r\nCare about always having banked energy. Spells can refund themselves when using [[Energy Reserves]], or get stronger is\r\nyou [[Overflow]] your energy reserve.\r\n\r\nConcepts:\r\n\r\n- Ramp\r\n- Using and Replenishing Reserved Energy\r\n-",
|
||||||
Frontmatter = new()
|
Frontmatter = new()
|
||||||
{
|
{
|
||||||
{ "category", "Faction" },
|
{ "category", "Faction" },
|
||||||
@@ -479,7 +479,7 @@ public static class DocDatabase
|
|||||||
{
|
{
|
||||||
Title = "Onboarding Track",
|
Title = "Onboarding Track",
|
||||||
Category = "Pass",
|
Category = "Pass",
|
||||||
Content = "| Tier | Reward |\r\n| ---- | -------------------------------- |\r\n| 1 | Sprouts Record |\r\n| 2 | 120 Core Charges |\r\n| 3 | Burn Precon |\r\n| 4 | 120 Core Charges, 25 Flux |\r\n| 5 | Profile Pictures |\r\n| 6 | Emotes |\r\n| 7 | 120 Core Charges |\r\n| 8 | Profile Pictures |\r\n| 9 | 60 Core Charges, 50 Flux |\r\n| 10 | 240 Core Charges, 1 Draft Ticket |\r\n\r\n| Core Charges | Flux |\r\n| ------------ | ---- |\r\n| 660 | 75 |",
|
Content = "| Tier | Reward |\r\n|------|----------------------------------|\r\n| 1 | Sprouts Record |\r\n| 2 | 120 Core Charges |\r\n| 3 | Burn Precon |\r\n| 4 | 120 Core Charges, 25 Flux |\r\n| 5 | Profile Pictures |\r\n| 6 | Emotes |\r\n| 7 | 120 Core Charges |\r\n| 8 | Profile Pictures |\r\n| 9 | 60 Core Charges, 50 Flux |\r\n| 10 | 240 Core Charges, 1 Draft Ticket |\r\n\r\n| Core Charges | Flux |\r\n|--------------|------|\r\n| 660 | 75 |",
|
||||||
Frontmatter = new()
|
Frontmatter = new()
|
||||||
{
|
{
|
||||||
{ "expPer", "30" },
|
{ "expPer", "30" },
|
||||||
@@ -491,7 +491,7 @@ public static class DocDatabase
|
|||||||
{
|
{
|
||||||
Title = "Daily Wins",
|
Title = "Daily Wins",
|
||||||
Category = "Quest",
|
Category = "Quest",
|
||||||
Content = "1: 30cc 30xp \n2: 20cc 25xp \n3: 20cc 20xp \n4: 10cc 20xp \n5: 10cc 15xp \n6-12 are all simply 5xp. \n\nSo the daily total win reward for 12 wins is 90cc/145xp.",
|
Content = "1: 30cc 30xp\n2: 20cc 25xp\n3: 20cc 20xp\n4: 10cc 20xp\n5: 10cc 15xp\n6-12 are all simply 5xp.\n\nSo the daily total win reward for 12 wins is 90cc/145xp.",
|
||||||
Frontmatter = new()
|
Frontmatter = new()
|
||||||
{
|
{
|
||||||
{ "category", "Quest" },
|
{ "category", "Quest" },
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<main>
|
<main>
|
||||||
|
<NavigationTracker/>
|
||||||
<article class="content px-4">
|
<article class="content px-4">
|
||||||
<TelerikRootComponent>
|
<TelerikRootComponent>
|
||||||
@Body
|
@Body
|
||||||
|
|||||||
@@ -4,7 +4,8 @@
|
|||||||
<PageTitle>Chrono CCG - Agents</PageTitle>
|
<PageTitle>Chrono CCG - Agents</PageTitle>
|
||||||
|
|
||||||
<HeadContent>
|
<HeadContent>
|
||||||
<meta name="description" content="View and compare all Chrono CCG agents. Analyze stats, factions, and efficiency to build the perfect deck." />
|
<meta name="description"
|
||||||
|
content="View and compare all Chrono CCG agents. Analyze stats, factions, and efficiency to build the perfect deck."/>
|
||||||
</HeadContent>
|
</HeadContent>
|
||||||
|
|
||||||
<div class="agents-page">
|
<div class="agents-page">
|
||||||
|
|||||||
@@ -5,7 +5,8 @@
|
|||||||
<PageTitle>Chrono CCG - @(deck?.Name ?? "Deck Details")</PageTitle>
|
<PageTitle>Chrono CCG - @(deck?.Name ?? "Deck Details")</PageTitle>
|
||||||
|
|
||||||
<HeadContent>
|
<HeadContent>
|
||||||
<meta name="description" content="@(deck != null ? $"View details for the {deck.Name} deck. {deck.Cards.Count} cards, {deck.Factions.Count} factions." : "Deck details and card list.")" />
|
<meta name="description"
|
||||||
|
content="@(deck != null ? $"View details for the {deck.Name} deck. {deck.Cards.Count} cards, {deck.Factions.Count} factions." : "Deck details and card list.")"/>
|
||||||
</HeadContent>
|
</HeadContent>
|
||||||
|
|
||||||
<div class="deck-detail-page">
|
<div class="deck-detail-page">
|
||||||
@@ -23,7 +24,8 @@
|
|||||||
<article class="deck-article">
|
<article class="deck-article">
|
||||||
<header class="deck-header">
|
<header class="deck-header">
|
||||||
<h1>@deck.Name</h1>
|
<h1>@deck.Name</h1>
|
||||||
<b>Deck Code:</b> <pre>@deck.DeckCode</pre>
|
<b>Deck Code:</b>
|
||||||
|
<pre>@deck.DeckCode</pre>
|
||||||
@if (deck.Factions.Count > 0)
|
@if (deck.Factions.Count > 0)
|
||||||
{
|
{
|
||||||
<div class="deck-factions">
|
<div class="deck-factions">
|
||||||
@@ -44,14 +46,14 @@
|
|||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ManaCurve Distribution="GetManaDistribution()" MaxCostLabel="10" />
|
<ManaCurve Distribution="GetManaDistribution()" MaxCostLabel="10"/>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
@if (deck.Keycards.Count > 0)
|
@if (deck.Keycards.Count > 0)
|
||||||
{
|
{
|
||||||
<section class="deck-section">
|
<section class="deck-section">
|
||||||
<h2><i class="bi bi-star-fill" style="color: var(--gold);"></i> Keycards</h2>
|
<h2><i class="bi bi-star-fill" style="color: var(--gold);"></i> Keycards</h2>
|
||||||
<CardGrid Cards="deck.Keycards" OnSelectCard="SelectCard" />
|
<CardGrid Cards="deck.Keycards" OnSelectCard="SelectCard"/>
|
||||||
</section>
|
</section>
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,13 +61,13 @@
|
|||||||
{
|
{
|
||||||
<section class="deck-section">
|
<section class="deck-section">
|
||||||
<h2><i class="bi bi-shuffle"></i> Divers</h2>
|
<h2><i class="bi bi-shuffle"></i> Divers</h2>
|
||||||
<CardGrid Cards="deck.Divers" OnSelectCard="SelectCard" />
|
<CardGrid Cards="deck.Divers" OnSelectCard="SelectCard"/>
|
||||||
</section>
|
</section>
|
||||||
}
|
}
|
||||||
|
|
||||||
<section class="deck-section">
|
<section class="deck-section">
|
||||||
<h2><i class="bi bi-collection-fill"></i> Cards</h2>
|
<h2><i class="bi bi-collection-fill"></i> Cards</h2>
|
||||||
<CardGrid Cards="deck.Cards" OnSelectCard="SelectCard" />
|
<CardGrid Cards="deck.Cards" OnSelectCard="SelectCard"/>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
@if (deck.Description != null)
|
@if (deck.Description != null)
|
||||||
@@ -84,7 +86,7 @@
|
|||||||
<CardDetailModal Card="selectedCard"
|
<CardDetailModal Card="selectedCard"
|
||||||
OnClose="CloseDetail"
|
OnClose="CloseDetail"
|
||||||
OnSelectCard="SelectCard"
|
OnSelectCard="SelectCard"
|
||||||
DescriptionRenderer="@(desc => CardRenderer.RenderDescription(desc, SelectCard))" />
|
DescriptionRenderer="@(desc => CardRenderer.RenderDescription(desc, SelectCard))"/>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
[Parameter] public string Name { get; set; } = "";
|
[Parameter] public string Name { get; set; } = "";
|
||||||
@@ -124,7 +126,7 @@
|
|||||||
{
|
{
|
||||||
if (deck == null) return [];
|
if (deck == null) return [];
|
||||||
var counts = new Dictionary<int, int>();
|
var counts = new Dictionary<int, int>();
|
||||||
for (int i = 0; i <= 10; i++) counts[i] = 0;
|
for (var i = 0; i <= 10; i++) counts[i] = 0;
|
||||||
|
|
||||||
foreach (var cardName in deck.Cards)
|
foreach (var cardName in deck.Cards)
|
||||||
{
|
{
|
||||||
@@ -139,4 +141,5 @@
|
|||||||
|
|
||||||
return counts.OrderBy(x => x.Key).Select(x => (x.Key, x.Value)).ToList();
|
return counts.OrderBy(x => x.Key).Select(x => (x.Key, x.Value)).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,8 @@
|
|||||||
<PageTitle>Chrono CCG - Decks</PageTitle>
|
<PageTitle>Chrono CCG - Decks</PageTitle>
|
||||||
|
|
||||||
<HeadContent>
|
<HeadContent>
|
||||||
<meta name="description" content="Explore community and official Chrono CCG decks. Find the best strategies and card combinations." />
|
<meta name="description"
|
||||||
|
content="Explore community and official Chrono CCG decks. Find the best strategies and card combinations."/>
|
||||||
</HeadContent>
|
</HeadContent>
|
||||||
|
|
||||||
<div class="decks-page">
|
<div class="decks-page">
|
||||||
@@ -13,7 +14,8 @@
|
|||||||
<div class="decks-header-row">
|
<div class="decks-header-row">
|
||||||
<p class="text-secondary">@decks.Count deck@(decks.Count != 1 ? "s" : "")</p>
|
<p class="text-secondary">@decks.Count deck@(decks.Count != 1 ? "s" : "")</p>
|
||||||
<div class="form-check form-switch">
|
<div class="form-check form-switch">
|
||||||
<input class="form-check-input" type="checkbox" id="showPrecons" @bind="showPrecons" @bind:after="RefreshDecks">
|
<input class="form-check-input" type="checkbox" id="showPrecons" @bind="showPrecons"
|
||||||
|
@bind:after="RefreshDecks">
|
||||||
<label class="form-check-label" for="showPrecons">Show Precons</label>
|
<label class="form-check-label" for="showPrecons">Show Precons</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -70,7 +72,7 @@
|
|||||||
|
|
||||||
@code {
|
@code {
|
||||||
private List<DeckData> decks = [];
|
private List<DeckData> decks = [];
|
||||||
private bool showPrecons = false;
|
private bool showPrecons;
|
||||||
|
|
||||||
protected override void OnInitialized()
|
protected override void OnInitialized()
|
||||||
{
|
{
|
||||||
@@ -83,8 +85,8 @@
|
|||||||
.Where(d => d.IsVisible)
|
.Where(d => d.IsVisible)
|
||||||
.Where(d =>
|
.Where(d =>
|
||||||
showPrecons
|
showPrecons
|
||||||
? d.DeckType is "precon" or "store" :
|
? d.DeckType is "precon" or "store"
|
||||||
d.DeckType is "custom")
|
: d.DeckType is "custom")
|
||||||
.ToList();
|
.ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,17 +13,19 @@
|
|||||||
<aside class="docs-sidebar">
|
<aside class="docs-sidebar">
|
||||||
<div class="search-box">
|
<div class="search-box">
|
||||||
<i class="bi bi-search"></i>
|
<i class="bi bi-search"></i>
|
||||||
<input type="text" placeholder="Search docs..." @bind="searchQuery" @bind:event="oninput" />
|
<input type="text" placeholder="Search docs..." @bind="searchQuery" @bind:event="oninput"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="category-list">
|
<div class="category-list">
|
||||||
<div class="category-item @(selectedCategory == null ? "active" : "")" @onclick="() => SelectCategory(null)">
|
<div class="category-item @(selectedCategory == null ? "active" : "")"
|
||||||
|
@onclick="() => SelectCategory(null)">
|
||||||
All Pages
|
All Pages
|
||||||
</div>
|
</div>
|
||||||
@foreach (var category in categories)
|
@foreach (var category in categories)
|
||||||
{
|
{
|
||||||
var cat = category;
|
var cat = category;
|
||||||
<div class="category-item @(selectedCategory == cat ? "active" : "")" @onclick="() => SelectCategory(cat)">
|
<div class="category-item @(selectedCategory == cat ? "active" : "")"
|
||||||
|
@onclick="() => SelectCategory(cat)">
|
||||||
@cat
|
@cat
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
@@ -134,7 +136,9 @@
|
|||||||
return doc.Frontmatter.TryGetValue(key, out var val) && !string.IsNullOrWhiteSpace(val) ? val : null;
|
return doc.Frontmatter.TryGetValue(key, out var val) && !string.IsNullOrWhiteSpace(val) ? val : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GetLabel(string key) => key switch
|
private static string GetLabel(string key)
|
||||||
|
{
|
||||||
|
return key switch
|
||||||
{
|
{
|
||||||
"category" => "Category",
|
"category" => "Category",
|
||||||
"description" => "Description",
|
"description" => "Description",
|
||||||
@@ -148,6 +152,8 @@
|
|||||||
"faction" => "Faction",
|
"faction" => "Faction",
|
||||||
"exp" => "EXP Reward",
|
"exp" => "EXP Reward",
|
||||||
"see" => "See Also",
|
"see" => "See Also",
|
||||||
_ => string.Concat(key.Select((c, i) => i > 0 && char.IsUpper(c) ? " " + c : c.ToString())),
|
_ => string.Concat(key.Select((c, i) => i > 0 && char.IsUpper(c) ? " " + c : c.ToString()))
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
using Microsoft.JSInterop;
|
||||||
|
|
||||||
|
namespace Shared.Services;
|
||||||
|
|
||||||
|
public class AnalyticsService
|
||||||
|
{
|
||||||
|
private readonly IJSRuntime _jsRuntime;
|
||||||
|
|
||||||
|
public AnalyticsService(IJSRuntime jsRuntime)
|
||||||
|
{
|
||||||
|
_jsRuntime = jsRuntime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async ValueTask TrackPageView(string path)
|
||||||
|
{
|
||||||
|
await _jsRuntime.InvokeVoidAsync("trackPageView", path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async ValueTask TrackEvent(string eventName, object? eventData = null)
|
||||||
|
{
|
||||||
|
if (eventData != null)
|
||||||
|
await _jsRuntime.InvokeVoidAsync("gtag", "event", eventName, eventData);
|
||||||
|
else
|
||||||
|
await _jsRuntime.InvokeVoidAsync("gtag", "event", eventName);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
using Microsoft.AspNetCore.Components;
|
using System.Text.RegularExpressions;
|
||||||
using Chrono.Model;
|
using Chrono.Model;
|
||||||
using System.Text.RegularExpressions;
|
using Microsoft.AspNetCore.Components;
|
||||||
|
|
||||||
namespace Shared.Services;
|
namespace Shared.Services;
|
||||||
|
|
||||||
public class CardRenderingService
|
public class CardRenderingService
|
||||||
{
|
{
|
||||||
private readonly List<string> _cardNames;
|
|
||||||
private readonly Dictionary<string, CardData> _cardLookup;
|
private readonly Dictionary<string, CardData> _cardLookup;
|
||||||
|
private readonly List<string> _cardNames;
|
||||||
private readonly Regex _nameRegex;
|
private readonly Regex _nameRegex;
|
||||||
|
|
||||||
public CardRenderingService()
|
public CardRenderingService()
|
||||||
@@ -33,7 +33,7 @@ public class CardRenderingService
|
|||||||
{
|
{
|
||||||
return builder =>
|
return builder =>
|
||||||
{
|
{
|
||||||
int sequence = 0;
|
var sequence = 0;
|
||||||
var paragraphs = description.Split('\n', StringSplitOptions.RemoveEmptyEntries);
|
var paragraphs = description.Split('\n', StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
|
||||||
foreach (var paragraph in paragraphs)
|
foreach (var paragraph in paragraphs)
|
||||||
@@ -41,31 +41,27 @@ public class CardRenderingService
|
|||||||
builder.OpenElement(sequence++, "p");
|
builder.OpenElement(sequence++, "p");
|
||||||
|
|
||||||
var matches = _nameRegex.Matches(paragraph);
|
var matches = _nameRegex.Matches(paragraph);
|
||||||
int lastIndex = 0;
|
var lastIndex = 0;
|
||||||
|
|
||||||
foreach (Match match in matches)
|
foreach (Match match in matches)
|
||||||
{
|
{
|
||||||
if (match.Index > lastIndex)
|
if (match.Index > lastIndex)
|
||||||
{
|
|
||||||
builder.AddContent(sequence++, paragraph.Substring(lastIndex, match.Index - lastIndex));
|
builder.AddContent(sequence++, paragraph.Substring(lastIndex, match.Index - lastIndex));
|
||||||
}
|
|
||||||
|
|
||||||
var cardName = match.Value;
|
var cardName = match.Value;
|
||||||
var card = LookupCard(cardName);
|
var card = LookupCard(cardName);
|
||||||
|
|
||||||
builder.OpenElement(sequence++, "button");
|
builder.OpenElement(sequence++, "button");
|
||||||
builder.AddAttribute(sequence++, "class", "inline-card-btn");
|
builder.AddAttribute(sequence++, "class", "inline-card-btn");
|
||||||
builder.AddAttribute(sequence++, "onclick", EventCallback.Factory.Create(this, () => onCardClick(card)));
|
builder.AddAttribute(sequence++, "onclick",
|
||||||
|
EventCallback.Factory.Create(this, () => onCardClick(card)));
|
||||||
builder.AddContent(sequence++, cardName);
|
builder.AddContent(sequence++, cardName);
|
||||||
builder.CloseElement();
|
builder.CloseElement();
|
||||||
|
|
||||||
lastIndex = match.Index + match.Length;
|
lastIndex = match.Index + match.Length;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lastIndex < paragraph.Length)
|
if (lastIndex < paragraph.Length) builder.AddContent(sequence++, paragraph.Substring(lastIndex));
|
||||||
{
|
|
||||||
builder.AddContent(sequence++, paragraph.Substring(lastIndex));
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.CloseElement();
|
builder.CloseElement();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using Microsoft.Playwright;
|
using Microsoft.Playwright.NUnit;
|
||||||
using Microsoft.Playwright.NUnit;
|
|
||||||
using Tests.PageObjects;
|
using Tests.PageObjects;
|
||||||
|
|
||||||
namespace Tests;
|
namespace Tests;
|
||||||
@@ -8,10 +7,6 @@ namespace Tests;
|
|||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class FeatureTests : PageTest
|
public class FeatureTests : PageTest
|
||||||
{
|
{
|
||||||
private DecksPage _decksPage;
|
|
||||||
private DeckDetailPage _deckDetailPage;
|
|
||||||
private AgentsPage _agentsPage;
|
|
||||||
|
|
||||||
[SetUp]
|
[SetUp]
|
||||||
public void Setup()
|
public void Setup()
|
||||||
{
|
{
|
||||||
@@ -20,6 +15,10 @@ public class FeatureTests : PageTest
|
|||||||
_agentsPage = new AgentsPage(Page);
|
_agentsPage = new AgentsPage(Page);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private DecksPage _decksPage;
|
||||||
|
private DeckDetailPage _deckDetailPage;
|
||||||
|
private AgentsPage _agentsPage;
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public async Task DeckManaCurve_ShouldBeVisibleOnDetail()
|
public async Task DeckManaCurve_ShouldBeVisibleOnDetail()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4,12 +4,17 @@ namespace Tests.PageObjects;
|
|||||||
|
|
||||||
public class AgentsPage : BasePage
|
public class AgentsPage : BasePage
|
||||||
{
|
{
|
||||||
public AgentsPage(IPage page) : base(page) { }
|
public AgentsPage(IPage page) : base(page)
|
||||||
|
{
|
||||||
public async Task GotoAsync() => await NavigateToAsync("/agents");
|
}
|
||||||
|
|
||||||
public ILocator Grid => Page.Locator(".agents-grid");
|
public ILocator Grid => Page.Locator(".agents-grid");
|
||||||
|
|
||||||
|
public async Task GotoAsync()
|
||||||
|
{
|
||||||
|
await NavigateToAsync("/agents");
|
||||||
|
}
|
||||||
|
|
||||||
public async Task WaitForGridAsync()
|
public async Task WaitForGridAsync()
|
||||||
{
|
{
|
||||||
await Page.WaitForLoadStateAsync(LoadState.NetworkIdle);
|
await Page.WaitForLoadStateAsync(LoadState.NetworkIdle);
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ namespace Tests.PageObjects;
|
|||||||
|
|
||||||
public abstract class BasePage
|
public abstract class BasePage
|
||||||
{
|
{
|
||||||
protected readonly IPage Page;
|
|
||||||
protected readonly string BaseUrl = "http://localhost:5256";
|
protected readonly string BaseUrl = "http://localhost:5256";
|
||||||
|
protected readonly IPage Page;
|
||||||
|
|
||||||
protected BasePage(IPage page)
|
protected BasePage(IPage page)
|
||||||
{
|
{
|
||||||
@@ -17,5 +17,8 @@ public abstract class BasePage
|
|||||||
await Page.GotoAsync($"{BaseUrl}{path}");
|
await Page.GotoAsync($"{BaseUrl}{path}");
|
||||||
}
|
}
|
||||||
|
|
||||||
public ILocator GetHeading() => Page.Locator("h1");
|
public ILocator GetHeading()
|
||||||
|
{
|
||||||
|
return Page.Locator("h1");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -4,12 +4,17 @@ namespace Tests.PageObjects;
|
|||||||
|
|
||||||
public class DeckDetailPage : BasePage
|
public class DeckDetailPage : BasePage
|
||||||
{
|
{
|
||||||
public DeckDetailPage(IPage page) : base(page) { }
|
public DeckDetailPage(IPage page) : base(page)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public ILocator ManaCurve => Page.Locator(".mana-curve");
|
public ILocator ManaCurve => Page.Locator(".mana-curve");
|
||||||
public ILocator ManaBars => Page.Locator(".mana-bar");
|
public ILocator ManaBars => Page.Locator(".mana-bar");
|
||||||
public ILocator ManaBarLabels => Page.Locator(".mana-bar-label");
|
public ILocator ManaBarLabels => Page.Locator(".mana-bar-label");
|
||||||
public ILocator ManaCosts => Page.Locator(".mana-cost");
|
public ILocator ManaCosts => Page.Locator(".mana-cost");
|
||||||
|
|
||||||
public async Task<int> GetManaBarCountAsync() => await ManaBars.CountAsync();
|
public async Task<int> GetManaBarCountAsync()
|
||||||
|
{
|
||||||
|
return await ManaBars.CountAsync();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -4,14 +4,23 @@ namespace Tests.PageObjects;
|
|||||||
|
|
||||||
public class DecksPage : BasePage
|
public class DecksPage : BasePage
|
||||||
{
|
{
|
||||||
public DecksPage(IPage page) : base(page) { }
|
public DecksPage(IPage page) : base(page)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public async Task GotoAsync() => await NavigateToAsync("/decks");
|
public async Task GotoAsync()
|
||||||
|
{
|
||||||
|
await NavigateToAsync("/decks");
|
||||||
|
}
|
||||||
|
|
||||||
public ILocator GetDeckCards() => Page.Locator(".deck-card");
|
public ILocator GetDeckCards()
|
||||||
|
{
|
||||||
|
return Page.Locator(".deck-card");
|
||||||
|
}
|
||||||
|
|
||||||
public async Task ClickDeckByNameAsync(string name)
|
public async Task ClickDeckByNameAsync(string name)
|
||||||
{
|
{
|
||||||
await Page.Locator(".deck-card-title").GetByText(name, new() { Exact = true }).ClickAsync();
|
await Page.Locator(".deck-card-title").GetByText(name, new LocatorGetByTextOptions { Exact = true })
|
||||||
|
.ClickAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using System.Text.RegularExpressions;
|
||||||
using Microsoft.Playwright;
|
using Microsoft.Playwright;
|
||||||
using Microsoft.Playwright.NUnit;
|
using Microsoft.Playwright.NUnit;
|
||||||
|
|
||||||
@@ -13,7 +14,7 @@ public class PlaywrightTests : PageTest
|
|||||||
public async Task HomePage_ShouldLoad()
|
public async Task HomePage_ShouldLoad()
|
||||||
{
|
{
|
||||||
await Page.GotoAsync(BaseUrl);
|
await Page.GotoAsync(BaseUrl);
|
||||||
await Expect(Page).ToHaveTitleAsync(new System.Text.RegularExpressions.Regex("Chrono CCG"));
|
await Expect(Page).ToHaveTitleAsync(new Regex("Chrono CCG"));
|
||||||
|
|
||||||
// Check for some content that should be on the home page
|
// Check for some content that should be on the home page
|
||||||
var heading = Page.Locator("h1");
|
var heading = Page.Locator("h1");
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ namespace Tests;
|
|||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class TelerikLicenseTests : PageTest
|
public class TelerikLicenseTests : PageTest
|
||||||
{
|
{
|
||||||
|
|
||||||
private const string BaseUrl = "http://localhost:5256";
|
private const string BaseUrl = "http://localhost:5256";
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
|||||||
+12
-17
@@ -1,13 +1,12 @@
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Net.Http;
|
|
||||||
|
|
||||||
namespace Tests;
|
namespace Tests;
|
||||||
|
|
||||||
[SetUpFixture]
|
[SetUpFixture]
|
||||||
public class TestSetup
|
public class TestSetup
|
||||||
{
|
{
|
||||||
private Process? _serverProcess;
|
|
||||||
private const string ServerUrl = "http://localhost:5256";
|
private const string ServerUrl = "http://localhost:5256";
|
||||||
|
private Process? _serverProcess;
|
||||||
|
|
||||||
[OneTimeSetUp]
|
[OneTimeSetUp]
|
||||||
public async Task BeforeAllTests()
|
public async Task BeforeAllTests()
|
||||||
@@ -17,14 +16,9 @@ public class TestSetup
|
|||||||
// Find solution root
|
// Find solution root
|
||||||
var currentDir = AppContext.BaseDirectory;
|
var currentDir = AppContext.BaseDirectory;
|
||||||
while (currentDir != null && !File.Exists(Path.Combine(currentDir, "Chrono.sln")))
|
while (currentDir != null && !File.Exists(Path.Combine(currentDir, "Chrono.sln")))
|
||||||
{
|
|
||||||
currentDir = Path.GetDirectoryName(currentDir);
|
currentDir = Path.GetDirectoryName(currentDir);
|
||||||
}
|
|
||||||
|
|
||||||
if (currentDir == null)
|
if (currentDir == null) throw new Exception("Could not find Chrono.sln to determine project path.");
|
||||||
{
|
|
||||||
throw new Exception("Could not find Chrono.sln to determine project path.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var projectPath = Path.Combine(currentDir, "Server", "Server.csproj");
|
var projectPath = Path.Combine(currentDir, "Server", "Server.csproj");
|
||||||
|
|
||||||
@@ -41,24 +35,26 @@ public class TestSetup
|
|||||||
|
|
||||||
_serverProcess = Process.Start(startInfo);
|
_serverProcess = Process.Start(startInfo);
|
||||||
|
|
||||||
_serverProcess.OutputDataReceived += (s, e) => { if (e.Data != null) Console.WriteLine($"[SERVER OUT] {e.Data}"); };
|
_serverProcess.OutputDataReceived += (s, e) =>
|
||||||
_serverProcess.ErrorDataReceived += (s, e) => { if (e.Data != null) Console.WriteLine($"[SERVER ERR] {e.Data}"); };
|
{
|
||||||
|
if (e.Data != null) Console.WriteLine($"[SERVER OUT] {e.Data}");
|
||||||
|
};
|
||||||
|
_serverProcess.ErrorDataReceived += (s, e) =>
|
||||||
|
{
|
||||||
|
if (e.Data != null) Console.WriteLine($"[SERVER ERR] {e.Data}");
|
||||||
|
};
|
||||||
_serverProcess.BeginOutputReadLine();
|
_serverProcess.BeginOutputReadLine();
|
||||||
_serverProcess.BeginErrorReadLine();
|
_serverProcess.BeginErrorReadLine();
|
||||||
|
|
||||||
if (_serverProcess == null)
|
if (_serverProcess == null) throw new Exception("Failed to start server process.");
|
||||||
{
|
|
||||||
throw new Exception("Failed to start server process.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for the server to be ready
|
// Wait for the server to be ready
|
||||||
using var client = new HttpClient();
|
using var client = new HttpClient();
|
||||||
var sw = Stopwatch.StartNew();
|
var sw = Stopwatch.StartNew();
|
||||||
var timeout = TimeSpan.FromSeconds(30);
|
var timeout = TimeSpan.FromSeconds(30);
|
||||||
bool isReady = false;
|
var isReady = false;
|
||||||
|
|
||||||
while (sw.Elapsed < timeout)
|
while (sw.Elapsed < timeout)
|
||||||
{
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var response = await client.GetAsync(ServerUrl);
|
var response = await client.GetAsync(ServerUrl);
|
||||||
@@ -73,7 +69,6 @@ public class TestSetup
|
|||||||
// Wait and retry
|
// Wait and retry
|
||||||
await Task.Delay(1000);
|
await Task.Delay(1000);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (!isReady)
|
if (!isReady)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
<Router AppAssembly="@typeof(App).Assembly" AdditionalAssemblies="new[] { typeof(Home).Assembly }" NotFoundPage="typeof(NotFound)">
|
<Router AppAssembly="@typeof(App).Assembly" AdditionalAssemblies="new[] { typeof(Home).Assembly }"
|
||||||
|
NotFoundPage="typeof(NotFound)">
|
||||||
<Found Context="routeData">
|
<Found Context="routeData">
|
||||||
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)"/>
|
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)"/>
|
||||||
<FocusOnNavigate RouteData="@routeData" Selector="h1"/>
|
<FocusOnNavigate RouteData="@routeData" Selector="h1"/>
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
@using System.Text.RegularExpressions
|
||||||
@namespace Chrono.Components
|
@namespace Chrono.Components
|
||||||
|
|
||||||
@if (Card != null)
|
@if (Card != null)
|
||||||
@@ -10,7 +11,7 @@
|
|||||||
{
|
{
|
||||||
<div class="detail-image">
|
<div class="detail-image">
|
||||||
<img src="@Card.ImagePath" alt="@Card.Name"
|
<img src="@Card.ImagePath" alt="@Card.Name"
|
||||||
onerror="this.style.display='none';this.parentElement.style.display='none'" />
|
onerror="this.style.display='none';this.parentElement.style.display='none'"/>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
<div class="detail-info">
|
<div class="detail-info">
|
||||||
@@ -88,7 +89,8 @@
|
|||||||
var target = LookupCard(targetName);
|
var target = LookupCard(targetName);
|
||||||
if (target != null)
|
if (target != null)
|
||||||
{
|
{
|
||||||
<button class="inline-card-btn" @onclick="() => Navigate(target)">@targetName</button>
|
<button class="inline-card-btn"
|
||||||
|
@onclick="() => Navigate(target)">@targetName</button>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -108,7 +110,8 @@
|
|||||||
var fromCard = LookupCard(Card.ImmortalizeFrom);
|
var fromCard = LookupCard(Card.ImmortalizeFrom);
|
||||||
if (fromCard != null)
|
if (fromCard != null)
|
||||||
{
|
{
|
||||||
<button class="inline-card-btn" @onclick="() => Navigate(fromCard)">@Card.ImmortalizeFrom</button>
|
<button class="inline-card-btn"
|
||||||
|
@onclick="() => Navigate(fromCard)">@Card.ImmortalizeFrom</button>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -181,7 +184,9 @@
|
|||||||
{
|
{
|
||||||
await Http.PostAsJsonAsync("api/notes", new CardNote { CardName = Card.Name, Note = currentNote });
|
await Http.PostAsJsonAsync("api/notes", new CardNote { CardName = Card.Name, Note = currentNote });
|
||||||
}
|
}
|
||||||
catch { }
|
catch
|
||||||
|
{
|
||||||
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
isSaving = false;
|
isSaving = false;
|
||||||
@@ -209,7 +214,9 @@
|
|||||||
string.Equals(c.Name, cardName, StringComparison.OrdinalIgnoreCase));
|
string.Equals(c.Name, cardName, StringComparison.OrdinalIgnoreCase));
|
||||||
}
|
}
|
||||||
|
|
||||||
private RenderFragment RenderDescription(string description) => builder =>
|
private RenderFragment RenderDescription(string description)
|
||||||
|
{
|
||||||
|
return builder =>
|
||||||
{
|
{
|
||||||
var cardNames = CardDatabase.Cards
|
var cardNames = CardDatabase.Cards
|
||||||
.Select(c => c.Name)
|
.Select(c => c.Name)
|
||||||
@@ -217,18 +224,18 @@
|
|||||||
.OrderByDescending(n => n.Length)
|
.OrderByDescending(n => n.Length)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
int seq = 0;
|
var seq = 0;
|
||||||
var remaining = description;
|
var remaining = description;
|
||||||
|
|
||||||
while (!string.IsNullOrEmpty(remaining))
|
while (!string.IsNullOrEmpty(remaining))
|
||||||
{
|
{
|
||||||
int bestIdx = -1;
|
var bestIdx = -1;
|
||||||
string? bestName = null;
|
string? bestName = null;
|
||||||
|
|
||||||
foreach (var name in cardNames)
|
foreach (var name in cardNames)
|
||||||
{
|
{
|
||||||
var pattern = $@"\b{System.Text.RegularExpressions.Regex.Escape(name)}\b";
|
var pattern = $@"\b{Regex.Escape(name)}\b";
|
||||||
var match = System.Text.RegularExpressions.Regex.Match(remaining, pattern, System.Text.RegularExpressions.RegexOptions.IgnoreCase);
|
var match = Regex.Match(remaining, pattern, RegexOptions.IgnoreCase);
|
||||||
|
|
||||||
if (match.Success)
|
if (match.Success)
|
||||||
{
|
{
|
||||||
@@ -269,4 +276,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,8 +8,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@keyframes fade-in {
|
@keyframes fade-in {
|
||||||
from { opacity: 0; }
|
from {
|
||||||
to { opacity: 1; }
|
opacity: 0;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-detail {
|
.card-detail {
|
||||||
@@ -231,7 +235,8 @@
|
|||||||
text-decoration-color: #7b73ff;
|
text-decoration-color: #7b73ff;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@media (max-width: 768px) {
|
@
|
||||||
|
@media (max-width: 768px) {
|
||||||
.detail-layout {
|
.detail-layout {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
@page "/cards"
|
@page "/cards"
|
||||||
@inject HttpClient Http
|
@inject HttpClient Http
|
||||||
|
@inject AnalyticsService AnalyticsService
|
||||||
|
|
||||||
<PageTitle>Chrono CCG - Card Gallery</PageTitle>
|
<PageTitle>Chrono CCG - Card Gallery</PageTitle>
|
||||||
|
|
||||||
<HeadContent>
|
<HeadContent>
|
||||||
<meta name="description" content="Browse and filter all Chrono CCG cards. Explore agents, spells, and more." />
|
<meta name="description" content="Browse and filter all Chrono CCG cards. Explore agents, spells, and more."/>
|
||||||
</HeadContent>
|
</HeadContent>
|
||||||
|
|
||||||
<div class="gallery-container">
|
<div class="gallery-container">
|
||||||
@@ -21,7 +22,8 @@
|
|||||||
<button class="tab agent @(categoryFilter == "Agent" ? "active" : "")" @onclick='@(() => SetCategory("Agent"))'>
|
<button class="tab agent @(categoryFilter == "Agent" ? "active" : "")" @onclick='@(() => SetCategory("Agent"))'>
|
||||||
<i class="bi bi-person-fill"></i> Agents
|
<i class="bi bi-person-fill"></i> Agents
|
||||||
</button>
|
</button>
|
||||||
<button class="tab agent @(categoryFilter == "Immortalized" ? "active" : "")" @onclick='@(() => SetCategory("Immortalized"))'>
|
<button class="tab agent @(categoryFilter == "Immortalized" ? "active" : "")"
|
||||||
|
@onclick='@(() => SetCategory("Immortalized"))'>
|
||||||
<i class="bi bi-person-fill"></i> Immortalized
|
<i class="bi bi-person-fill"></i> Immortalized
|
||||||
</button>
|
</button>
|
||||||
<!-- //Todo make agent being linked to immortal for side by side sorting
|
<!-- //Todo make agent being linked to immortal for side by side sorting
|
||||||
@@ -118,7 +120,7 @@
|
|||||||
{
|
{
|
||||||
<div class="card-grid @(showDetailedView ? "detailed-view" : "")">
|
<div class="card-grid @(showDetailedView ? "detailed-view" : "")">
|
||||||
@{ var idx = 0; }
|
@{ var idx = 0; }
|
||||||
@foreach (var card in filteredCards.Where(a=>a.Category is "Agent" or "Spell" or "Immortalized"))
|
@foreach (var card in filteredCards.Where(a => a.Category is "Agent" or "Spell" or "Immortalized"))
|
||||||
{
|
{
|
||||||
<div class="card-cell @(selectedCard == card ? "selected" : "")"
|
<div class="card-cell @(selectedCard == card ? "selected" : "")"
|
||||||
style="--i: @idx"
|
style="--i: @idx"
|
||||||
@@ -171,7 +173,7 @@
|
|||||||
|
|
||||||
@if (selectedCard != null)
|
@if (selectedCard != null)
|
||||||
{
|
{
|
||||||
<CardDialog Card="selectedCard" OnClose="CloseDetail" OnNavigate="SelectCard" />
|
<CardDialog Card="selectedCard" OnClose="CloseDetail" OnNavigate="SelectCard"/>
|
||||||
}
|
}
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
@@ -234,11 +236,13 @@
|
|||||||
private void SetCategory(string cat)
|
private void SetCategory(string cat)
|
||||||
{
|
{
|
||||||
categoryFilter = categoryFilter == cat ? "" : cat;
|
categoryFilter = categoryFilter == cat ? "" : cat;
|
||||||
|
_ = AnalyticsService.TrackEvent("filter_category", new { category = categoryFilter });
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ClearCategoryFilter()
|
private void ClearCategoryFilter()
|
||||||
{
|
{
|
||||||
categoryFilter = "";
|
categoryFilter = "";
|
||||||
|
_ = AnalyticsService.TrackEvent("filter_category", new { category = "All" });
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ClearFactionFilter()
|
private void ClearFactionFilter()
|
||||||
@@ -264,6 +268,7 @@
|
|||||||
private void SelectCard(CardData card)
|
private void SelectCard(CardData card)
|
||||||
{
|
{
|
||||||
selectedCard = card;
|
selectedCard = card;
|
||||||
|
_ = AnalyticsService.TrackEvent("select_item", new { item_name = card.Name, item_category = card.Category });
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CloseDetail()
|
private void CloseDetail()
|
||||||
@@ -278,4 +283,5 @@
|
|||||||
factionFilter = "";
|
factionFilter = "";
|
||||||
costFilter = "";
|
costFilter = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using Microsoft.AspNetCore.Components.Web;
|
using Microsoft.AspNetCore.Components.Web;
|
||||||
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
|
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
|
||||||
|
using Shared.Services;
|
||||||
using Web;
|
using Web;
|
||||||
|
|
||||||
var builder = WebAssemblyHostBuilder.CreateDefault(args);
|
var builder = WebAssemblyHostBuilder.CreateDefault(args);
|
||||||
@@ -8,6 +9,7 @@ builder.RootComponents.Add<HeadOutlet>("head::after");
|
|||||||
|
|
||||||
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
|
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
|
||||||
builder.Services.AddTelerikBlazor();
|
builder.Services.AddTelerikBlazor();
|
||||||
builder.Services.AddSingleton<Shared.Services.CardRenderingService>();
|
builder.Services.AddSingleton<CardRenderingService>();
|
||||||
|
builder.Services.AddScoped<AnalyticsService>();
|
||||||
|
|
||||||
await builder.Build().RunAsync();
|
await builder.Build().RunAsync();
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ServiceWorker Include="wwwroot\service-worker.js" PublishedContent="wwwroot\service-worker.published.js" />
|
<ServiceWorker Include="wwwroot\service-worker.js" PublishedContent="wwwroot\service-worker.published.js"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -21,6 +21,6 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Model\Model.csproj"/>
|
<ProjectReference Include="..\Model\Model.csproj"/>
|
||||||
<ProjectReference Include="..\Shared\Shared.csproj" />
|
<ProjectReference Include="..\Shared\Shared.csproj"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -9,6 +9,8 @@
|
|||||||
@using Microsoft.JSInterop
|
@using Microsoft.JSInterop
|
||||||
@using Shared.Layout
|
@using Shared.Layout
|
||||||
@using Shared.Pages
|
@using Shared.Pages
|
||||||
|
@using Shared.Services
|
||||||
|
@using Shared.Components
|
||||||
@using Chrono.Components
|
@using Chrono.Components
|
||||||
@using Telerik.Blazor
|
@using Telerik.Blazor
|
||||||
@using Telerik.Blazor.Components
|
@using Telerik.Blazor.Components
|
||||||
|
|||||||
@@ -257,9 +257,6 @@ a, .btn-link {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.inline-card-btn {
|
.inline-card-btn {
|
||||||
background: none;
|
background: none;
|
||||||
border: none;
|
border: none;
|
||||||
|
|||||||
@@ -6,26 +6,29 @@
|
|||||||
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
|
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
|
||||||
<title>Chrono CCG - Card Gallery</title>
|
<title>Chrono CCG - Card Gallery</title>
|
||||||
<base href="/"/>
|
<base href="/"/>
|
||||||
<meta name="description" content="Explore the full collection of Chrono CCG cards. Filter by cost, faction, and type."/>
|
<meta content="Explore the full collection of Chrono CCG cards. Filter by cost, faction, and type."
|
||||||
<meta name="keywords" content="Chrono CCG, Reference, Card Gallery, DB"/>
|
name="description"/>
|
||||||
|
<meta content="Chrono CCG, Reference, Card Gallery, DB" name="keywords"/>
|
||||||
|
|
||||||
<!-- Open Graph / Facebook -->
|
<!-- Open Graph / Facebook -->
|
||||||
<meta property="og:type" content="website"/>
|
<meta content="website" property="og:type"/>
|
||||||
<meta property="og:url" content="https://chronoccg.com/"/>
|
<meta content="https://chronoccg.com/" property="og:url"/>
|
||||||
<meta property="og:title" content="Chrono CCG - Card Gallery"/>
|
<meta content="Chrono CCG - Card Gallery" property="og:title"/>
|
||||||
<meta property="og:description" content="Explore the full collection of Chrono CCG cards. Filter by cost, faction, and type."/>
|
<meta content="Explore the full collection of Chrono CCG cards. Filter by cost, faction, and type."
|
||||||
<meta property="og:image" content="https://chronoccg.com/favicon.png"/>
|
property="og:description"/>
|
||||||
|
<meta content="https://chronoccg.com/favicon.png" property="og:image"/>
|
||||||
|
|
||||||
<!-- Twitter -->
|
<!-- Twitter -->
|
||||||
<meta property="twitter:card" content="summary_large_image"/>
|
<meta content="summary_large_image" property="twitter:card"/>
|
||||||
<meta property="twitter:url" content="https://chronoccg.com/"/>
|
<meta content="https://chronoccg.com/" property="twitter:url"/>
|
||||||
<meta property="twitter:title" content="Chrono CCG - Card Gallery"/>
|
<meta content="Chrono CCG - Card Gallery" property="twitter:title"/>
|
||||||
<meta property="twitter:description" content="Explore the full collection of Chrono CCG cards. Filter by cost, faction, and type."/>
|
<meta content="Explore the full collection of Chrono CCG cards. Filter by cost, faction, and type."
|
||||||
<meta property="twitter:image" content="https://chronoccg.com/favicon.png"/>
|
property="twitter:description"/>
|
||||||
|
<meta content="https://chronoccg.com/favicon.png" property="twitter:image"/>
|
||||||
|
|
||||||
<link href="manifest.json" rel="manifest" />
|
<link href="manifest.json" rel="manifest"/>
|
||||||
<link rel="apple-touch-icon" sizes="512x512" href="icon-512.png" />
|
<link href="icon-512.png" rel="apple-touch-icon" sizes="512x512"/>
|
||||||
<link rel="apple-touch-icon" sizes="192x192" href="icon-192.png" />
|
<link href="icon-192.png" rel="apple-touch-icon" sizes="192x192"/>
|
||||||
<link href="https://fonts.googleapis.com" rel="preconnect"/>
|
<link href="https://fonts.googleapis.com" rel="preconnect"/>
|
||||||
<link crossorigin href="https://fonts.gstatic.com" rel="preconnect"/>
|
<link crossorigin href="https://fonts.gstatic.com" rel="preconnect"/>
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet"/>
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet"/>
|
||||||
@@ -37,6 +40,26 @@
|
|||||||
<script defer src="/_content/Telerik.UI.for.Blazor/js/telerik-blazor.js"></script>
|
<script defer src="/_content/Telerik.UI.for.Blazor/js/telerik-blazor.js"></script>
|
||||||
<link href="/favicon.png" rel="icon" type="image/png"/>
|
<link href="/favicon.png" rel="icon" type="image/png"/>
|
||||||
<script type="importmap"></script>
|
<script type="importmap"></script>
|
||||||
|
<!-- Google tag (gtag.js) -->
|
||||||
|
<script async src="https://www.googletagmanager.com/gtag/js?id=G-HVMP2QEJLR"></script>
|
||||||
|
<script>
|
||||||
|
window.dataLayer = window.dataLayer || [];
|
||||||
|
|
||||||
|
function gtag() {
|
||||||
|
dataLayer.push(arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
gtag('js', new Date());
|
||||||
|
|
||||||
|
gtag('config', 'G-HVMP2QEJLR');
|
||||||
|
|
||||||
|
window.trackPageView = (path) => {
|
||||||
|
gtag('event', 'page_view', {
|
||||||
|
page_path: path,
|
||||||
|
page_location: window.location.origin + path
|
||||||
|
});
|
||||||
|
};
|
||||||
|
</script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
// In development, always fetch from the network and do not cache.
|
// In development, always fetch from the network and do not cache.
|
||||||
// This is appropriate during development as the service worker will wake up and fetch resources from the network.
|
// This is appropriate during development as the service worker will wake up and fetch resources from the network.
|
||||||
self.addEventListener('fetch', () => { });
|
self.addEventListener('fetch', () => {
|
||||||
|
});
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ self.addEventListener('fetch', event => event.respondWith(onFetch(event)));
|
|||||||
|
|
||||||
const cacheNamePrefix = 'offline-cache-';
|
const cacheNamePrefix = 'offline-cache-';
|
||||||
const cacheName = `${cacheNamePrefix}${self.assetsManifest.version}`;
|
const cacheName = `${cacheNamePrefix}${self.assetsManifest.version}`;
|
||||||
const offlineAssetsInclude = [ /\.dll$/, /\.pdb$/, /\.wasm/, /\.html/, /\.js$/, /\.json$/, /\.css$/, /\.woff$/, /\.png$/, /\.jpe?g$/, /\.gif$/, /\.ico$/, /\.blat$/, /\.dat$/ ];
|
const offlineAssetsInclude = [/\.dll$/, /\.pdb$/, /\.wasm/, /\.html/, /\.js$/, /\.json$/, /\.css$/, /\.woff$/, /\.png$/, /\.jpe?g$/, /\.gif$/, /\.ico$/, /\.blat$/, /\.dat$/];
|
||||||
const offlineAssetsExclude = [ /^service-worker\.js$/ ];
|
const offlineAssetsExclude = [/^service-worker\.js$/];
|
||||||
|
|
||||||
async function onInstall(event) {
|
async function onInstall(event) {
|
||||||
console.info('Service worker installing');
|
console.info('Service worker installing');
|
||||||
@@ -18,7 +18,7 @@ async function onInstall(event) {
|
|||||||
const assetsRequests = self.assetsManifest.assets
|
const assetsRequests = self.assetsManifest.assets
|
||||||
.filter(asset => offlineAssetsInclude.some(pattern => pattern.test(asset.url)))
|
.filter(asset => offlineAssetsInclude.some(pattern => pattern.test(asset.url)))
|
||||||
.filter(asset => !offlineAssetsExclude.some(pattern => pattern.test(asset.url)))
|
.filter(asset => !offlineAssetsExclude.some(pattern => pattern.test(asset.url)))
|
||||||
.map(asset => new Request(asset.url, { integrity: asset.hash, cache: 'no-cache' }));
|
.map(asset => new Request(asset.url, {integrity: asset.hash, cache: 'no-cache'}));
|
||||||
await caches.open(cacheName).then(cache => cache.addAll(assetsRequests));
|
await caches.open(cacheName).then(cache => cache.addAll(assetsRequests));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,12 @@
|
|||||||
{
|
{
|
||||||
"navigationFallback": {
|
"navigationFallback": {
|
||||||
"rewrite": "/index.html",
|
"rewrite": "/index.html",
|
||||||
"exclude": ["/css/*", "/lib/*", "/_framework/*", "/_content/*", "/sample-data/*"]
|
"exclude": [
|
||||||
|
"/css/*",
|
||||||
|
"/lib/*",
|
||||||
|
"/_framework/*",
|
||||||
|
"/_content/*",
|
||||||
|
"/sample-data/*"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ archetypes:
|
|||||||
imageLink: "[[Aardvark Precinct Captain.png]]"
|
imageLink: "[[Aardvark Precinct Captain.png]]"
|
||||||
ranking: A
|
ranking: A
|
||||||
---
|
---
|
||||||
I feel like it easy to always make this card sprout 5, so +5/+5 worth of stats for two man. Plus at that point, it will Immortalize as a +3/+3 and activate [[Deadly Fauna]].
|
|
||||||
|
I feel like it easy to always make this card sprout 5, so +5/+5 worth of stats for two man. Plus at that point, it will
|
||||||
|
Immortalize as a +3/+3 and activate [[Deadly Fauna]].
|
||||||
|
|
||||||
![[Aardvark Precinct Captain.png]]
|
![[Aardvark Precinct Captain.png]]
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ archetypes:
|
|||||||
- "[[Rewound]]"
|
- "[[Rewound]]"
|
||||||
imageLink: "[[Divergence Assassin.png]]"
|
imageLink: "[[Divergence Assassin.png]]"
|
||||||
---
|
---
|
||||||
|
|
||||||
![[Divergence Assassin.png]]
|
![[Divergence Assassin.png]]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ archetypes:
|
|||||||
imageLink: "[[Scuttling Spares.png]]"
|
imageLink: "[[Scuttling Spares.png]]"
|
||||||
rank: S
|
rank: S
|
||||||
---
|
---
|
||||||
|
|
||||||
I haven't played this card, but it sounds like it obviously enables discard.
|
I haven't played this card, but it sounds like it obviously enables discard.
|
||||||
|
|
||||||
![[Scuttling Spares.png]]
|
![[Scuttling Spares.png]]
|
||||||
|
|||||||
@@ -2,4 +2,5 @@
|
|||||||
category: Debuff
|
category: Debuff
|
||||||
description: Set this Agent's strength to 0 until Round End.
|
description: Set this Agent's strength to 0 until Round End.
|
||||||
---
|
---
|
||||||
|
|
||||||
Reduce attack to zero.
|
Reduce attack to zero.
|
||||||
@@ -74,20 +74,30 @@ factions:
|
|||||||
deckCode: AUHUE2LHEBCW4ZLSM54SAQ3POB4QWY3PNZZXI4TVMN2GKZBTAADACASPCICESCQCAUFDCAYGAMCAGAQDDYBQCAYBAOTQCAYQAMDQG
|
deckCode: AUHUE2LHEBCW4ZLSM54SAQ3POB4QWY3PNZZXI4TVMN2GKZBTAADACASPCICESCQCAUFDCAYGAMCAGAQDDYBQCAYBAOTQCAYQAMDQG
|
||||||
deckType: custom
|
deckType: custom
|
||||||
---
|
---
|
||||||
The idea of this deck is to go heavy on stat-efficient cards. Like 2 for a 2/3 [[Kinetic Absorber]], 5/6 [[Lumbering Starseeker]], and 8 for a 9/14 [[Lightsteel Colossus]].
|
|
||||||
|
|
||||||
You got [[Devourer Spawn]] to help push for lethal. It's going to have a bunch of keywords on it, given how popular timelines are in the meta.
|
The idea of this deck is to go heavy on stat-efficient cards. Like 2 for a 2/3 [[Kinetic Absorber]],
|
||||||
|
5/6 [[Lumbering Starseeker]], and 8 for a 9/14 [[Lightsteel Colossus]].
|
||||||
|
|
||||||
You need to Overflow your mana once in a while, or some of your [[Supernova]] removal will be useless. Be pass heavy. And if you do end up overflowing many times [[Supernova]] can refill for mana bar, and it's worth adding some [[Snap Back]]s to the deck to use that spell multiple times.
|
You got [[Devourer Spawn]] to help push for lethal. It's going to have a bunch of keywords on it, given how popular
|
||||||
|
timelines are in the meta.
|
||||||
|
|
||||||
|
You need to Overflow your mana once in a while, or some of your [[Supernova]] removal will be useless. Be pass heavy.
|
||||||
|
And if you do end up overflowing many times [[Supernova]] can refill for mana bar, and it's worth adding
|
||||||
|
some [[Snap Back]]s to the deck to use that spell multiple times.
|
||||||
|
|
||||||
Attack aggressively with [[Brilliant Martyr]]. You really want [[Star Siphon]] to ramp.
|
Attack aggressively with [[Brilliant Martyr]]. You really want [[Star Siphon]] to ramp.
|
||||||
|
|
||||||
As divers, [[Peaceful Synthesizer]] is a 1 drop that will quickly turn into a 3/3. Can't get more stat efficient than that. [[Nonlethal Special Forces]] is to enable [[Phasetide]] for AOE [[Mute]] cards given we don't care about stat buffs. Also as it stands, it always seems to punch a above it's weight on the board given the [[Disarm]] effect.
|
As divers, [[Peaceful Synthesizer]] is a 1 drop that will quickly turn into a 3/3. Can't get more stat efficient than
|
||||||
|
that. [[Nonlethal Special Forces]] is to enable [[Phasetide]] for AOE [[Mute]] cards given we don't care about stat
|
||||||
|
buffs. Also as it stands, it always seems to punch a above it's weight on the board given the [[Disarm]] effect.
|
||||||
|
|
||||||
So ya, [[Curb the Anomalies]] can weaken the enemy board, by shutting off all there better stats, activates, and synergies. Hopefully destroy the entire enemy board in combat.
|
So ya, [[Curb the Anomalies]] can weaken the enemy board, by shutting off all there better stats, activates, and
|
||||||
|
synergies. Hopefully destroy the entire enemy board in combat.
|
||||||
|
|
||||||
And I suppose [[Bearer of the Broth]]. Since the deck has so much raw stats, you will probably find somewhere to heal. Like healing [[Kinetic Absorber]] so it can keep killing after it Immortalizes.
|
And I suppose [[Bearer of the Broth]]. Since the deck has so much raw stats, you will probably find somewhere to heal.
|
||||||
|
Like healing [[Kinetic Absorber]] so it can keep killing after it Immortalizes.
|
||||||
|
|
||||||
Also in practice [[Bearer of the Broth]] has proven to be an insane and reliable draw engine for this deck.
|
Also in practice [[Bearer of the Broth]] has proven to be an insane and reliable draw engine for this deck.
|
||||||
|
|
||||||
[[Gunnery Captain]] helps clear up the board. Tokens are just 1 to 2 cost units that grew to absurd sizes. And [[Army of the Sun]] helps in the mirror match, given how common this deck is due to the precon.
|
[[Gunnery Captain]] helps clear up the board. Tokens are just 1 to 2 cost units that grew to absurd sizes.
|
||||||
|
And [[Army of the Sun]] helps in the mirror match, given how common this deck is due to the precon.
|
||||||
@@ -62,11 +62,13 @@ factions:
|
|||||||
deckCode: AUMU65TFOJTGY33XNFXGOICTMVRXK4TJOR4SAQ3POB4QWY3PNZZXI4TVMN2GKZBRACQAAANZAEBZEAI6AQFTSAYCAMOQGBADAEBQCAZ2AMCAGGQDAQBQCAY
|
deckCode: AUMU65TFOJTGY33XNFXGOICTMVRXK4TJOR4SAQ3POB4QWY3PNZZXI4TVMN2GKZBRACQAAANZAEBZEAI6AQFTSAYCAMOQGBADAEBQCAZ2AMCAGGQDAQBQCAY
|
||||||
deckType: custom
|
deckType: custom
|
||||||
---
|
---
|
||||||
|
|
||||||
Built deck as a joke, but it kind of feels nuts in limited testing.
|
Built deck as a joke, but it kind of feels nuts in limited testing.
|
||||||
|
|
||||||
Mulligan everything for [[Radiant Channeling]] and [[Supernova]]. You are hoping that limited removal keeps you alive.
|
Mulligan everything for [[Radiant Channeling]] and [[Supernova]]. You are hoping that limited removal keeps you alive.
|
||||||
|
|
||||||
If they heavy buff something out from your removal range, you might still have enough reserve to [[Throw into the Sun]] that big target.
|
If they heavy buff something out from your removal range, you might still have enough reserve to [[Throw into the Sun]]
|
||||||
|
that big target.
|
||||||
|
|
||||||
If you survive, start scaling your board with [[Awakened Security System]] or [[Limit Breaker]].
|
If you survive, start scaling your board with [[Awakened Security System]] or [[Limit Breaker]].
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
---
|
---
|
||||||
category: Faction
|
category: Faction
|
||||||
---
|
---
|
||||||
|
|
||||||
Concepts:
|
Concepts:
|
||||||
|
|
||||||
- Wide Boards
|
- Wide Boards
|
||||||
- Overpower
|
- Overpower
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
---
|
---
|
||||||
category: Faction
|
category: Faction
|
||||||
---
|
---
|
||||||
|
|
||||||
Concepts:
|
Concepts:
|
||||||
|
|
||||||
- Healing
|
- Healing
|
||||||
-
|
-
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
---
|
---
|
||||||
category: Faction
|
category: Faction
|
||||||
---
|
---
|
||||||
|
|
||||||
Concepts:
|
Concepts:
|
||||||
|
|
||||||
- Activatable Agents
|
- Activatable Agents
|
||||||
-
|
-
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
---
|
---
|
||||||
category: Faction
|
category: Faction
|
||||||
---
|
---
|
||||||
|
|
||||||
Concepts:
|
Concepts:
|
||||||
|
|
||||||
- Discard
|
- Discard
|
||||||
@@ -1,11 +1,14 @@
|
|||||||
---
|
---
|
||||||
category: Faction
|
category: Faction
|
||||||
---
|
---
|
||||||
|
|
||||||
# Energy Reserves
|
# Energy Reserves
|
||||||
|
|
||||||
Care about always having banked energy. Spells can refund themselves when using [[Energy Reserves]], or get stronger is you [[Overflow]] your energy reserve.
|
Care about always having banked energy. Spells can refund themselves when using [[Energy Reserves]], or get stronger is
|
||||||
|
you [[Overflow]] your energy reserve.
|
||||||
|
|
||||||
Concepts:
|
Concepts:
|
||||||
|
|
||||||
- Ramp
|
- Ramp
|
||||||
- Using and Replenishing Reserved Energy
|
- Using and Replenishing Reserved Energy
|
||||||
-
|
-
|
||||||
@@ -17,4 +17,5 @@ archetypes:
|
|||||||
- "[[Keyword/Sprout]]"
|
- "[[Keyword/Sprout]]"
|
||||||
imageLink: "[[Horticulturist Oswald.png]]"
|
imageLink: "[[Horticulturist Oswald.png]]"
|
||||||
---
|
---
|
||||||
|
|
||||||
![[Horticulturist Oswald.png]]
|
![[Horticulturist Oswald.png]]
|
||||||
@@ -17,4 +17,5 @@ archetypes:
|
|||||||
- "[[Rewound]]"
|
- "[[Rewound]]"
|
||||||
imageLink: "[[Violet Inquisitioner]]"
|
imageLink: "[[Violet Inquisitioner]]"
|
||||||
---
|
---
|
||||||
|
|
||||||
![[Violent Inquisitioner.png]]
|
![[Violent Inquisitioner.png]]
|
||||||
@@ -17,4 +17,5 @@ archetypes:
|
|||||||
- N/A
|
- N/A
|
||||||
imageLink: "[[Scoot Sparkles.png]]"
|
imageLink: "[[Scoot Sparkles.png]]"
|
||||||
---
|
---
|
||||||
|
|
||||||
![[Scoot Sparkles.png]]
|
![[Scoot Sparkles.png]]
|
||||||
@@ -17,4 +17,5 @@ archetypes:
|
|||||||
- N/A
|
- N/A
|
||||||
imageLink: "[[The Ripper.png]]"
|
imageLink: "[[The Ripper.png]]"
|
||||||
---
|
---
|
||||||
|
|
||||||
![[The Ripper.png]]
|
![[The Ripper.png]]
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ category: Pass
|
|||||||
---
|
---
|
||||||
|
|
||||||
| Tier | Reward |
|
| Tier | Reward |
|
||||||
| ---- | -------------------------------- |
|
|------|----------------------------------|
|
||||||
| 1 | Sprouts Record |
|
| 1 | Sprouts Record |
|
||||||
| 2 | 120 Core Charges |
|
| 2 | 120 Core Charges |
|
||||||
| 3 | Burn Precon |
|
| 3 | Burn Precon |
|
||||||
@@ -18,5 +18,5 @@ category: Pass
|
|||||||
| 10 | 240 Core Charges, 1 Draft Ticket |
|
| 10 | 240 Core Charges, 1 Draft Ticket |
|
||||||
|
|
||||||
| Core Charges | Flux |
|
| Core Charges | Flux |
|
||||||
| ------------ | ---- |
|
|--------------|------|
|
||||||
| 660 | 75 |
|
| 660 | 75 |
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
---
|
---
|
||||||
category: Quest
|
category: Quest
|
||||||
---
|
---
|
||||||
|
|
||||||
1: 30cc 30xp
|
1: 30cc 30xp
|
||||||
2: 20cc 25xp
|
2: 20cc 25xp
|
||||||
3: 20cc 20xp
|
3: 20cc 20xp
|
||||||
|
|||||||
Reference in New Issue
Block a user