💥 Add /api prefix for json endpoints with redirect

This commit is contained in:
2025-07-10 14:18:02 +08:00
parent 1f2cdb146d
commit fc6edd7378
43 changed files with 217 additions and 41 deletions

View File

@ -11,7 +11,7 @@ using System.Collections.Generic;
namespace DysonNetwork.Sphere.Account; namespace DysonNetwork.Sphere.Account;
[ApiController] [ApiController]
[Route("/accounts")] [Route("/api/accounts")]
public class AccountController( public class AccountController(
AppDatabase db, AppDatabase db,
AuthService auth, AuthService auth,

View File

@ -12,7 +12,7 @@ namespace DysonNetwork.Sphere.Account;
[Authorize] [Authorize]
[ApiController] [ApiController]
[Route("/accounts/me")] [Route("/api/accounts/me")]
public class AccountCurrentController( public class AccountCurrentController(
AppDatabase db, AppDatabase db,
AccountService accounts, AccountService accounts,

View File

@ -3,7 +3,7 @@ using Microsoft.AspNetCore.Mvc;
namespace DysonNetwork.Sphere.Account; namespace DysonNetwork.Sphere.Account;
[ApiController] [ApiController]
[Route("/spells")] [Route("/api/spells")]
public class MagicSpellController(AppDatabase db, MagicSpellService sp) : ControllerBase public class MagicSpellController(AppDatabase db, MagicSpellService sp) : ControllerBase
{ {
[HttpPost("{spellId:guid}/resend")] [HttpPost("{spellId:guid}/resend")]

View File

@ -9,7 +9,7 @@ using NodaTime;
namespace DysonNetwork.Sphere.Account; namespace DysonNetwork.Sphere.Account;
[ApiController] [ApiController]
[Route("/notifications")] [Route("/api/notifications")]
public class NotificationController(AppDatabase db, NotificationService nty) : ControllerBase public class NotificationController(AppDatabase db, NotificationService nty) : ControllerBase
{ {
[HttpGet("count")] [HttpGet("count")]

View File

@ -7,7 +7,7 @@ using NodaTime;
namespace DysonNetwork.Sphere.Account; namespace DysonNetwork.Sphere.Account;
[ApiController] [ApiController]
[Route("/relationships")] [Route("/api/relationships")]
public class RelationshipController(AppDatabase db, RelationshipService rels) : ControllerBase public class RelationshipController(AppDatabase db, RelationshipService rels) : ControllerBase
{ {
[HttpGet] [HttpGet]

View File

@ -8,7 +8,7 @@ namespace DysonNetwork.Sphere.Activity;
/// Activity is a universal feed that contains multiple kinds of data. Personalized and generated dynamically. /// Activity is a universal feed that contains multiple kinds of data. Personalized and generated dynamically.
/// </summary> /// </summary>
[ApiController] [ApiController]
[Route("/activities")] [Route("/api/activities")]
public class ActivityController( public class ActivityController(
ActivityService acts ActivityService acts
) : ControllerBase ) : ControllerBase

View File

@ -11,7 +11,7 @@ using DysonNetwork.Sphere.Connection;
namespace DysonNetwork.Sphere.Auth; namespace DysonNetwork.Sphere.Auth;
[ApiController] [ApiController]
[Route("/auth")] [Route("/api/auth")]
public class AuthController( public class AuthController(
AppDatabase db, AppDatabase db,
AccountService accounts, AccountService accounts,

View File

@ -14,7 +14,7 @@ using NodaTime;
namespace DysonNetwork.Sphere.Auth.OidcProvider.Controllers; namespace DysonNetwork.Sphere.Auth.OidcProvider.Controllers;
[Route("/auth/open")] [Route("/api/auth/open")]
[ApiController] [ApiController]
public class OidcProviderController( public class OidcProviderController(
AppDatabase db, AppDatabase db,

View File

@ -8,7 +8,7 @@ using NodaTime;
namespace DysonNetwork.Sphere.Auth.OpenId; namespace DysonNetwork.Sphere.Auth.OpenId;
[ApiController] [ApiController]
[Route("/accounts/me/connections")] [Route("/api/accounts/me/connections")]
[Authorize] [Authorize]
public class ConnectionController( public class ConnectionController(
AppDatabase db, AppDatabase db,
@ -164,7 +164,7 @@ public class ConnectionController(
} }
[AllowAnonymous] [AllowAnonymous]
[Route("/auth/callback/{provider}")] [Route("/api/auth/callback/{provider}")]
[HttpGet, HttpPost] [HttpGet, HttpPost]
public async Task<IActionResult> HandleCallback([FromRoute] string provider) public async Task<IActionResult> HandleCallback([FromRoute] string provider)
{ {

View File

@ -8,7 +8,7 @@ using NodaTime;
namespace DysonNetwork.Sphere.Auth.OpenId; namespace DysonNetwork.Sphere.Auth.OpenId;
[ApiController] [ApiController]
[Route("/auth/login")] [Route("/api/auth/login")]
public class OidcController( public class OidcController(
IServiceProvider serviceProvider, IServiceProvider serviceProvider,
AppDatabase db, AppDatabase db,

View File

@ -9,7 +9,7 @@ using Microsoft.EntityFrameworkCore;
namespace DysonNetwork.Sphere.Chat; namespace DysonNetwork.Sphere.Chat;
[ApiController] [ApiController]
[Route("/chat")] [Route("/api/chat")]
public partial class ChatController(AppDatabase db, ChatService cs, ChatRoomService crs) : ControllerBase public partial class ChatController(AppDatabase db, ChatService cs, ChatRoomService crs) : ControllerBase
{ {
public class MarkMessageReadRequest public class MarkMessageReadRequest

View File

@ -13,7 +13,7 @@ using NodaTime;
namespace DysonNetwork.Sphere.Chat; namespace DysonNetwork.Sphere.Chat;
[ApiController] [ApiController]
[Route("/chat")] [Route("/api/chat")]
public class ChatRoomController( public class ChatRoomController(
AppDatabase db, AppDatabase db,
FileReferenceService fileRefService, FileReferenceService fileRefService,

View File

@ -13,7 +13,7 @@ public class RealtimeChatConfiguration
} }
[ApiController] [ApiController]
[Route("/chat/realtime")] [Route("/api/chat/realtime")]
public class RealtimeCallController( public class RealtimeCallController(
IConfiguration configuration, IConfiguration configuration,
AppDatabase db, AppDatabase db,

View File

@ -0,0 +1,44 @@
using Azure.Core;
namespace DysonNetwork.Sphere.Connection;
public class ClientTypeMiddleware(RequestDelegate next)
{
public async Task Invoke(HttpContext context)
{
var headers = context.Request.Headers;
bool isWebPage;
// Priority 1: Check for custom header
if (headers.TryGetValue("X-Client", out var clientType))
{
isWebPage = clientType.ToString().Length == 0;
}
else
{
var userAgent = headers["User-Agent"].ToString();
var accept = headers["Accept"].ToString();
// Priority 2: Check known app User-Agent (backward compatibility)
if (!string.IsNullOrEmpty(userAgent) && userAgent.Contains("Solian"))
isWebPage = false;
// Priority 3: Accept header can help infer intent
else if (!string.IsNullOrEmpty(accept) && accept.Contains("text/html"))
isWebPage = true;
else if (!string.IsNullOrEmpty(accept) && accept.Contains("application/json"))
isWebPage = false;
else
isWebPage = true;
}
context.Items["IsWebPage"] = isWebPage;
if (!isWebPage && !context.Request.Path.StartsWithSegments("/api"))
context.Response.Redirect(
"/api" + context.Request.Path.Value,
permanent: false
);
else
await next(context);
}
}

View File

@ -4,7 +4,7 @@ using Microsoft.EntityFrameworkCore;
namespace DysonNetwork.Sphere.Connection.WebReader; namespace DysonNetwork.Sphere.Connection.WebReader;
[ApiController] [ApiController]
[Route("/feeds/articles")] [Route("/api/feeds/articles")]
public class WebArticleController(AppDatabase db) : ControllerBase public class WebArticleController(AppDatabase db) : ControllerBase
{ {
/// <summary> /// <summary>

View File

@ -7,7 +7,7 @@ namespace DysonNetwork.Sphere.Connection.WebReader;
[Authorize] [Authorize]
[ApiController] [ApiController]
[Route("/publishers/{pubName}/feeds")] [Route("/api/publishers/{pubName}/feeds")]
public class WebFeedController(WebFeedService webFeed, PublisherService ps) : ControllerBase public class WebFeedController(WebFeedService webFeed, PublisherService ps) : ControllerBase
{ {
public record WebFeedRequest( public record WebFeedRequest(

View File

@ -9,7 +9,7 @@ namespace DysonNetwork.Sphere.Connection.WebReader;
/// Controller for web scraping and link preview services /// Controller for web scraping and link preview services
/// </summary> /// </summary>
[ApiController] [ApiController]
[Route("/scrap")] [Route("/api/scrap")]
[EnableRateLimiting("fixed")] [EnableRateLimiting("fixed")]
public class WebReaderController(WebReaderService reader, ILogger<WebReaderController> logger) public class WebReaderController(WebReaderService reader, ILogger<WebReaderController> logger)
: ControllerBase : ControllerBase

View File

@ -8,10 +8,10 @@ using Swashbuckle.AspNetCore.Annotations;
namespace DysonNetwork.Sphere.Connection; namespace DysonNetwork.Sphere.Connection;
[ApiController] [ApiController]
[Route("/ws")] [Route("/api/ws")]
public class WebSocketController(WebSocketService ws, ILogger<WebSocketContext> logger) : ControllerBase public class WebSocketController(WebSocketService ws, ILogger<WebSocketContext> logger) : ControllerBase
{ {
[Route("/ws")] [Route("/api/ws")]
[Authorize] [Authorize]
[SwaggerIgnore] [SwaggerIgnore]
public async Task TheGateway() public async Task TheGateway()

View File

@ -7,7 +7,7 @@ using Microsoft.AspNetCore.Mvc;
namespace DysonNetwork.Sphere.Developer; namespace DysonNetwork.Sphere.Developer;
[ApiController] [ApiController]
[Route("/developers/{pubName}/apps")] [Route("/api/developers/{pubName}/apps")]
public class CustomAppController(CustomAppService customApps, PublisherService ps) : ControllerBase public class CustomAppController(CustomAppService customApps, PublisherService ps) : ControllerBase
{ {
public record CustomAppRequest( public record CustomAppRequest(

View File

@ -9,7 +9,7 @@ using NodaTime;
namespace DysonNetwork.Sphere.Developer; namespace DysonNetwork.Sphere.Developer;
[ApiController] [ApiController]
[Route("/developers")] [Route("/api/developers")]
public class DeveloperController( public class DeveloperController(
AppDatabase db, AppDatabase db,
PublisherService ps, PublisherService ps,

View File

@ -3,7 +3,7 @@ using Microsoft.AspNetCore.Mvc;
namespace DysonNetwork.Sphere.Discovery; namespace DysonNetwork.Sphere.Discovery;
[ApiController] [ApiController]
[Route("/discovery")] [Route("/api/discovery")]
public class DiscoveryController(DiscoveryService discoveryService) : ControllerBase public class DiscoveryController(DiscoveryService discoveryService) : ControllerBase
{ {
[HttpGet("realms")] [HttpGet("realms")]

View File

@ -1,4 +1,4 @@
@page "/web/account/profile" @page "//account/profile"
@model DysonNetwork.Sphere.Pages.Account.ProfileModel @model DysonNetwork.Sphere.Pages.Account.ProfileModel
@{ @{
ViewData["Title"] = "Profile"; ViewData["Title"] = "Profile";

View File

@ -1,9 +1,9 @@
@page "/web/auth/challenge/{id:guid}" @page "//auth/challenge/{id:guid}"
@model DysonNetwork.Sphere.Pages.Auth.ChallengeModel @model DysonNetwork.Sphere.Pages.Auth.ChallengeModel
@{ @{
// This page is kept for backward compatibility // This page is kept for backward compatibility
// It will automatically redirect to the new SelectFactor page // It will automatically redirect to the new SelectFactor page
Response.Redirect($"/web/auth/challenge/{Model.Id}/select-factor"); Response.Redirect($"//auth/challenge/{Model.Id}/select-factor");
} }
<div class="hero min-h-full bg-base-200"> <div class="hero min-h-full bg-base-200">

View File

@ -1,4 +1,4 @@
@page "/web/auth/login" @page "//auth/login"
@model DysonNetwork.Sphere.Pages.Auth.LoginModel @model DysonNetwork.Sphere.Pages.Auth.LoginModel
@{ @{
ViewData["Title"] = "Login | Solar Network"; ViewData["Title"] = "Login | Solar Network";

View File

@ -1,4 +1,4 @@
@page "/web/auth/challenge/{id:guid}/select-factor" @page "//auth/challenge/{id:guid}/select-factor"
@using DysonNetwork.Sphere.Account @using DysonNetwork.Sphere.Account
@model DysonNetwork.Sphere.Pages.Auth.SelectFactorModel @model DysonNetwork.Sphere.Pages.Auth.SelectFactorModel
@{ @{

View File

@ -1,4 +1,4 @@
@page "/web/auth/challenge/{id:guid}/verify/{factorId:guid}" @page "//auth/challenge/{id:guid}/verify/{factorId:guid}"
@using DysonNetwork.Sphere.Account @using DysonNetwork.Sphere.Account
@model DysonNetwork.Sphere.Pages.Auth.VerifyFactorModel @model DysonNetwork.Sphere.Pages.Auth.VerifyFactorModel
@{ @{

View File

@ -0,0 +1,22 @@
@page "/posts/{PostId:guid}"
@model DysonNetwork.Sphere.Pages.Posts.PostDetailModel
@{
ViewData["Title"] = Model.Post?.Title + " | Solar Network";
}
<div class="container mx-auto p-4">
@if (Model.Post != null)
{
<h1 class="text-3xl font-bold mb-4">@Model.Post.Title</h1>
<p class="text-gray-600 mb-2">Created at: @Model.Post.CreatedAt</p>
<div class="prose lg:prose-xl">
<p>@Model.Post.Content</p>
</div>
}
else
{
<div class="alert alert-error">
<span>Post not found.</span>
</div>
}
</div>

View File

@ -0,0 +1,44 @@
using DysonNetwork.Sphere.Account;
using DysonNetwork.Sphere.Post;
using DysonNetwork.Sphere.Publisher;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
namespace DysonNetwork.Sphere.Pages.Posts;
public class PostDetailModel(
AppDatabase db,
PublisherService pub,
RelationshipService rels
) : PageModel
{
[BindProperty(SupportsGet = true)]
public Guid PostId { get; set; }
public Post.Post? Post { get; set; }
public async Task<IActionResult> OnGetAsync()
{
if (PostId == Guid.Empty)
return NotFound();
HttpContext.Items.TryGetValue("CurrentUser", out var currentUserValue);
var currentUser = currentUserValue as Sphere.Account.Account;
var userFriends = currentUser is null ? [] : await rels.ListAccountFriends(currentUser);
var userPublishers = currentUser is null ? [] : await pub.GetUserPublishers(currentUser.Id);
Post = await db.Posts
.Where(e => e.Id == PostId)
.Include(e => e.Publisher)
.Include(e => e.Tags)
.Include(e => e.Categories)
.FilterWithVisibility(currentUser, userFriends, userPublishers)
.FirstOrDefaultAsync();
if (Post == null)
return NotFound();
return Page();
}
}

View File

@ -26,7 +26,7 @@
@if (Context.Request.Cookies.TryGetValue(AuthConstants.CookieTokenName, out _)) @if (Context.Request.Cookies.TryGetValue(AuthConstants.CookieTokenName, out _))
{ {
<li class="tooltip tooltip-bottom" data-tip="Profile"> <li class="tooltip tooltip-bottom" data-tip="Profile">
<a href="/web/account/profile"> <a href="//account/profile">
<span class="material-symbols-outlined">account_circle</span> <span class="material-symbols-outlined">account_circle</span>
</a> </a>
</li> </li>
@ -43,7 +43,7 @@
else else
{ {
<li class="tooltip tooltip-bottom" data-tip="Login"> <li class="tooltip tooltip-bottom" data-tip="Login">
<a href="/web/auth/login"><span class="material-symbols-outlined">login</span></a> <a href="//auth/login"><span class="material-symbols-outlined">login</span></a>
</li> </li>
} }
</ul> </ul>

View File

@ -1,6 +1,7 @@
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.Text.Json; using System.Text.Json;
using DysonNetwork.Sphere.Account; using DysonNetwork.Sphere.Account;
using DysonNetwork.Sphere.Pages.Posts;
using DysonNetwork.Sphere.Permission; using DysonNetwork.Sphere.Permission;
using DysonNetwork.Sphere.Publisher; using DysonNetwork.Sphere.Publisher;
using DysonNetwork.Sphere.Storage; using DysonNetwork.Sphere.Storage;
@ -13,7 +14,7 @@ using NpgsqlTypes;
namespace DysonNetwork.Sphere.Post; namespace DysonNetwork.Sphere.Post;
[ApiController] [ApiController]
[Route("/posts")] [Route("/api/posts")]
public class PostController( public class PostController(
AppDatabase db, AppDatabase db,
PostService ps, PostService ps,
@ -65,6 +66,9 @@ public class PostController(
[HttpGet("{id:guid}")] [HttpGet("{id:guid}")]
public async Task<ActionResult<Post>> GetPost(Guid id) public async Task<ActionResult<Post>> GetPost(Guid id)
{ {
if (HttpContext.Items["IsWebPage"] as bool? ?? true)
return RedirectToPage("/Posts/PostDetail", new { PostId = id });
HttpContext.Items.TryGetValue("CurrentUser", out var currentUserValue); HttpContext.Items.TryGetValue("CurrentUser", out var currentUserValue);
var currentUser = currentUserValue as Account.Account; var currentUser = currentUserValue as Account.Account;
var userFriends = currentUser is null ? [] : await rels.ListAccountFriends(currentUser); var userFriends = currentUser is null ? [] : await rels.ListAccountFriends(currentUser);

View File

@ -11,7 +11,7 @@ using NodaTime;
namespace DysonNetwork.Sphere.Publisher; namespace DysonNetwork.Sphere.Publisher;
[ApiController] [ApiController]
[Route("/publishers")] [Route("/api/publishers")]
public class PublisherController( public class PublisherController(
AppDatabase db, AppDatabase db,
PublisherService ps, PublisherService ps,

View File

@ -6,7 +6,7 @@ using Microsoft.EntityFrameworkCore;
namespace DysonNetwork.Sphere.Publisher; namespace DysonNetwork.Sphere.Publisher;
[ApiController] [ApiController]
[Route("/publishers")] [Route("/api/publishers")]
public class PublisherSubscriptionController( public class PublisherSubscriptionController(
PublisherSubscriptionService subs, PublisherSubscriptionService subs,
AppDatabase db, AppDatabase db,

View File

@ -6,7 +6,7 @@ using Microsoft.EntityFrameworkCore;
namespace DysonNetwork.Sphere.Realm; namespace DysonNetwork.Sphere.Realm;
[ApiController] [ApiController]
[Route("/realms/{slug}")] [Route("/api/realms/{slug}")]
public class RealmChatController(AppDatabase db, RealmService rs) : ControllerBase public class RealmChatController(AppDatabase db, RealmService rs) : ControllerBase
{ {
[HttpGet("chat")] [HttpGet("chat")]

View File

@ -9,7 +9,7 @@ using NodaTime;
namespace DysonNetwork.Sphere.Realm; namespace DysonNetwork.Sphere.Realm;
[ApiController] [ApiController]
[Route("/realms")] [Route("/api/realms")]
public class RealmController( public class RealmController(
AppDatabase db, AppDatabase db,
RealmService rs, RealmService rs,

View File

@ -7,7 +7,7 @@ using Microsoft.AspNetCore.Mvc;
namespace DysonNetwork.Sphere.Safety; namespace DysonNetwork.Sphere.Safety;
[ApiController] [ApiController]
[Route("/safety/reports")] [Route("/api/safety/reports")]
public class AbuseReportController( public class AbuseReportController(
SafetyService safety SafetyService safety
) : ControllerBase ) : ControllerBase

View File

@ -1,4 +1,5 @@
using System.Net; using System.Net;
using DysonNetwork.Sphere.Connection;
using DysonNetwork.Sphere.Permission; using DysonNetwork.Sphere.Permission;
using DysonNetwork.Sphere.Storage; using DysonNetwork.Sphere.Storage;
using Microsoft.AspNetCore.HttpOverrides; using Microsoft.AspNetCore.HttpOverrides;
@ -14,6 +15,7 @@ public static class ApplicationConfiguration
{ {
app.MapMetrics(); app.MapMetrics();
app.MapOpenApi(); app.MapOpenApi();
app.UseMiddleware<ClientTypeMiddleware>();
app.UseSwagger(); app.UseSwagger();
app.UseSwaggerUI(); app.UseSwaggerUI();

View File

@ -9,7 +9,7 @@ using Microsoft.EntityFrameworkCore;
namespace DysonNetwork.Sphere.Sticker; namespace DysonNetwork.Sphere.Sticker;
[ApiController] [ApiController]
[Route("/stickers")] [Route("/api/stickers")]
public class StickerController(AppDatabase db, StickerService st) : ControllerBase public class StickerController(AppDatabase db, StickerService st) : ControllerBase
{ {
private async Task<IActionResult> _CheckStickerPackPermissions(Guid packId, Account.Account currentUser, private async Task<IActionResult> _CheckStickerPackPermissions(Guid packId, Account.Account currentUser,

View File

@ -7,7 +7,7 @@ using Minio.DataModel.Args;
namespace DysonNetwork.Sphere.Storage; namespace DysonNetwork.Sphere.Storage;
[ApiController] [ApiController]
[Route("/files")] [Route("/api/files")]
public class FileController( public class FileController(
AppDatabase db, AppDatabase db,
FileService fs, FileService fs,

View File

@ -6,7 +6,7 @@ using Microsoft.EntityFrameworkCore;
namespace DysonNetwork.Sphere.Wallet; namespace DysonNetwork.Sphere.Wallet;
[ApiController] [ApiController]
[Route("/orders")] [Route("/api/orders")]
public class OrderController(PaymentService payment, AuthService auth, AppDatabase db) : ControllerBase public class OrderController(PaymentService payment, AuthService auth, AppDatabase db) : ControllerBase
{ {
[HttpGet("{id:guid}")] [HttpGet("{id:guid}")]

View File

@ -8,7 +8,7 @@ using DysonNetwork.Sphere.Wallet.PaymentHandlers;
namespace DysonNetwork.Sphere.Wallet; namespace DysonNetwork.Sphere.Wallet;
[ApiController] [ApiController]
[Route("/subscriptions")] [Route("/api/subscriptions")]
public class SubscriptionController(SubscriptionService subscriptions, AfdianPaymentHandler afdian, AppDatabase db) : ControllerBase public class SubscriptionController(SubscriptionService subscriptions, AfdianPaymentHandler afdian, AppDatabase db) : ControllerBase
{ {
[HttpGet] [HttpGet]

View File

@ -7,7 +7,7 @@ using Microsoft.EntityFrameworkCore;
namespace DysonNetwork.Sphere.Wallet; namespace DysonNetwork.Sphere.Wallet;
[ApiController] [ApiController]
[Route("/wallets")] [Route("/api/wallets")]
public class WalletController(AppDatabase db, WalletService ws, PaymentService payment) : ControllerBase public class WalletController(AppDatabase db, WalletService ws, PaymentService payment) : ControllerBase
{ {
[HttpPost] [HttpPost]

View File

@ -7,6 +7,7 @@
--font-mono: "Noto Sans Mono", monospace; --font-mono: "Noto Sans Mono", monospace;
--color-blue-400: oklch(70.7% 0.165 254.624); --color-blue-400: oklch(70.7% 0.165 254.624);
--color-blue-600: oklch(54.6% 0.245 262.881); --color-blue-600: oklch(54.6% 0.245 262.881);
--color-gray-600: oklch(44.6% 0.03 256.802);
--color-black: #000; --color-black: #000;
--spacing: 0.25rem; --spacing: 0.25rem;
--container-xs: 20rem; --container-xs: 20rem;
@ -1861,6 +1862,61 @@
padding-block: calc(0.25rem * 1); padding-block: calc(0.25rem * 1);
--mask-chat: url("data:image/svg+xml,%3csvg width='13' height='13' xmlns='http://www.w3.org/2000/svg'%3e%3cpath fill='black' d='M0 11.5004C0 13.0004 2 13.0004 2 13.0004H12H13V0.00036329L12.5 0C12.5 0 11.977 2.09572 11.8581 2.50033C11.6075 3.35237 10.9149 4.22374 9 5.50036C6 7.50036 0 10.0004 0 11.5004Z'/%3e%3c/svg%3e"); --mask-chat: url("data:image/svg+xml,%3csvg width='13' height='13' xmlns='http://www.w3.org/2000/svg'%3e%3cpath fill='black' d='M0 11.5004C0 13.0004 2 13.0004 2 13.0004H12H13V0.00036329L12.5 0C12.5 0 11.977 2.09572 11.8581 2.50033C11.6075 3.35237 10.9149 4.22374 9 5.50036C6 7.50036 0 10.0004 0 11.5004Z'/%3e%3c/svg%3e");
} }
.prose {
:root & {
--tw-prose-body: var(--color-base-content);
@supports (color: color-mix(in lab, red, red)) {
--tw-prose-body: color-mix(in oklab, var(--color-base-content) 80%, #0000);
}
--tw-prose-headings: var(--color-base-content);
--tw-prose-lead: var(--color-base-content);
--tw-prose-links: var(--color-base-content);
--tw-prose-bold: var(--color-base-content);
--tw-prose-counters: var(--color-base-content);
--tw-prose-bullets: var(--color-base-content);
@supports (color: color-mix(in lab, red, red)) {
--tw-prose-bullets: color-mix(in oklab, var(--color-base-content) 50%, #0000);
}
--tw-prose-hr: var(--color-base-content);
@supports (color: color-mix(in lab, red, red)) {
--tw-prose-hr: color-mix(in oklab, var(--color-base-content) 20%, #0000);
}
--tw-prose-quotes: var(--color-base-content);
--tw-prose-quote-borders: var(--color-base-content);
@supports (color: color-mix(in lab, red, red)) {
--tw-prose-quote-borders: color-mix(in oklab, var(--color-base-content) 20%, #0000);
}
--tw-prose-captions: var(--color-base-content);
@supports (color: color-mix(in lab, red, red)) {
--tw-prose-captions: color-mix(in oklab, var(--color-base-content) 50%, #0000);
}
--tw-prose-code: var(--color-base-content);
--tw-prose-pre-code: var(--color-neutral-content);
--tw-prose-pre-bg: var(--color-neutral);
--tw-prose-th-borders: var(--color-base-content);
@supports (color: color-mix(in lab, red, red)) {
--tw-prose-th-borders: color-mix(in oklab, var(--color-base-content) 50%, #0000);
}
--tw-prose-td-borders: var(--color-base-content);
@supports (color: color-mix(in lab, red, red)) {
--tw-prose-td-borders: color-mix(in oklab, var(--color-base-content) 20%, #0000);
}
--tw-prose-kbd: var(--color-base-content);
@supports (color: color-mix(in lab, red, red)) {
--tw-prose-kbd: color-mix(in oklab, var(--color-base-content) 80%, #0000);
}
:where(code):not(pre > code) {
background-color: var(--color-base-200);
border-radius: var(--radius-selector);
border: var(--border) solid var(--color-base-300);
padding-inline: 0.5em;
font-weight: inherit;
&:before, &:after {
display: none;
}
}
}
}
.mask { .mask {
display: inline-block; display: inline-block;
vertical-align: middle; vertical-align: middle;
@ -2243,6 +2299,9 @@
.text-error { .text-error {
color: var(--color-error); color: var(--color-error);
} }
.text-gray-600 {
color: var(--color-gray-600);
}
.text-info { .text-info {
color: var(--color-info); color: var(--color-info);
} }

View File

@ -12,6 +12,7 @@
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AClaim_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fa7fdc52b6e574ae7b9822133be91162a15800_003Ff7_003Feebffd8d_003FClaim_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AClaim_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fa7fdc52b6e574ae7b9822133be91162a15800_003Ff7_003Feebffd8d_003FClaim_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AConnectionMultiplexer_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FSourcesCache_003F2ed0e2f073b1d77b98dadb822da09ee8a9dfb91bf29bf2bbaecb8750d7e74cc9_003FConnectionMultiplexer_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AConnectionMultiplexer_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FSourcesCache_003F2ed0e2f073b1d77b98dadb822da09ee8a9dfb91bf29bf2bbaecb8750d7e74cc9_003FConnectionMultiplexer_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AControllerBase_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F0b5acdd962e549369896cece0026e556214600_003Ff6_003Fdf150bb3_003FControllerBase_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AControllerBase_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F0b5acdd962e549369896cece0026e556214600_003Ff6_003Fdf150bb3_003FControllerBase_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AController_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fb320290c1b964c3e88434ff5505d9086c9a00_003Fdf_003F95b535f9_003FController_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ACookieOptions_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F663f33943e4c4e889dc7050c1e97e703e000_003F89_003Fb06980d7_003FCookieOptions_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ACookieOptions_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F663f33943e4c4e889dc7050c1e97e703e000_003F89_003Fb06980d7_003FCookieOptions_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ACorsPolicyBuilder_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F051ad509d0504b7ca10dedd9c2cabb9914200_003F8e_003Fb28257cb_003FCorsPolicyBuilder_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ACorsPolicyBuilder_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F051ad509d0504b7ca10dedd9c2cabb9914200_003F8e_003Fb28257cb_003FCorsPolicyBuilder_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ADailyTimeIntervalScheduleBuilder_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F929ef51651404d13aacd3eb8198d2961e4800_003F2b_003Ff86eadcb_003FDailyTimeIntervalScheduleBuilder_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ADailyTimeIntervalScheduleBuilder_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F929ef51651404d13aacd3eb8198d2961e4800_003F2b_003Ff86eadcb_003FDailyTimeIntervalScheduleBuilder_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>