From 8348603b138bd2315b67dda50f232a294d56c44f Mon Sep 17 00:00:00 2001 From: Brian Bicknell Date: Sun, 22 Feb 2026 01:56:04 -0500 Subject: [PATCH] Added voice work image fallback. Added tag/creator/circle chip components. Updated voice work search response to include favorite/blacklisted flags for tags/creators/circles. --- .../Queries/Search/VoiceWorkSearchResult.cs | 13 ++ .../VoiceWorks/VoiceWorkSearchProvider.cs | 22 ++- JSMR.UI.Blazor/Components/Chip.razor | 94 ++++++++++++- .../Components/Chips/CircleChip.razor | 43 ++++++ .../Components/Chips/CreatorChip.razor | 43 ++++++ JSMR.UI.Blazor/Components/Chips/TagChip.razor | 54 ++++++++ JSMR.UI.Blazor/Components/JImage.razor | 28 +++- JSMR.UI.Blazor/Components/JProduct.razor | 11 +- JSMR.UI.Blazor/Components/ProductTag.razor | 25 +++- JSMR.UI.Blazor/Enums/ColorVarient.cs | 3 + JSMR.UI.Blazor/Enums/ElementVarient.cs | 8 ++ JSMR.UI.Blazor/Enums/ImageExtension.cs | 7 + JSMR.UI.Blazor/Enums/ToneVarient.cs | 8 ++ JSMR.UI.Blazor/Services/ImageUrlProvider.cs | 53 +++++++- JSMR.UI.Blazor/wwwroot/css/app.css | 127 +++++++++++++++++- JSMR.UI.Blazor/wwwroot/css/theme-frozen.css | 5 + .../images/web/home/not_found_img_main.png | Bin 0 -> 6950 bytes 17 files changed, 521 insertions(+), 23 deletions(-) create mode 100644 JSMR.UI.Blazor/Components/Chips/CircleChip.razor create mode 100644 JSMR.UI.Blazor/Components/Chips/CreatorChip.razor create mode 100644 JSMR.UI.Blazor/Components/Chips/TagChip.razor create mode 100644 JSMR.UI.Blazor/Enums/ElementVarient.cs create mode 100644 JSMR.UI.Blazor/Enums/ImageExtension.cs create mode 100644 JSMR.UI.Blazor/Enums/ToneVarient.cs create mode 100644 JSMR.UI.Blazor/wwwroot/images/web/home/not_found_img_main.png diff --git a/JSMR.Application/VoiceWorks/Queries/Search/VoiceWorkSearchResult.cs b/JSMR.Application/VoiceWorks/Queries/Search/VoiceWorkSearchResult.cs index d322666..1fc2371 100644 --- a/JSMR.Application/VoiceWorks/Queries/Search/VoiceWorkSearchResult.cs +++ b/JSMR.Application/VoiceWorks/Queries/Search/VoiceWorkSearchResult.cs @@ -27,18 +27,31 @@ public record VoiceWorkSearchResult public byte Status { get; init; } public byte SubtitleLanguage { get; init; } public bool? IsValid { get; init; } + public required VoiceWorkCircleItem Circle { get; set; } public VoiceWorkTagItem[] Tags { get; set; } = []; public VoiceWorkCreatorItem[] Creators { get; set; } = []; } +public class VoiceWorkCircleItem +{ + public required string Name { get; init; } + public required string MakerId { get; init; } + public bool IsFavorite { get; init; } + public bool IsBlacklisted { get; init; } +} + public class VoiceWorkTagItem { public int TagId { get; set; } public required string Name { get; set; } + public bool IsFavorite { get; set; } + public bool IsBlacklisted { get; set; } } public class VoiceWorkCreatorItem { public int CreatorId { get; set; } public required string Name { get; set; } + public bool IsFavorite { get; set; } + public bool IsBlacklisted { get; set; } } \ No newline at end of file diff --git a/JSMR.Infrastructure/Data/Repositories/VoiceWorks/VoiceWorkSearchProvider.cs b/JSMR.Infrastructure/Data/Repositories/VoiceWorks/VoiceWorkSearchProvider.cs index e641da0..dd20a04 100644 --- a/JSMR.Infrastructure/Data/Repositories/VoiceWorks/VoiceWorkSearchProvider.cs +++ b/JSMR.Infrastructure/Data/Repositories/VoiceWorks/VoiceWorkSearchProvider.cs @@ -376,6 +376,13 @@ public class VoiceWorkSearchProvider(AppDbContext context, IVoiceWorkFullTextSea HasImage = voiceWork.HasImage, Maker = circle.Name, MakerId = circle.MakerId, + Circle = new() + { + Name = circle.Name, + MakerId = circle.MakerId, + IsFavorite = circle.Favorite, + IsBlacklisted = circle.Blacklisted + }, ExpectedDate = voiceWork.ExpectedDate, SalesDate = voiceWork.SalesDate, PlannedReleaseDate = voiceWork.PlannedReleaseDate, @@ -413,6 +420,13 @@ public class VoiceWorkSearchProvider(AppDbContext context, IVoiceWorkFullTextSea HasImage = voiceWork.HasImage, Maker = circle.Name, MakerId = circle.MakerId, + Circle = new() + { + Name = circle.Name, + MakerId = circle.MakerId, + IsFavorite = circle.Favorite, + IsBlacklisted = circle.Blacklisted + }, ExpectedDate = voiceWork.ExpectedDate, SalesDate = voiceWork.SalesDate, PlannedReleaseDate = voiceWork.PlannedReleaseDate, @@ -457,14 +471,14 @@ public class VoiceWorkSearchProvider(AppDbContext context, IVoiceWorkFullTextSea from englishTag in et.DefaultIfEmpty() where voiceWorkIds.Contains(voiceWorkTag.VoiceWorkId) orderby voiceWorkTag.VoiceWorkId, voiceWorkTag.Position - select new { voiceWorkTag.VoiceWorkId, voiceWorkTag.TagId, tag.Name, EnglishName = englishTag.Name } + select new { voiceWorkTag.VoiceWorkId, voiceWorkTag.TagId, tag.Name, EnglishName = englishTag.Name, IsFavorite = tag.Favorite, IsBlacklisted = tag.Blacklisted } ).ToListAsync(cancellationToken); return tagRows .GroupBy(r => r.VoiceWorkId) .ToDictionary( g => g.Key, - g => g.Select(r => new VoiceWorkTagItem { TagId = r.TagId, Name = r.EnglishName ?? r.Name }).ToArray() + g => g.Select(r => new VoiceWorkTagItem { TagId = r.TagId, Name = r.EnglishName ?? r.Name, IsFavorite = r.IsFavorite, IsBlacklisted = r.IsBlacklisted }).ToArray() ); } @@ -475,14 +489,14 @@ public class VoiceWorkSearchProvider(AppDbContext context, IVoiceWorkFullTextSea join creator in context.Creators.AsNoTracking() on voiceWorkCreator.CreatorId equals creator.CreatorId where voiceWorkIds.Contains(voiceWorkCreator.VoiceWorkId) orderby voiceWorkCreator.VoiceWorkId, voiceWorkCreator.Position - select new { voiceWorkCreator.VoiceWorkId, creator.CreatorId, creator.Name } + select new { voiceWorkCreator.VoiceWorkId, creator.CreatorId, creator.Name, creator.Favorite, creator.Blacklisted } ).ToListAsync(cancellationToken); return creatorRows .GroupBy(r => r.VoiceWorkId) .ToDictionary( g => g.Key, - g => g.Select(r => new VoiceWorkCreatorItem { CreatorId = r.CreatorId, Name = r.Name }).ToArray() + g => g.Select(r => new VoiceWorkCreatorItem { CreatorId = r.CreatorId, Name = r.Name, IsFavorite = r.Favorite, IsBlacklisted = r.Blacklisted }).ToArray() ); } } \ No newline at end of file diff --git a/JSMR.UI.Blazor/Components/Chip.razor b/JSMR.UI.Blazor/Components/Chip.razor index 8450ad0..17d8d27 100644 --- a/JSMR.UI.Blazor/Components/Chip.razor +++ b/JSMR.UI.Blazor/Components/Chip.razor @@ -1,12 +1,26 @@ @using JSMR.UI.Blazor.Enums -
- @if (Graphic != null) - { - - } - @ChildContent -
+@if (string.IsNullOrWhiteSpace(Url)) +{ +
+ @if (Graphic != null) + { + + } + @ChildContent +
+} +else +{ + + @if (Graphic != null) + { + + } + @ChildContent + +} + @code { [Parameter] @@ -15,6 +29,72 @@ [Parameter] public Graphic? Graphic { get; set; } + [Parameter] + public IconVarient? IconVarient { get; set; } + [Parameter] public ColorVarient Color { get; set; } = ColorVarient.Primary; + + [Parameter] + public ElementVarient Varient { get; set; } = ElementVarient.None; + + [Parameter] + public ToneVarient Tone { get; set; } = ToneVarient.None; + + [Parameter] + public string? Url { get; set; } + + [Parameter] + public string? Target { get; set; } + + [Parameter] + public EventCallback Click { get; set; } + + private string GetClasses() + { + string color = Color.ToString().ToLower(); + + List classNames = + [ + $"j-chip", + $"color-{color}" + ]; + + switch (Varient) + { + case ElementVarient.Filled: + classNames.Add($"varient-filled"); + //classNames.Add($"background-color-{color}"); + break; + case ElementVarient.Outlined: + classNames.Add($"varient-outlined"); + //classNames.Add($"border-color-{color}"); + break; + } + + switch (Tone) + { + case ToneVarient.Solid: + classNames.Add($"tone-solid"); + break; + case ToneVarient.Tint: + classNames.Add($"tone-tint"); + break; + } + + if (Click.HasDelegate || string.IsNullOrWhiteSpace(Url) == false) + { + classNames.Add("is-clickable"); + } + + return string.Join(" ", classNames); + } + + private async Task OnClickAsync() + { + if (Click.HasDelegate) + { + await Click.InvokeAsync(); + } + } } \ No newline at end of file diff --git a/JSMR.UI.Blazor/Components/Chips/CircleChip.razor b/JSMR.UI.Blazor/Components/Chips/CircleChip.razor new file mode 100644 index 0000000..97879e2 --- /dev/null +++ b/JSMR.UI.Blazor/Components/Chips/CircleChip.razor @@ -0,0 +1,43 @@ +@using JSMR.Application.Tags.Queries.Search.Contracts +@using JSMR.Application.VoiceWorks.Queries.Search +@using JSMR.UI.Blazor.Enums +@using JSMR.UI.Blazor.Filters +@using JSMR.UI.Blazor.Services +@using Microsoft.AspNetCore.WebUtilities + +@Circle.Name + +@code { + [Parameter] + public required VoiceWorkCircleItem Circle { get; set; } + + private string GetUrl() + { + return $"https://www.dlsite.com/maniax/circle/profile/=/maker_id/{Circle.MakerId}.html"; + } + + private ColorVarient GetColor() + { + if (Circle.IsFavorite) + { + return ColorVarient.Mint; + } + + if (Circle.IsBlacklisted) + { + return ColorVarient.Pink; + } + + return ColorVarient.Secondary; + } + + private ToneVarient GetTone() + { + if (Circle.IsFavorite || Circle.IsBlacklisted) + { + return ToneVarient.Tint; + } + + return ToneVarient.None; + } +} \ No newline at end of file diff --git a/JSMR.UI.Blazor/Components/Chips/CreatorChip.razor b/JSMR.UI.Blazor/Components/Chips/CreatorChip.razor new file mode 100644 index 0000000..22e7789 --- /dev/null +++ b/JSMR.UI.Blazor/Components/Chips/CreatorChip.razor @@ -0,0 +1,43 @@ +@using JSMR.Application.Tags.Queries.Search.Contracts +@using JSMR.Application.VoiceWorks.Queries.Search +@using JSMR.UI.Blazor.Enums +@using JSMR.UI.Blazor.Filters +@using JSMR.UI.Blazor.Services +@using Microsoft.AspNetCore.WebUtilities + +@Creator.Name + +@code { + [Parameter] + public required VoiceWorkCreatorItem Creator { get; set; } + + private string GetUrl() + { + return $"https://www.dlsite.com/maniax/fsr/=/keyword_creater/{Creator.Name}"; + } + + private ColorVarient GetColor() + { + if (Creator.IsFavorite) + { + return ColorVarient.Mint; + } + + if (Creator.IsBlacklisted) + { + return ColorVarient.Pink; + } + + return ColorVarient.Secondary; + } + + private ToneVarient GetTone() + { + if (Creator.IsFavorite || Creator.IsBlacklisted) + { + return ToneVarient.Tint; + } + + return ToneVarient.None; + } +} \ No newline at end of file diff --git a/JSMR.UI.Blazor/Components/Chips/TagChip.razor b/JSMR.UI.Blazor/Components/Chips/TagChip.razor new file mode 100644 index 0000000..beaa13e --- /dev/null +++ b/JSMR.UI.Blazor/Components/Chips/TagChip.razor @@ -0,0 +1,54 @@ +@using JSMR.Application.Tags.Queries.Search.Contracts +@using JSMR.Application.VoiceWorks.Queries.Search +@using JSMR.UI.Blazor.Enums +@using JSMR.UI.Blazor.Filters +@using JSMR.UI.Blazor.Services +@using Microsoft.AspNetCore.WebUtilities + +@Tag.Name + +@code { + [Inject] + protected NavigationManager NavigationManager { get; set; } = default!; + + [Parameter] + public required VoiceWorkTagItem Tag { get; set; } + + private ColorVarient GetColor() + { + if (Tag.IsFavorite) + { + return ColorVarient.Mint; + } + + if (Tag.IsBlacklisted) + { + return ColorVarient.Pink; + } + + return ColorVarient.Secondary; + } + + private ToneVarient GetTone() + { + if (Tag.IsFavorite || Tag.IsBlacklisted) + { + return ToneVarient.Tint; + } + + return ToneVarient.None; + } + + private void OnClick() + { + VoiceWorkFilterState state = new() + { + TagIds = [Tag.TagId] + }; + + string basePath = new Uri(NavigationManager.Uri).GetLeftPart(UriPartial.Authority); + string uri = QueryHelpers.AddQueryString($"{basePath}/voiceworks", state.ToQuery()); + + NavigationManager.NavigateTo(uri); + } +} \ No newline at end of file diff --git a/JSMR.UI.Blazor/Components/JImage.razor b/JSMR.UI.Blazor/Components/JImage.razor index 8653961..0d32b84 100644 --- a/JSMR.UI.Blazor/Components/JImage.razor +++ b/JSMR.UI.Blazor/Components/JImage.razor @@ -1,6 +1,6 @@ 
- +
@code { @@ -8,7 +8,7 @@ public required string Source { get; set; } [Parameter] - public string FallbackSource { get; set; } = "images/home/no_img_main.gif"; + public string FallbackSource { get; set; } = "images/web/home/not_found_img_main.png"; // "images/web/home/no_img_main.gif"; [Parameter] public bool LazyLoading { get; set; } = true; @@ -22,6 +22,10 @@ [Parameter] public string? ImageClass { get; set; } + private string? currentSource; + private bool hasSourceErrored = false; + private bool hasFallbackSourceErrored = false; + private bool _isLoaded; private string? _lastSource; @@ -33,6 +37,10 @@ protected override void OnParametersSet() { + currentSource = Source; + hasSourceErrored = false; + hasFallbackSourceErrored = false; + if (!string.Equals(_lastSource, Source, StringComparison.Ordinal)) { _lastSource = Source; @@ -103,4 +111,20 @@ { _isLoaded = true; } + + private void OnImageError() + { + if (!hasSourceErrored && !string.IsNullOrEmpty(FallbackSource)) + { + hasSourceErrored = true; + currentSource = FallbackSource; + StateHasChanged(); + } + else if (!hasFallbackSourceErrored) + { + hasFallbackSourceErrored = true; + currentSource = "images/web/home/not_found_img_main.png"; + StateHasChanged(); + } + } } \ No newline at end of file diff --git a/JSMR.UI.Blazor/Components/JProduct.razor b/JSMR.UI.Blazor/Components/JProduct.razor index fa04a23..6447ba5 100644 --- a/JSMR.UI.Blazor/Components/JProduct.razor +++ b/JSMR.UI.Blazor/Components/JProduct.razor @@ -1,5 +1,6 @@ @using JSMR.Application.VoiceWorks.Queries.Search @using JSMR.Domain.Enums +@using JSMR.UI.Blazor.Components.Chips @using JSMR.UI.Blazor.Enums @using JSMR.UI.Blazor.Filters @using JSMR.UI.Blazor.Services @@ -8,7 +9,7 @@
- +
@@ -21,6 +22,7 @@ Target="_blank" Variant="MudBlazor.Variant.Filled" Icon="@Icons.Material.Outlined.Circle">@Product.Maker + @* *@ @foreach (var creator in Product.Creators) { @creator.Name + @* *@ }
@@ -39,6 +42,12 @@ }
+
+ @foreach (var tag in Product.Tags) + { + @* *@ + } +
diff --git a/JSMR.UI.Blazor/Components/ProductTag.razor b/JSMR.UI.Blazor/Components/ProductTag.razor index 593a4b9..35246c7 100644 --- a/JSMR.UI.Blazor/Components/ProductTag.razor +++ b/JSMR.UI.Blazor/Components/ProductTag.razor @@ -1,8 +1,10 @@ -@using JSMR.Application.VoiceWorks.Queries.Search +@using JSMR.Application.Tags.Queries.Search.Contracts +@using JSMR.Application.VoiceWorks.Queries.Search @using JSMR.UI.Blazor.Filters +@using JSMR.UI.Blazor.Services @using Microsoft.AspNetCore.WebUtilities -@Tag.Name +@Tag.Name @* @Tag.Name *@ @code { @@ -12,6 +14,25 @@ [Parameter] public required VoiceWorkTagItem Tag { get; set; } + private string Classes => GetClasses(); + + private string GetClasses() + { + List classNames = ["j-tag", "j-tag-2"]; + + if (Tag.IsFavorite) + { + classNames.Add("j-tag-favorite"); + } + + if (Tag.IsBlacklisted) + { + classNames.Add("j-tag-blacklisted"); + } + + return string.Join(" ", classNames); + } + private void OnClick() { VoiceWorkFilterState state = new() diff --git a/JSMR.UI.Blazor/Enums/ColorVarient.cs b/JSMR.UI.Blazor/Enums/ColorVarient.cs index f59221e..085b9e2 100644 --- a/JSMR.UI.Blazor/Enums/ColorVarient.cs +++ b/JSMR.UI.Blazor/Enums/ColorVarient.cs @@ -16,6 +16,7 @@ public enum ColorVarient Black, Yellow, Green, + Mint, Teal, Blue, Orange, @@ -35,6 +36,7 @@ public static class CssUtil ColorVarient.SurfaceContainerOutlineLow => "surface-container-outline-low", ColorVarient.Yellow => "text-yellow", ColorVarient.Green => "text-green", + ColorVarient.Mint => "text-mint", ColorVarient.Teal => "text-teal", ColorVarient.Blue => "text-blue", ColorVarient.Orange => "text-orange", @@ -50,6 +52,7 @@ public static class CssUtil ColorVarient.SurfaceContainerLow => "surface-container-low", ColorVarient.Yellow => "text-yellow", ColorVarient.Green => "text-green", + ColorVarient.Mint => "text-mint", ColorVarient.Teal => "text-teal", ColorVarient.Blue => "text-blue", ColorVarient.Orange => "text-orange", diff --git a/JSMR.UI.Blazor/Enums/ElementVarient.cs b/JSMR.UI.Blazor/Enums/ElementVarient.cs new file mode 100644 index 0000000..3327030 --- /dev/null +++ b/JSMR.UI.Blazor/Enums/ElementVarient.cs @@ -0,0 +1,8 @@ +namespace JSMR.UI.Blazor.Enums; + +public enum ElementVarient +{ + None = 0, + Filled = 1, + Outlined = 2 +} \ No newline at end of file diff --git a/JSMR.UI.Blazor/Enums/ImageExtension.cs b/JSMR.UI.Blazor/Enums/ImageExtension.cs new file mode 100644 index 0000000..4e3af0d --- /dev/null +++ b/JSMR.UI.Blazor/Enums/ImageExtension.cs @@ -0,0 +1,7 @@ +namespace JSMR.UI.Blazor.Enums; + +public enum ImageExtension +{ + Jpeg, + WebP +} \ No newline at end of file diff --git a/JSMR.UI.Blazor/Enums/ToneVarient.cs b/JSMR.UI.Blazor/Enums/ToneVarient.cs new file mode 100644 index 0000000..d0bef80 --- /dev/null +++ b/JSMR.UI.Blazor/Enums/ToneVarient.cs @@ -0,0 +1,8 @@ +namespace JSMR.UI.Blazor.Enums; + +public enum ToneVarient +{ + None = 0, + Tint = 1, + Solid = 2 +} \ No newline at end of file diff --git a/JSMR.UI.Blazor/Services/ImageUrlProvider.cs b/JSMR.UI.Blazor/Services/ImageUrlProvider.cs index 38a92b4..9804ee6 100644 --- a/JSMR.UI.Blazor/Services/ImageUrlProvider.cs +++ b/JSMR.UI.Blazor/Services/ImageUrlProvider.cs @@ -1,15 +1,21 @@ using JSMR.Application.VoiceWorks.Queries.Search; +using JSMR.UI.Blazor.Enums; namespace JSMR.UI.Blazor.Services; public static class ImageUrlProvider { - public static string GetImageUrl(VoiceWorkSearchResult voiceWork, string size) + public static string GetImageUrl(VoiceWorkSearchResult voiceWork, string size, string extension = "jpg") { - return GetImageURL(voiceWork.OriginalProductId ?? voiceWork.ProductId, voiceWork.HasImage, voiceWork.SalesDate, size); + return GetImageURL(voiceWork.OriginalProductId ?? voiceWork.ProductId, voiceWork.HasImage, voiceWork.SalesDate, size, extension); } - public static string GetImageURL(string? productId, bool hasImage, DateTime? salesDate, string size) + public static string GetImageUrl(VoiceWorkSearchResult voiceWork, ImageSize imageSize, ImageExtension imageExtension) + { + return GetImageUrl(voiceWork.OriginalProductId ?? voiceWork.ProductId, voiceWork.HasImage, voiceWork.SalesDate.HasValue, imageSize, imageExtension); + } + + public static string GetImageURL(string? productId, bool hasImage, DateTime? salesDate, string size, string extension = "jpg") { string folder = "modpub"; string imageSize = "main"; @@ -46,7 +52,7 @@ public static class ImageUrlProvider return noImageUrl; } - var imageUrlTemplate = "//img.dlsite.jp/[folder]/images2/[imageType1]/[imageWorkType]/[fullRoundedProductId]/[productId][imageType2]_img_[imageSize].jpg"; + var imageUrlTemplate = $"//img.dlsite.jp/[folder]/images2/[imageType1]/[imageWorkType]/[fullRoundedProductId]/[productId][imageType2]_img_[imageSize].{extension}"; string productIdWithoutPrefixString = productId.Substring(2); int productIdWithoutPrefix = Convert.ToInt32(productId.Substring(2)); @@ -78,4 +84,43 @@ public static class ImageUrlProvider return imageUrl; } + + public static string GetImageUrl(string? productId, bool hasImage, bool isOnSale, ImageSize size, ImageExtension extension) + { + string imageWorkType = productId != null ? productId.StartsWith("RJ") ? "doujin" : "professional" : "doujin"; + (string imageSize, string folder) = GetImageSizeAndFolder(size, hasImage); + + if (hasImage == false || productId == null) + { + return $"/images/web/home/no_img_{imageSize}.gif"; + } + + string productIdWithoutPrefixString = productId.Substring(2); + int productIdWithoutPrefix = Convert.ToInt32(productId.Substring(2)); + + string productIdPrefix = productId[..2]; + + int roundedProductId = (int)Math.Round(Math.Ceiling((double)productIdWithoutPrefix / 1000) * 1000); + + int productIdWithPrefixStringLength = productIdWithoutPrefixString.Length; + int zeroPadLength = productIdWithPrefixStringLength - roundedProductId.ToString().Length; + + var fullRoundedProductId = productIdPrefix.PadRight(productIdPrefix.Length + zeroPadLength, '0') + roundedProductId; + + string imageType1 = isOnSale ? "work" : "ana"; + string imageType2 = isOnSale ? "" : "_ana"; + + return $"//img.dlsite.jp/{folder}/images2/{imageType1}/{imageWorkType}/{fullRoundedProductId}/{productId}{imageType2}_img_{imageSize}.{extension}"; + } + + private static (string, string) GetImageSizeAndFolder(ImageSize imageSize, bool hasImage) + { + return imageSize switch + { + ImageSize.Thumb100 => ("sam", "modpub"), + ImageSize.Square240 => (hasImage ? "main_240x240" : "main", "resize"), + ImageSize.Square300 => (hasImage ? "main_300x300" : "main", "resize"), + _ => ("main", "modpub"), + }; + } } \ No newline at end of file diff --git a/JSMR.UI.Blazor/wwwroot/css/app.css b/JSMR.UI.Blazor/wwwroot/css/app.css index f3fda9e..4569813 100644 --- a/JSMR.UI.Blazor/wwwroot/css/app.css +++ b/JSMR.UI.Blazor/wwwroot/css/app.css @@ -608,15 +608,90 @@ code { font-weight: 500;*/ } -/* Chips */ -.j-chip { +.j-tag-2 { + padding: .4rem .8rem; + font-size: .8rem; + border-color: var(--tag-text-color); + border-width: 1px; + border-style: solid; + background-color: transparent; + border-radius: 1rem; display: flex; align-items: center; - gap: .5rem; + gap: .3rem; +} + +.j-tag-favorite { + border: 1px solid rgb(167, 243, 208); + color: rgb(167, 243, 208); + background-color: rgba(67, 243, 208, .1); +} + + .j-tag-favorite > .j-icon-tag { + background-color: #a7f3d0; + } + +.j-tag-blacklisted { + border: 1px solid rgb(254, 205, 211); + color: rgb(254, 205, 211); + background-color: rgba(254, 205, 211, .1); + color: #ea9ab2; + border: 1px solid #ea9ab2; + background-color: #ea9ab222; + color: rgb(243, 167, 167); + border: 1px solid rgb(243, 167, 167); + background-color: rgba(243, 167, 167, .1); +} + +/* Chips */ +.j-chip { + display: inline-flex; + align-items: center; + gap: .4rem; font-size: 1rem; font-weight: 500; + font-size: .8rem; + font-weight: 400; + --chip-rgb: var(--secondary-rgb, 148 163 184); + --chip-tint-alpha: 0.12; } + .j-chip.is-clickable { + cursor: pointer; + user-select: none; + } + + .j-chip.varient-filled { + padding: .4rem .8rem; + border-radius: 1rem; + } + + .j-chip.varient-outlined { + border-width: 1px; + border-style: solid; + padding: .4rem .8rem; + border-radius: 1rem; + border-color: rgb(var(--chip-rgb)); + color: rgb(var(--chip-rgb)); + background: transparent; + } + + .j-chip.tone-tint { + background: rgb(var(--chip-rgb) / var(--chip-tint-alpha)); + } + + .j-chip.color-mint { + --chip-rgb: var(--rgb-mint); + } + + .j-chip.color-green { + --chip-rgb: var(--rgb-green); + } + + .j-chip.color-teal { + --chip-rgb: var(--rgb-teal); + } + /* Icons */ .j-icon { mask-repeat: no-repeat; @@ -705,6 +780,10 @@ code { mask-image: url("../svg/person.svg"); } +.j-icon-person-fill { + mask-image: url("../svg/person-fill.svg"); +} + .j-icon-sort { mask-image: url("../svg/sort.svg"); } @@ -741,6 +820,39 @@ code { background-image: url("../svg/flag-kr.svg"); } +/* Colors */ +.color-black { + color: var(--color-black); +} + +.color-yellow { + color: var(--color-yellow); +} + +.color-green { + color: var(--color-green); +} + +.color-mint { + color: var(--color-green); +} + +.color-teal { + color: var(--color-teal); +} + +.color-blue { + color: var(--color-blue); +} + +.color-orange { + color: var(--color-orange); +} + +.color-pink { + color: var(--color-pink); +} + /* Border Colors */ .border-color-black { border-color: var(--color-black); @@ -754,6 +866,11 @@ code { border-color: var(--color-green); } +.border-color-mint { + border-color: var(--color-mint); +} + + .border-color-teal { border-color: var(--color-teal); } @@ -799,6 +916,10 @@ code { background-color: var(--color-green); } +.background-color-mint { + background-color: var(--color-mint); +} + .background-color-teal { background-color: var(--color-teal); } diff --git a/JSMR.UI.Blazor/wwwroot/css/theme-frozen.css b/JSMR.UI.Blazor/wwwroot/css/theme-frozen.css index 8642a38..2b5b2b6 100644 --- a/JSMR.UI.Blazor/wwwroot/css/theme-frozen.css +++ b/JSMR.UI.Blazor/wwwroot/css/theme-frozen.css @@ -39,12 +39,17 @@ --input-background-color: rgb(0,20,34); --input-focus-border-color: #64b5f6; --input-focus-box-shadow: 0 0 0 1px #93cbf9; + /* RGB Tokens */ + --rgb-mint: 167 243 208; + --rgb-green: 175 224 125; + --rgb-teal: 110 236 255; /* Colors */ --color-primary: rgb(180,200, 214); --color-secondary: rgb(200,220,234); --color-black: #272727; --color-yellow: #ffe073; --color-green: #afe07d; + --color-mint: #a7f3d0; --color-teal: #6eecff; --color-blue: #73c4ff; --color-orange: #ffa773; diff --git a/JSMR.UI.Blazor/wwwroot/images/web/home/not_found_img_main.png b/JSMR.UI.Blazor/wwwroot/images/web/home/not_found_img_main.png new file mode 100644 index 0000000000000000000000000000000000000000..e3933d03171a56bf3fc53117c8cee0abdb2e48f0 GIT binary patch literal 6950 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 P2>i~THak^efWP@a_4O#0 literal 0 HcmV?d00001