diff --git a/docs/feature-plan-next.md b/docs/feature-plan-next.md new file mode 100644 index 0000000..6b31d9e --- /dev/null +++ b/docs/feature-plan-next.md @@ -0,0 +1,173 @@ +# Next Feature Plan + +Four features to implement after PR #6 merges. Work on `master`. + +--- + +## 1. Refresh button — Jobs and Library pages + +**Files:** `server/ClientApp/src/pages/JobsListPage.tsx`, `server/ClientApp/src/pages/LibraryPage.tsx` + +Both pages already expose `refetch` / `refetchVolumes` / `refetchChapters` from `createResource`. + +**Jobs page:** Add a `RefreshCw` (lucide-solid) button in the header row, right-aligned next to the existing icon. Call `refetch()` on click. Spin the icon while `jobs.loading` is true. + +**Library page:** Add a Refresh button next to "New Volume" / "New Chapter" in the header. Call both `refetchVolumes()` and `refetchChapters()`. Disable while either resource is loading. + +No server changes needed. + +--- + +## 2. Delete button per item — Jobs list page ✅ DONE (merged in PR #6) + +**Already implemented.** `JobCard` uses `
` outer wrapper with two sibling native ` +
+ )} + + +
+ {/* Re-OCR */} + + + {/* Re-translate */} + + + {/* Re-inpaint */} + + + {/* Re-patch */} + +
+ + {/* Exclude toggle */}

Jobs

+ {/* Filters */} diff --git a/server/ClientApp/src/pages/LibraryPage.tsx b/server/ClientApp/src/pages/LibraryPage.tsx index 33e173c..edbcaf0 100644 --- a/server/ClientApp/src/pages/LibraryPage.tsx +++ b/server/ClientApp/src/pages/LibraryPage.tsx @@ -6,6 +6,7 @@ import { Edit2, FolderOpen, Plus, + RefreshCw, Trash2, } from "lucide-solid"; import { @@ -179,6 +180,15 @@ export function LibraryPage() {

Library

+
+ {/* Bubble display padding */} +
+ Pad + + {bubblePadding()} + +
+ {/* Spacer */}
@@ -438,7 +515,7 @@ export function StudioPage() { > @@ -469,7 +546,7 @@ export function StudioPage() { > @@ -486,6 +563,10 @@ export function StudioPage() { bubble={selectedBubble()} onUpdate={handleBubbleUpdate} onDelete={handleBubbleDelete} + onReocr={handleBubbleReocr} + onRetranslate={handleBubbleRetranslate} + onReinpaint={handleBubbleReinpaint} + onRepatch={handleBubbleRepatch} />
diff --git a/server/ClientApp/src/types.ts b/server/ClientApp/src/types.ts index 41c51d1..01e104c 100644 --- a/server/ClientApp/src/types.ts +++ b/server/ClientApp/src/types.ts @@ -47,6 +47,8 @@ export interface TranslationBubble { translatedText: string; isManuallyAdded: boolean; isExcluded: boolean; + fontFamily?: string; + fontSizeOverride?: number; lastEditedAt?: string; createdAt: string; } diff --git a/server/Migrations/20260727000001_AddBubbleFontSettings.Designer.cs b/server/Migrations/20260727000001_AddBubbleFontSettings.Designer.cs new file mode 100644 index 0000000..091caf3 --- /dev/null +++ b/server/Migrations/20260727000001_AddBubbleFontSettings.Designer.cs @@ -0,0 +1,289 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using WebOcrServer.Data; + +#nullable disable + +namespace WebOcrServer.Migrations +{ + [DbContext(typeof(AppDbContext))] + [Migration("20260727000001_AddBubbleFontSettings")] + partial class AddBubbleFontSettings + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "9.0.18"); + + modelBuilder.Entity("WebOcrServer.Data.Chapter", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChapterNumber") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("SortOrder") + .HasColumnType("INTEGER"); + + b.Property("Title") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("VolumeId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("VolumeId"); + + b.ToTable("Chapters"); + }); + + modelBuilder.Entity("WebOcrServer.Data.OcrLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("ElapsedMs") + .HasColumnType("INTEGER"); + + b.Property("TargetLang") + .HasColumnType("TEXT"); + + b.Property("Text") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Translation") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("OcrLogs"); + }); + + modelBuilder.Entity("WebOcrServer.Data.PageTranslationJob", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("BubbleCount") + .HasColumnType("INTEGER"); + + b.Property("ChapterId") + .HasColumnType("INTEGER"); + + b.Property("CompletedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("ErrorMessage") + .HasColumnType("TEXT"); + + b.Property("OriginalHeight") + .HasColumnType("INTEGER"); + + b.Property("OriginalImagePath") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("OriginalWidth") + .HasColumnType("INTEGER"); + + b.Property("PageOrder") + .HasColumnType("INTEGER"); + + b.Property("ResultImagePath") + .HasColumnType("TEXT"); + + b.Property("Status") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Title") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ChapterId"); + + b.ToTable("PageTranslationJobs"); + }); + + modelBuilder.Entity("WebOcrServer.Data.PageTranslationLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("BubbleH") + .HasColumnType("REAL"); + + b.Property("BubbleIndex") + .HasColumnType("INTEGER"); + + b.Property("BubbleW") + .HasColumnType("REAL"); + + b.Property("BubbleX") + .HasColumnType("REAL"); + + b.Property("BubbleY") + .HasColumnType("REAL"); + + b.Property("Confidence") + .HasColumnType("REAL"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("FontFamily") + .HasColumnType("TEXT"); + + b.Property("FontSizeOverride") + .HasColumnType("INTEGER"); + + b.Property("IsExcluded") + .HasColumnType("INTEGER"); + + b.Property("IsManuallyAdded") + .HasColumnType("INTEGER"); + + b.Property("JobId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("LastEditedAt") + .HasColumnType("TEXT"); + + b.Property("SourceText") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("TranslatedText") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("JobId"); + + b.ToTable("PageTranslationLogs"); + }); + + modelBuilder.Entity("WebOcrServer.Data.TranslateLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("ElapsedMs") + .HasColumnType("INTEGER"); + + b.Property("SourceText") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("TargetLang") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Translated") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("TranslateLogs"); + }); + + modelBuilder.Entity("WebOcrServer.Data.Volume", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CoverImagePath") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("SortOrder") + .HasColumnType("INTEGER"); + + b.Property("Synopsis") + .HasColumnType("TEXT"); + + b.Property("Title") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Volumes"); + }); + + modelBuilder.Entity("WebOcrServer.Data.Chapter", b => + { + b.HasOne("WebOcrServer.Data.Volume", "Volume") + .WithMany("Chapters") + .HasForeignKey("VolumeId"); + + b.Navigation("Volume"); + }); + + modelBuilder.Entity("WebOcrServer.Data.PageTranslationJob", b => + { + b.HasOne("WebOcrServer.Data.Chapter", "Chapter") + .WithMany("Jobs") + .HasForeignKey("ChapterId"); + + b.Navigation("Chapter"); + }); + + modelBuilder.Entity("WebOcrServer.Data.PageTranslationLog", b => + { + b.HasOne("WebOcrServer.Data.PageTranslationJob", "Job") + .WithMany() + .HasForeignKey("JobId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Job"); + }); + + modelBuilder.Entity("WebOcrServer.Data.Chapter", b => + { + b.Navigation("Jobs"); + }); + + modelBuilder.Entity("WebOcrServer.Data.Volume", b => + { + b.Navigation("Chapters"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/server/Migrations/20260727000001_AddBubbleFontSettings.cs b/server/Migrations/20260727000001_AddBubbleFontSettings.cs new file mode 100644 index 0000000..5ba71fd --- /dev/null +++ b/server/Migrations/20260727000001_AddBubbleFontSettings.cs @@ -0,0 +1,38 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace WebOcrServer.Migrations +{ + /// + public partial class AddBubbleFontSettings : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "FontFamily", + table: "PageTranslationLogs", + type: "TEXT", + nullable: true); + + migrationBuilder.AddColumn( + name: "FontSizeOverride", + table: "PageTranslationLogs", + type: "INTEGER", + nullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "FontFamily", + table: "PageTranslationLogs"); + + migrationBuilder.DropColumn( + name: "FontSizeOverride", + table: "PageTranslationLogs"); + } + } +} diff --git a/server/Migrations/AppDbContextModelSnapshot.cs b/server/Migrations/AppDbContextModelSnapshot.cs index a0fd844..11bb9a1 100644 --- a/server/Migrations/AppDbContextModelSnapshot.cs +++ b/server/Migrations/AppDbContextModelSnapshot.cs @@ -152,6 +152,12 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("CreatedAt") .HasColumnType("TEXT"); + b.Property("FontFamily") + .HasColumnType("TEXT"); + + b.Property("FontSizeOverride") + .HasColumnType("INTEGER"); + b.Property("IsExcluded") .HasColumnType("INTEGER"); diff --git a/server/Models/RequestModels.cs b/server/Models/RequestModels.cs index 525c59d..f4961ca 100644 --- a/server/Models/RequestModels.cs +++ b/server/Models/RequestModels.cs @@ -2,7 +2,8 @@ namespace WebOcrServer; public record OcrRequest( string Image, - string? TranslateEngine = "none" + string? TranslateEngine = "none", + bool? TrackJob = null ); public record TranslateRequest( diff --git a/server/Models/ResponseModels.cs b/server/Models/ResponseModels.cs index 232a215..5092bda 100644 --- a/server/Models/ResponseModels.cs +++ b/server/Models/ResponseModels.cs @@ -3,7 +3,9 @@ namespace WebOcrServer; public record OcrResponse( string Text, string? Translation, - long ElapsedMs + long ElapsedMs, + [property: System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)] + string? JobId = null ); public record TranslateResponse( diff --git a/server/src/Data/AppDbContext.cs b/server/src/Data/AppDbContext.cs index 5dabec9..9c0be43 100644 --- a/server/src/Data/AppDbContext.cs +++ b/server/src/Data/AppDbContext.cs @@ -82,6 +82,8 @@ public class PageTranslationLog public string TranslatedText { get; set; } = ""; public bool IsManuallyAdded { get; set; } public bool IsExcluded { get; set; } + public string? FontFamily { get; set; } + public int? FontSizeOverride { get; set; } public DateTime? LastEditedAt { get; set; } public DateTime CreatedAt { get; set; } = DateTime.UtcNow; } diff --git a/server/src/Routes/OcrRoutes.cs b/server/src/Routes/OcrRoutes.cs index 9a12526..f2ce1a3 100644 --- a/server/src/Routes/OcrRoutes.cs +++ b/server/src/Routes/OcrRoutes.cs @@ -1,3 +1,4 @@ +using SkiaSharp; using WebOcrServer.Data; namespace WebOcrServer; @@ -10,8 +11,11 @@ public static void MapOcrRoutes(this WebApplication app) OcrRequest req, BootState boot, InferenceQueue queue, + PageTranslationQueue translationQueue, IServiceScopeFactory scopeFactory, - ILogger logger) => + AppConfig config, + ILogger logger, + CancellationToken ct) => { if (!boot.OcrReady) return Results.Json(new { error = "OCR model not ready" }, statusCode: 503); @@ -53,8 +57,67 @@ public static void MapOcrRoutes(this WebApplication app) // Fire-and-forget DB log (non-blocking) _ = LogOcrAsync(scopeFactory, result, engine); - return Results.Ok(result); + // Optional background page-translation job (for extension result push). + // Use TryWrite so a full or closed queue never delays or fails the OCR response. + string? jobId = null; + if (req.TrackJob == true && boot.IsReady) + { + try + { + var candidateId = Guid.NewGuid().ToString("N"); + var pngBytes = JpegToPng(imageBytes); + if (translationQueue.Writer.TryWrite(new PageTranslationItem(candidateId, pngBytes))) + jobId = candidateId; + else + logger.LogWarning("Translation queue full; skipping tracked job for this OCR request."); + } + catch (Exception ex) + { + logger.LogWarning(ex, "Failed to enqueue page-translation job; proceeding without job tracking."); + } + } + + return Results.Ok(result with { JobId = jobId }); }); + + // ── Job status (extension-accessible, default CORS) ─────────────────── + + app.MapGet("/jobs/{id}/status", async (string id, AppDbContext db) => + { + var job = await db.PageTranslationJobs.FindAsync(id); + if (job is null) return Results.NotFound(); + return Results.Ok(new { status = job.Status, job_id = id }); + }); + + // ── Job result image (extension-accessible, default CORS) ───────────── + + app.MapGet("/jobs/{id}/result-image", async ( + string id, + AppDbContext db, + AppConfig config, + HttpContext ctx) => + { + var job = await db.PageTranslationJobs.FindAsync(id); + if (job is null || string.IsNullOrEmpty(job.ResultImagePath)) + return Results.NotFound(); + + var dataDir = Path.GetDirectoryName(Path.GetFullPath(config.DatabasePath))!; + var fullPath = Path.Combine(dataDir, job.ResultImagePath); + if (!File.Exists(fullPath)) return Results.NotFound(); + + ctx.Response.Headers["Cache-Control"] = "no-cache"; + return Results.File(fullPath, "image/png"); + }); + } + + // ── Helpers ─────────────────────────────────────────────────────────────── + + private static byte[] JpegToPng(byte[] jpeg) + { + using var bmp = SKBitmap.Decode(jpeg); + if (bmp is null) throw new InvalidOperationException("Failed to decode image: unsupported format or corrupt data."); + using var imgData = bmp.Encode(SKEncodedImageFormat.Png, 100); + return imgData.ToArray(); } private static async Task LogOcrAsync( diff --git a/server/src/Routes/PortalRoutes.cs b/server/src/Routes/PortalRoutes.cs index 14cb31d..5b2fe02 100644 --- a/server/src/Routes/PortalRoutes.cs +++ b/server/src/Routes/PortalRoutes.cs @@ -145,13 +145,15 @@ public static void MapPortalRoutes(this WebApplication app) .FirstOrDefaultAsync(l => l.JobId == jobId && l.BubbleIndex == bubbleIndex); if (bubble is null) return Results.NotFound(); - if (req.BubbleX is not null) bubble.BubbleX = req.BubbleX.Value; - if (req.BubbleY is not null) bubble.BubbleY = req.BubbleY.Value; - if (req.BubbleW is not null) bubble.BubbleW = req.BubbleW.Value; - if (req.BubbleH is not null) bubble.BubbleH = req.BubbleH.Value; - if (req.SourceText is not null) bubble.SourceText = req.SourceText; - if (req.TranslatedText is not null) bubble.TranslatedText = req.TranslatedText; - if (req.IsExcluded is not null) bubble.IsExcluded = req.IsExcluded.Value; + if (req.BubbleX is not null) bubble.BubbleX = req.BubbleX.Value; + if (req.BubbleY is not null) bubble.BubbleY = req.BubbleY.Value; + if (req.BubbleW is not null) bubble.BubbleW = req.BubbleW.Value; + if (req.BubbleH is not null) bubble.BubbleH = req.BubbleH.Value; + if (req.SourceText is not null) bubble.SourceText = req.SourceText; + if (req.TranslatedText is not null) bubble.TranslatedText = req.TranslatedText; + if (req.IsExcluded is not null) bubble.IsExcluded = req.IsExcluded.Value; + if (req.FontFamily is not null) bubble.FontFamily = req.FontFamily == "" ? null : req.FontFamily; + if (req.FontSizeOverride is not null) bubble.FontSizeOverride = req.FontSizeOverride == 0 ? null : req.FontSizeOverride; bubble.LastEditedAt = DateTime.UtcNow; await db.SaveChangesAsync(); @@ -170,6 +172,52 @@ public static void MapPortalRoutes(this WebApplication app) return Results.NoContent(); }); + // ── Per-bubble actions (synchronous — single bubble is fast) ────────── + + g.MapPost("/jobs/{jobId}/bubbles/{bubbleIndex:int}/reocr", async ( + string jobId, int bubbleIndex, PageTranslationService pipeline) => + { + try + { + var updated = await pipeline.ReocrBubbleAsync(jobId, bubbleIndex); + return Results.Ok(updated); + } + catch (KeyNotFoundException) { return Results.NotFound(); } + }); + + g.MapPost("/jobs/{jobId}/bubbles/{bubbleIndex:int}/retranslate", async ( + string jobId, int bubbleIndex, PageTranslationService pipeline) => + { + try + { + var updated = await pipeline.RetranslateBubbleAsync(jobId, bubbleIndex); + return Results.Ok(updated); + } + catch (KeyNotFoundException) { return Results.NotFound(); } + }); + + g.MapPost("/jobs/{jobId}/bubbles/{bubbleIndex:int}/reinpaint", async ( + string jobId, int bubbleIndex, BubblePaddingRequest? body, PageTranslationService pipeline) => + { + try + { + await pipeline.ReinpaintBubbleAsync(jobId, bubbleIndex, Math.Max(0, body?.Padding ?? 0)); + return Results.Ok(); + } + catch (KeyNotFoundException) { return Results.NotFound(); } + }); + + g.MapPost("/jobs/{jobId}/bubbles/{bubbleIndex:int}/repatch", async ( + string jobId, int bubbleIndex, BubblePaddingRequest? body, PageTranslationService pipeline) => + { + try + { + await pipeline.RepatchBubbleAsync(jobId, bubbleIndex, Math.Max(0, body?.Padding ?? 0)); + return Results.Ok(); + } + catch (KeyNotFoundException) { return Results.NotFound(); } + }); + // ── Job actions (fire-and-forget; caller polls job status) ──────────── g.MapPost("/jobs/{id}/redetect", async ( @@ -284,6 +332,7 @@ public static void MapPortalRoutes(this WebApplication app) g.MapPost("/jobs/{id}/rerender", async ( string id, + RerenderRequest? body, PageTranslationService pipeline, AppDbContext db, ILoggerFactory loggerFactory) => @@ -291,6 +340,7 @@ public static void MapPortalRoutes(this WebApplication app) var job = await db.PageTranslationJobs.FindAsync(id); if (job is null) return Results.NotFound(); + int padding = Math.Max(0, body?.Padding ?? 0); var logger = loggerFactory.CreateLogger("PortalRerender"); _ = Task.Run(async () => { @@ -301,7 +351,7 @@ public static void MapPortalRoutes(this WebApplication app) var j = await db2.PageTranslationJobs.FindAsync(id); if (j is not null) { j.Status = "processing"; await db2.SaveChangesAsync(); } - await pipeline.RerenderAsync(id); + await pipeline.RerenderAsync(id, padding); j = await db2.PageTranslationJobs.FindAsync(id); if (j is not null) { j.Status = "done"; await db2.SaveChangesAsync(); } @@ -464,10 +514,13 @@ await db.PageTranslationJobs.Where(j => j.ChapterId == id) // ── Request / Response records ──────────────────────────────────────────── private record UpdateJobRequest(string? Title, int? ChapterId, int? PageOrder); + private record RerenderRequest(int Padding = 0); + private record BubblePaddingRequest(int Padding = 0); private record AddBubbleRequest(float X, float Y, float W, float H); private record UpdateBubbleRequest( float? BubbleX, float? BubbleY, float? BubbleW, float? BubbleH, - string? SourceText, string? TranslatedText, bool? IsExcluded); + string? SourceText, string? TranslatedText, bool? IsExcluded, + string? FontFamily = null, int? FontSizeOverride = null); private record CreateVolumeRequest(string Title, string? Synopsis); private record UpdateVolumeRequest(string? Title, string? Synopsis, int? SortOrder); private record CreateChapterRequest(string Title, string ChapterNumber, int? VolumeId); diff --git a/server/src/ServiceExtensions.cs b/server/src/ServiceExtensions.cs index 7992e6b..9d780f6 100644 --- a/server/src/ServiceExtensions.cs +++ b/server/src/ServiceExtensions.cs @@ -88,6 +88,8 @@ public static void AddWebOcrServices(this WebApplicationBuilder builder) // Background inference queue (serialises ONNX sessions) builder.Services.AddSingleton(); builder.Services.AddHostedService(); + builder.Services.AddSingleton(); + builder.Services.AddHostedService(); // Boot background service: scaffolds dirs, runs migrations, downloads models, inits services builder.Services.AddHostedService(); diff --git a/server/src/Services/BubbleDetectionService.cs b/server/src/Services/BubbleDetectionService.cs index 5252e8f..07d9699 100644 --- a/server/src/Services/BubbleDetectionService.cs +++ b/server/src/Services/BubbleDetectionService.cs @@ -65,7 +65,7 @@ public void LoadModel(string modelPath) /// Returns pixel-coordinate bounding boxes in the original image dimensions. /// Returns an empty list when no model is loaded. /// - public List Detect(byte[] imagePng, float confidenceThreshold = 0.35f) + public List Detect(byte[] imagePng, float confidenceThreshold = 0.35f, CancellationToken ct = default) { // Capture session once; field is volatile so this read has acquire semantics var session = _session; @@ -75,8 +75,8 @@ public List Detect(byte[] imagePng, float confidenceThreshold = 0.35f if (bitmap is null) return []; return _family == ModelFamily.RtDetr - ? DetectRtDetr(session, bitmap, confidenceThreshold) - : DetectYolo(session, bitmap, confidenceThreshold); + ? DetectRtDetr(session, bitmap, confidenceThreshold, ct) + : DetectYolo(session, bitmap, confidenceThreshold, ct); } // ── RT-DETR inference ───────────────────────────────────────────────────── @@ -85,7 +85,7 @@ public List Detect(byte[] imagePng, float confidenceThreshold = 0.35f // Inputs: images [1,3,640,640] float32 normalised, orig_target_sizes [1,2] int64 (H, W) // Outputs: labels [1,N] int64, boxes [1,N,4] float32 (x1,y1,x2,y2 pixel coords), scores [1,N] float32 - private List DetectRtDetr(InferenceSession session, SKBitmap bitmap, float confidenceThreshold) + private List DetectRtDetr(InferenceSession session, SKBitmap bitmap, float confidenceThreshold, CancellationToken ct) { const int InputSize = 640; int origW = bitmap.Width, origH = bitmap.Height; @@ -172,13 +172,15 @@ private List DetectRtDetr(InferenceSession session, SKBitmap bitmap, result.Add(new BubbleBox(x1, y1, bw, bh, score)); } - return Nms(result, iouThreshold: 0.45f); + // NMS first to eliminate overlapping duplicates, then refine surviving boxes + return Nms(result, iouThreshold: 0.45f) + .ConvertAll(b => RefineToInnerBoundary(bitmap, b, ct: ct)); } // ── YOLOv8 inference ────────────────────────────────────────────────────── // Handles both channels-first [1,5+cls,anchors] and channels-last [1,anchors,5+cls] layouts. - private List DetectYolo(InferenceSession session, SKBitmap bitmap, float confidenceThreshold) + private List DetectYolo(InferenceSession session, SKBitmap bitmap, float confidenceThreshold, CancellationToken ct) { const int InputSize = 640; int origW = bitmap.Width, origH = bitmap.Height; @@ -246,7 +248,97 @@ private List DetectYolo(InferenceSession session, SKBitmap bitmap, fl } } - return Nms(boxes, iouThreshold: 0.45f); + // NMS first to eliminate overlapping duplicates, then refine surviving boxes + return Nms(boxes, iouThreshold: 0.45f) + .ConvertAll(b => RefineToInnerBoundary(bitmap, b, ct: ct)); + } + + // ── Inner-boundary refinement ───────────────────────────────────────────── + + /// + /// Refines a model bounding box to the actual white inner region of a speech bubble + /// by BFS flood-filling bright pixels from the box centre in the original image. + /// Returns unchanged when the centre pixel is not bright, + /// the box area exceeds , or refinement yields a degenerate rect. + /// + private static BubbleBox RefineToInnerBoundary( + SKBitmap src, BubbleBox box, byte brightnessThreshold = 200, CancellationToken ct = default) + { + int x0 = Math.Max(0, (int)box.X); + int y0 = Math.Max(0, (int)box.Y); + int w = Math.Min((int)box.Width, src.Width - x0); + int h = Math.Min((int)box.Height, src.Height - y0); + if (w <= 0 || h <= 0) return box; + + // Guard against unbounded allocation for very large boxes. + const int MaxBfsArea = 1_000_000; // ~1000×1000 pixels + if (w * h > MaxBfsArea) return box; + + ct.ThrowIfCancellationRequested(); + + bool IsBright(int px, int py) + { + if ((uint)px >= (uint)src.Width || (uint)py >= (uint)src.Height) return false; + var c = src.GetPixel(px, py); + return c.Red > brightnessThreshold + && c.Green > brightnessThreshold + && c.Blue > brightnessThreshold; + } + + // Sample a 3×3 grid of candidate seeds at 25/50/75 % of the box dimensions. + // The exact centre is often occupied by a text glyph (dark), so we probe + // multiple points and take the first bright one as the BFS seed. + (int lx, int ly) seed = (-1, -1); + for (int gy = 1; gy <= 3 && seed.lx < 0; gy++) + for (int gx2 = 1; gx2 <= 3 && seed.lx < 0; gx2++) + { + int tx = x0 + gx2 * w / 4; + int ty = y0 + gy * h / 4; + if (IsBright(tx, ty)) + seed = (tx - x0, ty - y0); + } + + if (seed.lx < 0) return box; // no bright pixel found in bbox + + var visited = new bool[w, h]; + var queue = new Queue<(int lx, int ly)>(); + + visited[seed.lx, seed.ly] = true; + queue.Enqueue(seed); + + int minX = seed.lx + x0, maxX = minX; + int minY = seed.ly + y0, maxY = minY; + + void TryExpand(int nx, int ny) + { + if ((uint)nx >= (uint)w || (uint)ny >= (uint)h || visited[nx, ny]) return; + visited[nx, ny] = true; + if (IsBright(nx + x0, ny + y0)) queue.Enqueue((nx, ny)); + } + + int bfsCount = 0; + while (queue.Count > 0) + { + if (++bfsCount % 1_000 == 0) ct.ThrowIfCancellationRequested(); + + var (lx, ly) = queue.Dequeue(); + int gx = lx + x0, gy2 = ly + y0; + if (gx < minX) minX = gx; if (gx > maxX) maxX = gx; + if (gy2 < minY) minY = gy2; if (gy2 > maxY) maxY = gy2; + TryExpand(lx + 1, ly); + TryExpand(lx - 1, ly); + TryExpand(lx, ly + 1); + TryExpand(lx, ly - 1); + } + + // Shrink 2 px inward on each side to leave border pixels intact. + const int EdgeInset = 2; + minX += EdgeInset; minY += EdgeInset; + maxX -= EdgeInset; maxY -= EdgeInset; + if (maxX <= minX || maxY <= minY) return box; + + // +1 converts inclusive pixel coordinates to pixel counts. + return new BubbleBox(minX, minY, maxX - minX + 1, maxY - minY + 1, box.Confidence); } // ── Coordinate helpers ──────────────────────────────────────────────────── diff --git a/server/src/Services/PageTranslationService.cs b/server/src/Services/PageTranslationService.cs index eeadd34..c933a0d 100644 --- a/server/src/Services/PageTranslationService.cs +++ b/server/src/Services/PageTranslationService.cs @@ -1,3 +1,4 @@ +using System.Collections.Concurrent; using Microsoft.EntityFrameworkCore; using SkiaSharp; using WebOcrServer.Data; @@ -232,13 +233,11 @@ await db.PageTranslationLogs } /// Re-runs typesetting on the current DB bubble data and overwrites the stored result image. - public async Task RerenderAsync(string jobId, CancellationToken ct = default) + public async Task RerenderAsync(string jobId, int padding = 0, CancellationToken ct = default) { var originalPath = Path.Combine(config.JobsDir, jobId, "original.png"); if (!File.Exists(originalPath)) throw new FileNotFoundException("Original image not found for job", originalPath); - var imagePng = await File.ReadAllBytesAsync(originalPath, ct); - await using var scope = scopeFactory.CreateAsyncScope(); var db = scope.ServiceProvider.GetRequiredService(); @@ -249,19 +248,25 @@ public async Task RerenderAsync(string jobId, CancellationToken ct = default) var translations = bubbleLogs.Select(l => new BubbleTranslation( new BubbleBox(l.BubbleX, l.BubbleY, l.BubbleW, l.BubbleH, l.Confidence), - l.SourceText, l.TranslatedText)).ToList(); - - var resultPng = await Task.Run(() => typesetter.RenderTranslations(imagePng, translations), ct); - - var resultPath = Path.Combine(config.JobsDir, jobId, "result.png"); - await File.WriteAllBytesAsync(resultPath, resultPng, ct); + l.SourceText, l.TranslatedText, l.FontFamily, l.FontSizeOverride)).ToList(); - var job = await db.PageTranslationJobs.FindAsync(jobId); - if (job is not null) + var imgLock = GetImageLock(jobId); + await imgLock.WaitAsync(ct); + try { - job.ResultImagePath = Path.Combine("jobs", jobId, "result.png"); - await db.SaveChangesAsync(ct); + var imagePng = await File.ReadAllBytesAsync(originalPath, ct); + var resultPng = await Task.Run(() => typesetter.RenderTranslations(imagePng, translations, padding), ct); + var resultPath = Path.Combine(config.JobsDir, jobId, "result.png"); + await File.WriteAllBytesAsync(resultPath, resultPng, ct); + + var job = await db.PageTranslationJobs.FindAsync(jobId); + if (job is not null) + { + job.ResultImagePath = Path.Combine("jobs", jobId, "result.png"); + await db.SaveChangesAsync(ct); + } } + finally { imgLock.Release(); } } // ── Public helpers used by PortalRoutes ─────────────────────────────────── @@ -276,6 +281,145 @@ public async Task RerenderAsync(string jobId, CancellationToken ct = default) public static byte[] CropBubblePublic(byte[] imagePng, BubbleBox box, float padding) => CropBubble(imagePng, box, padding); + // ── Per-bubble actions ──────────────────────────────────────────────────── + + /// Re-runs OCR on a single bubble, updates . + public async Task ReocrBubbleAsync(string jobId, int bubbleIndex, CancellationToken ct = default) + { + await using var scope = scopeFactory.CreateAsyncScope(); + var db = scope.ServiceProvider.GetRequiredService(); + + var bubble = await db.PageTranslationLogs + .FirstOrDefaultAsync(l => l.JobId == jobId && l.BubbleIndex == bubbleIndex, ct) + ?? throw new KeyNotFoundException($"Bubble {bubbleIndex} not found in job {jobId}"); + + var imagePng = await File.ReadAllBytesAsync(Path.Combine(config.JobsDir, jobId, "original.png"), ct); + var cropped = CropBubble(imagePng, + new BubbleBox(bubble.BubbleX, bubble.BubbleY, bubble.BubbleW, bubble.BubbleH, bubble.Confidence), 0.05f); + + var ocrTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + await queue.Writer.WriteAsync(new OcrJob(cropped, "none", ocrTcs), ct); + var ocrResult = (OcrResponse)await ocrTcs.Task; + + bubble.SourceText = ocrResult.Text?.Trim() ?? ""; + bubble.LastEditedAt = DateTime.UtcNow; + await db.SaveChangesAsync(ct); + return bubble; + } + + /// Translates the stored source text for a single bubble, updates . + public async Task RetranslateBubbleAsync(string jobId, int bubbleIndex, CancellationToken ct = default) + { + await using var scope = scopeFactory.CreateAsyncScope(); + var db = scope.ServiceProvider.GetRequiredService(); + + var bubble = await db.PageTranslationLogs + .FirstOrDefaultAsync(l => l.JobId == jobId && l.BubbleIndex == bubbleIndex, ct) + ?? throw new KeyNotFoundException($"Bubble {bubbleIndex} not found in job {jobId}"); + + if (!string.IsNullOrEmpty(bubble.SourceText)) + { + var trTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + await queue.Writer.WriteAsync( + new TranslateJob(bubble.SourceText, modelSettings.Current.PreferredTranslationEngine, trTcs), ct); + var trResult = (TranslateResponse)await trTcs.Task; + + bubble.TranslatedText = trResult.Translation; + bubble.LastEditedAt = DateTime.UtcNow; + await db.SaveChangesAsync(ct); + } + + return bubble; + } + + /// + /// White-fills a single bubble in the stored result image (per-bubble re-inpaint). + /// If no result image exists yet, copies the original first. + /// + public async Task ReinpaintBubbleAsync(string jobId, int bubbleIndex, int padding = 0, CancellationToken ct = default) + { + await using var scope = scopeFactory.CreateAsyncScope(); + var db = scope.ServiceProvider.GetRequiredService(); + + var bubble = await db.PageTranslationLogs + .FirstOrDefaultAsync(l => l.JobId == jobId && l.BubbleIndex == bubbleIndex, ct) + ?? throw new KeyNotFoundException($"Bubble {bubbleIndex} not found in job {jobId}"); + + var jobDir = Path.Combine(config.JobsDir, jobId); + var resultPath = Path.Combine(jobDir, "result.png"); + var originalPath = Path.Combine(jobDir, "original.png"); + + var box = padding > 0 + ? new BubbleBox(bubble.BubbleX + padding, bubble.BubbleY + padding, + Math.Max(1, bubble.BubbleW - 2 * padding), Math.Max(1, bubble.BubbleH - 2 * padding), bubble.Confidence) + : new BubbleBox(bubble.BubbleX, bubble.BubbleY, bubble.BubbleW, bubble.BubbleH, bubble.Confidence); + + var imgLock = GetImageLock(jobId); + await imgLock.WaitAsync(ct); + try + { + if (!File.Exists(resultPath)) File.Copy(originalPath, resultPath); + var imagePng = await File.ReadAllBytesAsync(resultPath, ct); + var resultPng = await Task.Run(() => typesetter.WhiteFillBubble(imagePng, box), ct); + await File.WriteAllBytesAsync(resultPath, resultPng, ct); + + var job = await db.PageTranslationJobs.FindAsync(jobId); + if (job is not null && string.IsNullOrEmpty(job.ResultImagePath)) + { + job.ResultImagePath = Path.Combine("jobs", jobId, "result.png"); + await db.SaveChangesAsync(ct); + } + } + finally { imgLock.Release(); } + } + + /// + /// Re-renders (white-fill + typeset) a single bubble onto the stored result image. + /// If no result image exists yet, copies the original first. + /// + public async Task RepatchBubbleAsync(string jobId, int bubbleIndex, int padding = 0, CancellationToken ct = default) + { + await using var scope = scopeFactory.CreateAsyncScope(); + var db = scope.ServiceProvider.GetRequiredService(); + + var bubble = await db.PageTranslationLogs + .FirstOrDefaultAsync(l => l.JobId == jobId && l.BubbleIndex == bubbleIndex, ct) + ?? throw new KeyNotFoundException($"Bubble {bubbleIndex} not found in job {jobId}"); + + var jobDir = Path.Combine(config.JobsDir, jobId); + var resultPath = Path.Combine(jobDir, "result.png"); + var originalPath = Path.Combine(jobDir, "original.png"); + + var t = new BubbleTranslation( + new BubbleBox(bubble.BubbleX, bubble.BubbleY, bubble.BubbleW, bubble.BubbleH, bubble.Confidence), + bubble.SourceText, bubble.TranslatedText, bubble.FontFamily, bubble.FontSizeOverride); + + var imgLock = GetImageLock(jobId); + await imgLock.WaitAsync(ct); + try + { + if (!File.Exists(resultPath)) File.Copy(originalPath, resultPath); + var imagePng = await File.ReadAllBytesAsync(resultPath, ct); + var resultPng = await Task.Run(() => typesetter.RenderOneBubble(imagePng, t, padding), ct); + await File.WriteAllBytesAsync(resultPath, resultPng, ct); + + var job = await db.PageTranslationJobs.FindAsync(jobId); + if (job is not null && string.IsNullOrEmpty(job.ResultImagePath)) + { + job.ResultImagePath = Path.Combine("jobs", jobId, "result.png"); + await db.SaveChangesAsync(ct); + } + } + finally { imgLock.Release(); } + } + + // ── Per-job image lock (serialises rerender/reinpaint/repatch) ─────────── + + private static readonly ConcurrentDictionary ImageLocks = new(); + + private static SemaphoreSlim GetImageLock(string jobId) => + ImageLocks.GetOrAdd(jobId, _ => new SemaphoreSlim(1, 1)); + // ── DB helpers ──────────────────────────────────────────────────────────── private async Task CreateJobRowAsync(string jobId, string originalPath, int width, int height) diff --git a/server/src/Services/TypesettingService.cs b/server/src/Services/TypesettingService.cs index 5d76255..19fb8ca 100644 --- a/server/src/Services/TypesettingService.cs +++ b/server/src/Services/TypesettingService.cs @@ -6,7 +6,14 @@ namespace WebOcrServer; /// Bounding box in the page's pixel coordinates. /// Original OCR text (Japanese). /// Translated text (English). -public record BubbleTranslation(BubbleBox Box, string SourceText, string TranslatedText); +/// Optional font family override (null = default sans-serif). +/// Optional fixed font size override (null or 0 = auto-fit). +public record BubbleTranslation( + BubbleBox Box, + string SourceText, + string TranslatedText, + string? FontFamily = null, + int? FontSizeOverride = null); /// /// Renders translated text into speech-bubble regions of an image using SkiaSharp. @@ -27,7 +34,7 @@ public TypesettingService(ILogger logger) /// inside each bubble region. Returns the original bytes unchanged if no translations /// are provided or if decoding fails. /// - public byte[] RenderTranslations(byte[] imagePng, IReadOnlyList translations) + public byte[] RenderTranslations(byte[] imagePng, IReadOnlyList translations, int padding = 0) { if (translations.Count == 0) return imagePng; @@ -43,7 +50,13 @@ public byte[] RenderTranslations(byte[] imagePng, IReadOnlyList 0 + ? new BubbleBox(t.Box.X + padding, t.Box.Y + padding, + Math.Max(1, t.Box.Width - 2 * padding), + Math.Max(1, t.Box.Height - 2 * padding), + t.Box.Confidence) + : t.Box; + RenderTextInBubble(canvas, box, t.TranslatedText, t.FontFamily, t.FontSizeOverride); } canvas.Flush(); @@ -53,9 +66,56 @@ public byte[] RenderTranslations(byte[] imagePng, IReadOnlyList + /// White-fills a single bubble region (no text). Used for per-bubble re-inpaint. + /// + public byte[] WhiteFillBubble(byte[] imagePng, BubbleBox box) + { + using var bitmap = SKBitmap.Decode(imagePng); + if (bitmap is null) return imagePng; + + using var canvas = new SKCanvas(bitmap); + using var paint = new SKPaint { Color = SKColors.White, Style = SKPaintStyle.Fill }; + canvas.DrawRect(box.X, box.Y, box.Width, box.Height, paint); + canvas.Flush(); + + using var image = SKImage.FromBitmap(bitmap); + using var data = image.Encode(SKEncodedImageFormat.Png, 95); + return data.ToArray(); + } + + /// + /// White-fills then typesets a single bubble onto . + /// Used for per-bubble re-patch. + /// + public byte[] RenderOneBubble(byte[] imagePng, BubbleTranslation t, int padding = 0) + { + using var bitmap = SKBitmap.Decode(imagePng); + if (bitmap is null) return imagePng; + + using var canvas = new SKCanvas(bitmap); + var box = padding > 0 + ? new BubbleBox(t.Box.X + padding, t.Box.Y + padding, + Math.Max(1, t.Box.Width - 2 * padding), + Math.Max(1, t.Box.Height - 2 * padding), + t.Box.Confidence) + : t.Box; + RenderTextInBubble(canvas, box, t.TranslatedText, t.FontFamily, t.FontSizeOverride); + canvas.Flush(); + + using var image = SKImage.FromBitmap(bitmap); + using var data = image.Encode(SKEncodedImageFormat.Png, 95); + return data.ToArray(); + } + // ── Private render helpers ───────────────────────────────────────────────── - private static void RenderTextInBubble(SKCanvas canvas, BubbleBox box, string text) + private static void RenderTextInBubble( + SKCanvas canvas, + BubbleBox box, + string text, + string? fontFamily = null, + int? fontSizeOverride = null) { // Target area = 85 % of the bubble so text has some margin float targetW = box.Width * 0.85f; @@ -69,15 +129,18 @@ private static void RenderTextInBubble(SKCanvas canvas, BubbleBox box, string te using var fillPaint = new SKPaint { Color = SKColors.White, Style = SKPaintStyle.Fill }; canvas.DrawRect(box.X, box.Y, box.Width, box.Height, fillPaint); - // Binary-search the largest font size that fits - float fontSize = BestFitFontSize(text, targetW, targetH, minSize: 8f, maxSize: 36f); - + var family = string.IsNullOrWhiteSpace(fontFamily) ? "sans-serif" : fontFamily; using var typeface = SKTypeface.FromFamilyName( - "sans-serif", + family, SKFontStyleWeight.Bold, SKFontStyleWidth.Normal, SKFontStyleSlant.Upright) ?? SKTypeface.Default; + // Use override size when set and > 0, otherwise binary-search the best fit + float fontSize = fontSizeOverride is > 0 + ? (float)fontSizeOverride + : BestFitFontSize(text, targetW, targetH, minSize: 8f, maxSize: 36f, typeface); + using var font = new SKFont(typeface, fontSize); var lines = WordWrap(text, font, targetW); @@ -109,22 +172,23 @@ private static void RenderTextInBubble(SKCanvas canvas, BubbleBox box, string te // ── Font-size search ────────────────────────────────────────────────────── private static float BestFitFontSize( - string text, float maxW, float maxH, float minSize, float maxSize) + string text, float maxW, float maxH, float minSize, float maxSize, + SKTypeface? typeface = null) { float lo = minSize, hi = maxSize, best = minSize; for (int iter = 0; iter < 14; iter++) { float mid = (lo + hi) / 2f; - if (TextFits(text, mid, maxW, maxH)) { best = mid; lo = mid; } + if (TextFits(text, mid, maxW, maxH, typeface)) { best = mid; lo = mid; } else hi = mid; if (hi - lo < 0.5f) break; } return best; } - private static bool TextFits(string text, float fontSize, float maxW, float maxH) + private static bool TextFits(string text, float fontSize, float maxW, float maxH, SKTypeface? typeface = null) { - using var font = new SKFont(SKTypeface.Default, fontSize); + using var font = new SKFont(typeface ?? SKTypeface.Default, fontSize); var lines = WordWrap(text, font, maxW); float totalH = lines.Count * fontSize * 1.25f; return totalH <= maxH; diff --git a/server/src/Workers/PageTranslationQueue.cs b/server/src/Workers/PageTranslationQueue.cs new file mode 100644 index 0000000..5131a50 --- /dev/null +++ b/server/src/Workers/PageTranslationQueue.cs @@ -0,0 +1,23 @@ +using System.Threading.Channels; + +namespace WebOcrServer; + +/// +/// Bounded channel that queues page-translation jobs for . +/// Unlike the CPU-bound , each item here is a full orchestration job +/// (OCR → translate → typeset) that itself drives the InferenceQueue internally. +/// +public sealed class PageTranslationQueue +{ + private readonly Channel _channel = + Channel.CreateBounded(new BoundedChannelOptions(32) + { + SingleReader = true, + FullMode = BoundedChannelFullMode.Wait, + }); + + public ChannelWriter Writer => _channel.Writer; + public ChannelReader Reader => _channel.Reader; +} + +public sealed record PageTranslationItem(string JobId, byte[] PngBytes); diff --git a/server/src/Workers/PageTranslationWorker.cs b/server/src/Workers/PageTranslationWorker.cs new file mode 100644 index 0000000..7acd635 --- /dev/null +++ b/server/src/Workers/PageTranslationWorker.cs @@ -0,0 +1,37 @@ +namespace WebOcrServer; + +/// +/// Hosted service that drains and runs full page-translation +/// jobs (OCR → translate → typeset) one at a time, keeping them off the ASP.NET thread pool. +/// +public sealed class PageTranslationWorker( + PageTranslationQueue queue, + PageTranslationService translationSvc, + ILogger logger +) : BackgroundService +{ + protected override async Task ExecuteAsync(CancellationToken ct) + { + await foreach (var item in queue.Reader.ReadAllAsync(ct)) + { + try + { + await translationSvc.TranslatePageAsync( + item.JobId, item.PngBytes, + new Progress(), + log: null, + ct: ct); + } + catch (OperationCanceledException) when (ct.IsCancellationRequested) + { + logger.LogWarning("Page translation job {JobId} cancelled at shutdown", item.JobId); + break; + } + catch (Exception ex) + { + logger.LogWarning(ex, "Page translation job {JobId} failed", item.JobId); + await translationSvc.MarkJobFailedAsync(item.JobId, ex.Message); + } + } + } +} diff --git a/server/wwwroot/js/app.css b/server/wwwroot/js/app.css index 28d18b4..a2178e9 100644 --- a/server/wwwroot/js/app.css +++ b/server/wwwroot/js/app.css @@ -1,3 +1,3 @@ /*! tailwindcss v4.3.0 | MIT License | https://tailwindcss.com */ -@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-space-y-reverse:0;--tw-divide-y-reverse:0;--tw-border-style:solid;--tw-font-weight:initial;--tw-tracking:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-duration:initial;--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-scale-x:1;--tw-scale-y:1;--tw-scale-z:1;--tw-outline-style:solid}}}@layer theme{:root,:host{--font-sans:ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-red-50:oklch(97.1% .013 17.38);--color-red-100:oklch(93.6% .032 17.717);--color-red-200:oklch(88.5% .062 18.334);--color-red-400:oklch(70.4% .191 22.216);--color-red-500:oklch(63.7% .237 25.331);--color-red-600:oklch(57.7% .245 27.325);--color-red-700:oklch(50.5% .213 27.518);--color-yellow-100:oklch(97.3% .071 103.193);--color-yellow-500:oklch(79.5% .184 86.047);--color-yellow-700:oklch(55.4% .135 66.442);--color-green-100:oklch(96.2% .044 156.743);--color-green-500:oklch(72.3% .219 149.579);--color-green-700:oklch(52.7% .154 150.069);--color-emerald-100:oklch(95% .052 163.051);--color-emerald-700:oklch(50.8% .118 165.612);--color-violet-50:oklch(96.9% .016 293.756);--color-violet-400:oklch(70.2% .183 293.541);--color-violet-500:oklch(60.6% .25 292.717);--color-violet-600:oklch(54.1% .281 293.009);--color-violet-700:oklch(49.1% .27 292.581);--color-slate-50:oklch(98.4% .003 247.858);--color-slate-100:oklch(96.8% .007 247.896);--color-slate-200:oklch(92.9% .013 255.508);--color-slate-300:oklch(86.9% .022 252.894);--color-slate-400:oklch(70.4% .04 256.788);--color-slate-500:oklch(55.4% .046 257.417);--color-slate-600:oklch(44.6% .043 257.281);--color-slate-700:oklch(37.2% .044 257.287);--color-slate-800:oklch(27.9% .041 260.031);--color-slate-900:oklch(20.8% .042 265.755);--color-black:#000;--color-white:#fff;--spacing:.25rem;--container-xs:20rem;--container-md:28rem;--container-2xl:42rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height:calc(1.5 / 1);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--text-3xl:1.875rem;--text-3xl--line-height:calc(2.25 / 1.875);--text-4xl:2.25rem;--text-4xl--line-height:calc(2.5 / 2.25);--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--tracking-wide:.025em;--radius-md:.375rem;--radius-lg:.5rem;--radius-xl:.75rem;--radius-2xl:1rem;--animate-spin:spin 1s linear infinite;--animate-pulse:pulse 2s cubic-bezier(.4, 0, .6, 1) infinite;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.pointer-events-none{pointer-events:none}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.inset-0{inset:calc(var(--spacing) * 0)}.top-2{top:calc(var(--spacing) * 2)}.right-2{right:calc(var(--spacing) * 2)}.bottom-2{bottom:calc(var(--spacing) * 2)}.left-2{left:calc(var(--spacing) * 2)}.z-10{z-index:10}.z-50{z-index:50}.m-6{margin:calc(var(--spacing) * 6)}.mx-1{margin-inline:calc(var(--spacing) * 1)}.mx-auto{margin-inline:auto}.mt-0\.5{margin-top:calc(var(--spacing) * .5)}.mt-1{margin-top:calc(var(--spacing) * 1)}.mt-auto{margin-top:auto}.mb-1{margin-bottom:calc(var(--spacing) * 1)}.mb-1\.5{margin-bottom:calc(var(--spacing) * 1.5)}.mb-3{margin-bottom:calc(var(--spacing) * 3)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.mb-5{margin-bottom:calc(var(--spacing) * 5)}.mb-6{margin-bottom:calc(var(--spacing) * 6)}.mb-10{margin-bottom:calc(var(--spacing) * 10)}.ml-1\.5{margin-left:calc(var(--spacing) * 1.5)}.ml-4{margin-left:calc(var(--spacing) * 4)}.ml-auto{margin-left:auto}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-flex{display:inline-flex}.table{display:table}.aspect-3\/4{aspect-ratio:3/4}.h-3{height:calc(var(--spacing) * 3)}.h-3\.5{height:calc(var(--spacing) * 3.5)}.h-4{height:calc(var(--spacing) * 4)}.h-5{height:calc(var(--spacing) * 5)}.h-6{height:calc(var(--spacing) * 6)}.h-7{height:calc(var(--spacing) * 7)}.h-8{height:calc(var(--spacing) * 8)}.h-9{height:calc(var(--spacing) * 9)}.h-10{height:calc(var(--spacing) * 10)}.h-12{height:calc(var(--spacing) * 12)}.h-14{height:calc(var(--spacing) * 14)}.h-20{height:calc(var(--spacing) * 20)}.h-32{height:calc(var(--spacing) * 32)}.h-full{height:100%}.h-screen{height:100vh}.max-h-80{max-height:calc(var(--spacing) * 80)}.min-h-screen{min-height:100vh}.w-3{width:calc(var(--spacing) * 3)}.w-3\.5{width:calc(var(--spacing) * 3.5)}.w-4{width:calc(var(--spacing) * 4)}.w-5{width:calc(var(--spacing) * 5)}.w-6{width:calc(var(--spacing) * 6)}.w-7{width:calc(var(--spacing) * 7)}.w-8{width:calc(var(--spacing) * 8)}.w-10{width:calc(var(--spacing) * 10)}.w-12{width:calc(var(--spacing) * 12)}.w-14{width:calc(var(--spacing) * 14)}.w-16{width:calc(var(--spacing) * 16)}.w-20{width:calc(var(--spacing) * 20)}.w-24{width:calc(var(--spacing) * 24)}.w-36{width:calc(var(--spacing) * 36)}.w-40{width:calc(var(--spacing) * 40)}.w-48{width:calc(var(--spacing) * 48)}.w-52{width:calc(var(--spacing) * 52)}.w-64{width:calc(var(--spacing) * 64)}.w-full{width:100%}.w-px{width:1px}.max-w-2xl{max-width:var(--container-2xl)}.max-w-md{max-width:var(--container-md)}.max-w-xs{max-width:var(--container-xs)}.min-w-0{min-width:calc(var(--spacing) * 0)}.flex-1{flex:1}.shrink-0{flex-shrink:0}.animate-pulse{animation:var(--animate-pulse)}.animate-spin{animation:var(--animate-spin)}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.resize{resize:both}.resize-y{resize:vertical}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.gap-0\.5{gap:calc(var(--spacing) * .5)}.gap-1{gap:calc(var(--spacing) * 1)}.gap-1\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}.gap-6{gap:calc(var(--spacing) * 6)}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-8>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 8) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 8) * calc(1 - var(--tw-space-y-reverse)))}:where(.divide-y>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px * var(--tw-divide-y-reverse));border-bottom-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-slate-50>:not(:last-child)){border-color:var(--color-slate-50)}:where(.divide-slate-100>:not(:last-child)){border-color:var(--color-slate-100)}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:var(--radius-2xl)}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.rounded-xl{border-radius:var(--radius-xl)}.border{border-style:var(--tw-border-style);border-width:1px}.border-0{border-style:var(--tw-border-style);border-width:0}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.border-l-2{border-left-style:var(--tw-border-style);border-left-width:2px}.border-dashed{--tw-border-style:dashed;border-style:dashed}.border-red-200{border-color:var(--color-red-200)}.border-slate-100{border-color:var(--color-slate-100)}.border-slate-200{border-color:var(--color-slate-200)}.border-slate-300{border-color:var(--color-slate-300)}.border-slate-700{border-color:var(--color-slate-700)}.border-violet-500{border-color:var(--color-violet-500)}.border-l-violet-500{border-left-color:var(--color-violet-500)}.bg-black\/50{background-color:#00000080}@supports (color:color-mix(in lab, red, red)){.bg-black\/50{background-color:color-mix(in oklab, var(--color-black) 50%, transparent)}}.bg-emerald-100{background-color:var(--color-emerald-100)}.bg-green-100{background-color:var(--color-green-100)}.bg-red-50{background-color:var(--color-red-50)}.bg-red-100{background-color:var(--color-red-100)}.bg-red-500\/90{background-color:#fb2c36e6}@supports (color:color-mix(in lab, red, red)){.bg-red-500\/90{background-color:color-mix(in oklab, var(--color-red-500) 90%, transparent)}}.bg-red-600{background-color:var(--color-red-600)}.bg-slate-50{background-color:var(--color-slate-50)}.bg-slate-100{background-color:var(--color-slate-100)}.bg-slate-200{background-color:var(--color-slate-200)}.bg-slate-800{background-color:var(--color-slate-800)}.bg-slate-900{background-color:var(--color-slate-900)}.bg-violet-50{background-color:var(--color-violet-50)}.bg-violet-600{background-color:var(--color-violet-600)}.bg-white{background-color:var(--color-white)}.bg-white\/80{background-color:#fffc}@supports (color:color-mix(in lab, red, red)){.bg-white\/80{background-color:color-mix(in oklab, var(--color-white) 80%, transparent)}}.bg-white\/90{background-color:#ffffffe6}@supports (color:color-mix(in lab, red, red)){.bg-white\/90{background-color:color-mix(in oklab, var(--color-white) 90%, transparent)}}.bg-yellow-100{background-color:var(--color-yellow-100)}.object-contain{object-fit:contain}.object-cover{object-fit:cover}.p-0\.5{padding:calc(var(--spacing) * .5)}.p-1{padding:calc(var(--spacing) * 1)}.p-1\.5{padding:calc(var(--spacing) * 1.5)}.p-2{padding:calc(var(--spacing) * 2)}.p-3{padding:calc(var(--spacing) * 3)}.p-4{padding:calc(var(--spacing) * 4)}.p-6{padding:calc(var(--spacing) * 6)}.px-1{padding-inline:calc(var(--spacing) * 1)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-2\.5{padding-inline:calc(var(--spacing) * 2.5)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-5{padding-inline:calc(var(--spacing) * 5)}.px-6{padding-inline:calc(var(--spacing) * 6)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:calc(var(--spacing) * 1)}.py-1\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-2\.5{padding-block:calc(var(--spacing) * 2.5)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-3\.5{padding-block:calc(var(--spacing) * 3.5)}.py-4{padding-block:calc(var(--spacing) * 4)}.py-6{padding-block:calc(var(--spacing) * 6)}.py-8{padding-block:calc(var(--spacing) * 8)}.py-10{padding-block:calc(var(--spacing) * 10)}.py-12{padding-block:calc(var(--spacing) * 12)}.py-16{padding-block:calc(var(--spacing) * 16)}.py-20{padding-block:calc(var(--spacing) * 20)}.py-24{padding-block:calc(var(--spacing) * 24)}.pt-1{padding-top:calc(var(--spacing) * 1)}.pt-3{padding-top:calc(var(--spacing) * 3)}.pr-4{padding-right:calc(var(--spacing) * 4)}.pr-10{padding-right:calc(var(--spacing) * 10)}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.font-mono{font-family:var(--font-mono)}.text-3xl{font-size:var(--text-3xl);line-height:var(--tw-leading,var(--text-3xl--line-height))}.text-4xl{font-size:var(--text-4xl);line-height:var(--tw-leading,var(--text-4xl--line-height))}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\[10px\]{font-size:10px}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-wide{--tw-tracking:var(--tracking-wide);letter-spacing:var(--tracking-wide)}.break-all{word-break:break-all}.whitespace-nowrap{white-space:nowrap}.text-emerald-700{color:var(--color-emerald-700)}.text-green-500{color:var(--color-green-500)}.text-green-700{color:var(--color-green-700)}.text-red-400{color:var(--color-red-400)}.text-red-500{color:var(--color-red-500)}.text-red-600{color:var(--color-red-600)}.text-red-700{color:var(--color-red-700)}.text-slate-300{color:var(--color-slate-300)}.text-slate-400{color:var(--color-slate-400)}.text-slate-500{color:var(--color-slate-500)}.text-slate-600{color:var(--color-slate-600)}.text-slate-700{color:var(--color-slate-700)}.text-slate-800{color:var(--color-slate-800)}.text-violet-500{color:var(--color-violet-500)}.text-violet-600{color:var(--color-violet-600)}.text-violet-700{color:var(--color-violet-700)}.text-white{color:var(--color-white)}.text-white\!{color:var(--color-white)!important}.text-yellow-500{color:var(--color-yellow-500)}.text-yellow-700{color:var(--color-yellow-700)}.uppercase{text-transform:uppercase}.line-through{text-decoration-line:line-through}.accent-violet-600{accent-color:var(--color-violet-600)}.opacity-0{opacity:0}.opacity-40{opacity:.4}.opacity-50{opacity:.5}.opacity-60{opacity:.6}.shadow{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-lg{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a), 0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-xl{--tw-shadow:0 20px 25px -5px var(--tw-shadow-color,#0000001a), 0 8px 10px -6px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring-1{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring-red-200{--tw-ring-color:var(--color-red-200)}.ring-slate-200{--tw-ring-color:var(--color-slate-200)}.blur{--tw-blur:blur(8px);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-all{transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-opacity{transition-property:opacity;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-200{--tw-duration:.2s;transition-duration:.2s}.select-none{-webkit-user-select:none;user-select:none}.ring-inset{--tw-ring-inset:inset}@media (hover:hover){.group-hover\:translate-x-0\.5:is(:where(.group):hover *){--tw-translate-x:calc(var(--spacing) * .5);translate:var(--tw-translate-x) var(--tw-translate-y)}.group-hover\:scale-105:is(:where(.group):hover *){--tw-scale-x:105%;--tw-scale-y:105%;--tw-scale-z:105%;scale:var(--tw-scale-x) var(--tw-scale-y)}.group-hover\:text-slate-500:is(:where(.group):hover *){color:var(--color-slate-500)}.group-hover\:opacity-100:is(:where(.group):hover *){opacity:1}}.placeholder\:text-slate-400::placeholder{color:var(--color-slate-400)}@media (hover:hover){.hover\:w-48:hover{width:calc(var(--spacing) * 48)}.hover\:border-slate-300:hover{border-color:var(--color-slate-300)}.hover\:bg-red-50:hover{background-color:var(--color-red-50)}.hover\:bg-red-100:hover{background-color:var(--color-red-100)}.hover\:bg-red-600:hover{background-color:var(--color-red-600)}.hover\:bg-red-700:hover{background-color:var(--color-red-700)}.hover\:bg-slate-50:hover{background-color:var(--color-slate-50)}.hover\:bg-slate-100:hover{background-color:var(--color-slate-100)}.hover\:bg-slate-800:hover{background-color:var(--color-slate-800)}.hover\:bg-violet-700:hover{background-color:var(--color-violet-700)}.hover\:bg-white:hover{background-color:var(--color-white)}.hover\:text-red-500:hover{color:var(--color-red-500)}.hover\:text-red-600:hover{color:var(--color-red-600)}.hover\:text-slate-500:hover{color:var(--color-slate-500)}.hover\:text-slate-600:hover{color:var(--color-slate-600)}.hover\:text-slate-800:hover{color:var(--color-slate-800)}.hover\:text-white:hover{color:var(--color-white)}.hover\:shadow-md:hover{--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a), 0 2px 4px -2px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.hover\:ring-slate-300:hover{--tw-ring-color:var(--color-slate-300)}}.focus\:border-violet-400:focus{border-color:var(--color-violet-400)}.focus\:ring-2:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.focus\:ring-violet-400:focus{--tw-ring-color:var(--color-violet-400)}.focus\:ring-violet-500:focus{--tw-ring-color:var(--color-violet-500)}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}.focus-visible\:opacity-100:focus-visible{opacity:1}.focus-visible\:outline-2:focus-visible{outline-style:var(--tw-outline-style);outline-width:2px}.focus-visible\:outline-violet-500:focus-visible{outline-color:var(--color-violet-500)}.active\:bg-red-200:active{background-color:var(--color-red-200)}.active\:bg-slate-200:active{background-color:var(--color-slate-200)}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-30:disabled{opacity:.3}.disabled\:opacity-50:disabled{opacity:.5}@media (width>=40rem){.sm\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}}@media (width>=64rem){.lg\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}}@media (width>=80rem){.xl\:grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}}}body{font-family:Inter,system-ui,sans-serif}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-divide-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-scale-x{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-y{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-z{syntax:"*";inherits:false;initial-value:1}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@keyframes spin{to{transform:rotate(360deg)}}@keyframes pulse{50%{opacity:.5}} +@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-space-y-reverse:0;--tw-divide-y-reverse:0;--tw-border-style:solid;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-duration:initial;--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-scale-x:1;--tw-scale-y:1;--tw-scale-z:1;--tw-outline-style:solid}}}@layer theme{:root,:host{--font-sans:ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-red-50:oklch(97.1% .013 17.38);--color-red-100:oklch(93.6% .032 17.717);--color-red-200:oklch(88.5% .062 18.334);--color-red-400:oklch(70.4% .191 22.216);--color-red-500:oklch(63.7% .237 25.331);--color-red-600:oklch(57.7% .245 27.325);--color-red-700:oklch(50.5% .213 27.518);--color-red-900:oklch(39.6% .141 25.723);--color-yellow-100:oklch(97.3% .071 103.193);--color-yellow-500:oklch(79.5% .184 86.047);--color-yellow-700:oklch(55.4% .135 66.442);--color-green-100:oklch(96.2% .044 156.743);--color-green-500:oklch(72.3% .219 149.579);--color-green-700:oklch(52.7% .154 150.069);--color-emerald-100:oklch(95% .052 163.051);--color-emerald-700:oklch(50.8% .118 165.612);--color-violet-50:oklch(96.9% .016 293.756);--color-violet-400:oklch(70.2% .183 293.541);--color-violet-500:oklch(60.6% .25 292.717);--color-violet-600:oklch(54.1% .281 293.009);--color-violet-700:oklch(49.1% .27 292.581);--color-slate-50:oklch(98.4% .003 247.858);--color-slate-100:oklch(96.8% .007 247.896);--color-slate-200:oklch(92.9% .013 255.508);--color-slate-300:oklch(86.9% .022 252.894);--color-slate-400:oklch(70.4% .04 256.788);--color-slate-500:oklch(55.4% .046 257.417);--color-slate-600:oklch(44.6% .043 257.281);--color-slate-700:oklch(37.2% .044 257.287);--color-slate-800:oklch(27.9% .041 260.031);--color-slate-900:oklch(20.8% .042 265.755);--color-black:#000;--color-white:#fff;--spacing:.25rem;--container-xs:20rem;--container-md:28rem;--container-2xl:42rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height:calc(1.5 / 1);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--text-3xl:1.875rem;--text-3xl--line-height:calc(2.25 / 1.875);--text-4xl:2.25rem;--text-4xl--line-height:calc(2.5 / 2.25);--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--tracking-wide:.025em;--radius-md:.375rem;--radius-lg:.5rem;--radius-xl:.75rem;--radius-2xl:1rem;--animate-spin:spin 1s linear infinite;--animate-pulse:pulse 2s cubic-bezier(.4, 0, .6, 1) infinite;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.pointer-events-none{pointer-events:none}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.inset-0{inset:calc(var(--spacing) * 0)}.top-2{top:calc(var(--spacing) * 2)}.right-2{right:calc(var(--spacing) * 2)}.bottom-2{bottom:calc(var(--spacing) * 2)}.left-2{left:calc(var(--spacing) * 2)}.z-10{z-index:10}.z-50{z-index:50}.m-6{margin:calc(var(--spacing) * 6)}.mx-1{margin-inline:calc(var(--spacing) * 1)}.mx-auto{margin-inline:auto}.mt-0\.5{margin-top:calc(var(--spacing) * .5)}.mt-1{margin-top:calc(var(--spacing) * 1)}.mt-auto{margin-top:auto}.mb-1{margin-bottom:calc(var(--spacing) * 1)}.mb-1\.5{margin-bottom:calc(var(--spacing) * 1.5)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.mb-3{margin-bottom:calc(var(--spacing) * 3)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.mb-5{margin-bottom:calc(var(--spacing) * 5)}.mb-6{margin-bottom:calc(var(--spacing) * 6)}.mb-10{margin-bottom:calc(var(--spacing) * 10)}.ml-1\.5{margin-left:calc(var(--spacing) * 1.5)}.ml-3{margin-left:calc(var(--spacing) * 3)}.ml-4{margin-left:calc(var(--spacing) * 4)}.ml-auto{margin-left:auto}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-flex{display:inline-flex}.table{display:table}.aspect-3\/4{aspect-ratio:3/4}.h-3{height:calc(var(--spacing) * 3)}.h-3\.5{height:calc(var(--spacing) * 3.5)}.h-4{height:calc(var(--spacing) * 4)}.h-5{height:calc(var(--spacing) * 5)}.h-6{height:calc(var(--spacing) * 6)}.h-7{height:calc(var(--spacing) * 7)}.h-8{height:calc(var(--spacing) * 8)}.h-9{height:calc(var(--spacing) * 9)}.h-10{height:calc(var(--spacing) * 10)}.h-12{height:calc(var(--spacing) * 12)}.h-14{height:calc(var(--spacing) * 14)}.h-20{height:calc(var(--spacing) * 20)}.h-32{height:calc(var(--spacing) * 32)}.h-full{height:100%}.h-screen{height:100vh}.max-h-80{max-height:calc(var(--spacing) * 80)}.min-h-screen{min-height:100vh}.w-3{width:calc(var(--spacing) * 3)}.w-3\.5{width:calc(var(--spacing) * 3.5)}.w-4{width:calc(var(--spacing) * 4)}.w-5{width:calc(var(--spacing) * 5)}.w-6{width:calc(var(--spacing) * 6)}.w-7{width:calc(var(--spacing) * 7)}.w-8{width:calc(var(--spacing) * 8)}.w-10{width:calc(var(--spacing) * 10)}.w-12{width:calc(var(--spacing) * 12)}.w-14{width:calc(var(--spacing) * 14)}.w-16{width:calc(var(--spacing) * 16)}.w-20{width:calc(var(--spacing) * 20)}.w-24{width:calc(var(--spacing) * 24)}.w-36{width:calc(var(--spacing) * 36)}.w-40{width:calc(var(--spacing) * 40)}.w-48{width:calc(var(--spacing) * 48)}.w-52{width:calc(var(--spacing) * 52)}.w-64{width:calc(var(--spacing) * 64)}.w-full{width:100%}.w-px{width:1px}.max-w-2xl{max-width:var(--container-2xl)}.max-w-md{max-width:var(--container-md)}.max-w-xs{max-width:var(--container-xs)}.min-w-0{min-width:calc(var(--spacing) * 0)}.flex-1{flex:1}.shrink-0{flex-shrink:0}.animate-pulse{animation:var(--animate-pulse)}.animate-spin{animation:var(--animate-spin)}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.resize{resize:both}.resize-y{resize:vertical}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.gap-0\.5{gap:calc(var(--spacing) * .5)}.gap-1{gap:calc(var(--spacing) * 1)}.gap-1\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}.gap-6{gap:calc(var(--spacing) * 6)}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-8>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 8) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 8) * calc(1 - var(--tw-space-y-reverse)))}:where(.divide-y>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px * var(--tw-divide-y-reverse));border-bottom-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-slate-50>:not(:last-child)){border-color:var(--color-slate-50)}:where(.divide-slate-100>:not(:last-child)){border-color:var(--color-slate-100)}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:var(--radius-2xl)}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.rounded-xl{border-radius:var(--radius-xl)}.border{border-style:var(--tw-border-style);border-width:1px}.border-0{border-style:var(--tw-border-style);border-width:0}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.border-l-2{border-left-style:var(--tw-border-style);border-left-width:2px}.border-dashed{--tw-border-style:dashed;border-style:dashed}.border-red-200{border-color:var(--color-red-200)}.border-slate-100{border-color:var(--color-slate-100)}.border-slate-200{border-color:var(--color-slate-200)}.border-slate-300{border-color:var(--color-slate-300)}.border-slate-700{border-color:var(--color-slate-700)}.border-violet-500{border-color:var(--color-violet-500)}.border-l-violet-500{border-left-color:var(--color-violet-500)}.bg-black\/50{background-color:#00000080}@supports (color:color-mix(in lab, red, red)){.bg-black\/50{background-color:color-mix(in oklab, var(--color-black) 50%, transparent)}}.bg-emerald-100{background-color:var(--color-emerald-100)}.bg-green-100{background-color:var(--color-green-100)}.bg-red-50{background-color:var(--color-red-50)}.bg-red-100{background-color:var(--color-red-100)}.bg-red-500\/90{background-color:#fb2c36e6}@supports (color:color-mix(in lab, red, red)){.bg-red-500\/90{background-color:color-mix(in oklab, var(--color-red-500) 90%, transparent)}}.bg-red-600{background-color:var(--color-red-600)}.bg-slate-50{background-color:var(--color-slate-50)}.bg-slate-100{background-color:var(--color-slate-100)}.bg-slate-200{background-color:var(--color-slate-200)}.bg-slate-800{background-color:var(--color-slate-800)}.bg-slate-900{background-color:var(--color-slate-900)}.bg-violet-50{background-color:var(--color-violet-50)}.bg-violet-600{background-color:var(--color-violet-600)}.bg-white{background-color:var(--color-white)}.bg-white\/80{background-color:#fffc}@supports (color:color-mix(in lab, red, red)){.bg-white\/80{background-color:color-mix(in oklab, var(--color-white) 80%, transparent)}}.bg-white\/90{background-color:#ffffffe6}@supports (color:color-mix(in lab, red, red)){.bg-white\/90{background-color:color-mix(in oklab, var(--color-white) 90%, transparent)}}.bg-yellow-100{background-color:var(--color-yellow-100)}.object-contain{object-fit:contain}.object-cover{object-fit:cover}.p-0\.5{padding:calc(var(--spacing) * .5)}.p-1{padding:calc(var(--spacing) * 1)}.p-1\.5{padding:calc(var(--spacing) * 1.5)}.p-2{padding:calc(var(--spacing) * 2)}.p-3{padding:calc(var(--spacing) * 3)}.p-4{padding:calc(var(--spacing) * 4)}.p-6{padding:calc(var(--spacing) * 6)}.px-1{padding-inline:calc(var(--spacing) * 1)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-2\.5{padding-inline:calc(var(--spacing) * 2.5)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-5{padding-inline:calc(var(--spacing) * 5)}.px-6{padding-inline:calc(var(--spacing) * 6)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:calc(var(--spacing) * 1)}.py-1\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-2\.5{padding-block:calc(var(--spacing) * 2.5)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-3\.5{padding-block:calc(var(--spacing) * 3.5)}.py-4{padding-block:calc(var(--spacing) * 4)}.py-6{padding-block:calc(var(--spacing) * 6)}.py-8{padding-block:calc(var(--spacing) * 8)}.py-10{padding-block:calc(var(--spacing) * 10)}.py-12{padding-block:calc(var(--spacing) * 12)}.py-16{padding-block:calc(var(--spacing) * 16)}.py-20{padding-block:calc(var(--spacing) * 20)}.py-24{padding-block:calc(var(--spacing) * 24)}.pt-1{padding-top:calc(var(--spacing) * 1)}.pt-3{padding-top:calc(var(--spacing) * 3)}.pr-4{padding-right:calc(var(--spacing) * 4)}.pr-10{padding-right:calc(var(--spacing) * 10)}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.font-mono{font-family:var(--font-mono)}.text-3xl{font-size:var(--text-3xl);line-height:var(--tw-leading,var(--text-3xl--line-height))}.text-4xl{font-size:var(--text-4xl);line-height:var(--tw-leading,var(--text-4xl--line-height))}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\[10px\]{font-size:10px}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-wide{--tw-tracking:var(--tracking-wide);letter-spacing:var(--tracking-wide)}.break-all{word-break:break-all}.whitespace-nowrap{white-space:nowrap}.text-emerald-700{color:var(--color-emerald-700)}.text-green-500{color:var(--color-green-500)}.text-green-700{color:var(--color-green-700)}.text-red-400{color:var(--color-red-400)}.text-red-500{color:var(--color-red-500)}.text-red-600{color:var(--color-red-600)}.text-red-700{color:var(--color-red-700)}.text-slate-300{color:var(--color-slate-300)}.text-slate-400{color:var(--color-slate-400)}.text-slate-500{color:var(--color-slate-500)}.text-slate-600{color:var(--color-slate-600)}.text-slate-700{color:var(--color-slate-700)}.text-slate-800{color:var(--color-slate-800)}.text-violet-500{color:var(--color-violet-500)}.text-violet-600{color:var(--color-violet-600)}.text-violet-700{color:var(--color-violet-700)}.text-white{color:var(--color-white)}.text-white\!{color:var(--color-white)!important}.text-yellow-500{color:var(--color-yellow-500)}.text-yellow-700{color:var(--color-yellow-700)}.uppercase{text-transform:uppercase}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.line-through{text-decoration-line:line-through}.accent-violet-600{accent-color:var(--color-violet-600)}.opacity-0{opacity:0}.opacity-40{opacity:.4}.opacity-50{opacity:.5}.opacity-60{opacity:.6}.shadow{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-lg{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a), 0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-xl{--tw-shadow:0 20px 25px -5px var(--tw-shadow-color,#0000001a), 0 8px 10px -6px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring-1{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring-red-200{--tw-ring-color:var(--color-red-200)}.ring-slate-200{--tw-ring-color:var(--color-slate-200)}.blur{--tw-blur:blur(8px);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-all{transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-opacity{transition-property:opacity;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-200{--tw-duration:.2s;transition-duration:.2s}.select-none{-webkit-user-select:none;user-select:none}.ring-inset{--tw-ring-inset:inset}@media (hover:hover){.group-hover\:translate-x-0\.5:is(:where(.group):hover *){--tw-translate-x:calc(var(--spacing) * .5);translate:var(--tw-translate-x) var(--tw-translate-y)}.group-hover\:scale-105:is(:where(.group):hover *){--tw-scale-x:105%;--tw-scale-y:105%;--tw-scale-z:105%;scale:var(--tw-scale-x) var(--tw-scale-y)}.group-hover\:text-slate-500:is(:where(.group):hover *){color:var(--color-slate-500)}.group-hover\:opacity-100:is(:where(.group):hover *){opacity:1}}.placeholder\:text-slate-400::placeholder{color:var(--color-slate-400)}@media (hover:hover){.hover\:w-48:hover{width:calc(var(--spacing) * 48)}.hover\:border-slate-300:hover{border-color:var(--color-slate-300)}.hover\:bg-red-50:hover{background-color:var(--color-red-50)}.hover\:bg-red-100:hover{background-color:var(--color-red-100)}.hover\:bg-red-600:hover{background-color:var(--color-red-600)}.hover\:bg-red-700:hover{background-color:var(--color-red-700)}.hover\:bg-slate-50:hover{background-color:var(--color-slate-50)}.hover\:bg-slate-100:hover{background-color:var(--color-slate-100)}.hover\:bg-slate-800:hover{background-color:var(--color-slate-800)}.hover\:bg-violet-700:hover{background-color:var(--color-violet-700)}.hover\:bg-white:hover{background-color:var(--color-white)}.hover\:text-red-500:hover{color:var(--color-red-500)}.hover\:text-red-600:hover{color:var(--color-red-600)}.hover\:text-red-900:hover{color:var(--color-red-900)}.hover\:text-slate-500:hover{color:var(--color-slate-500)}.hover\:text-slate-600:hover{color:var(--color-slate-600)}.hover\:text-slate-800:hover{color:var(--color-slate-800)}.hover\:text-white:hover{color:var(--color-white)}.hover\:shadow-md:hover{--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a), 0 2px 4px -2px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.hover\:shadow-sm:hover{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.hover\:ring-1:hover{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.hover\:ring-slate-200:hover{--tw-ring-color:var(--color-slate-200)}.hover\:ring-slate-300:hover{--tw-ring-color:var(--color-slate-300)}}.focus\:border-violet-400:focus{border-color:var(--color-violet-400)}.focus\:ring-2:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.focus\:ring-violet-400:focus{--tw-ring-color:var(--color-violet-400)}.focus\:ring-violet-500:focus{--tw-ring-color:var(--color-violet-500)}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}.focus-visible\:outline-2:focus-visible{outline-style:var(--tw-outline-style);outline-width:2px}.focus-visible\:outline-violet-500:focus-visible{outline-color:var(--color-violet-500)}.active\:bg-red-200:active{background-color:var(--color-red-200)}.active\:bg-slate-200:active{background-color:var(--color-slate-200)}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-30:disabled{opacity:.3}.disabled\:opacity-50:disabled{opacity:.5}@media (width>=40rem){.sm\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}}@media (width>=64rem){.lg\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}}@media (width>=80rem){.xl\:grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}}}body{font-family:Inter,system-ui,sans-serif}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-divide-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-scale-x{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-y{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-z{syntax:"*";inherits:false;initial-value:1}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@keyframes spin{to{transform:rotate(360deg)}}@keyframes pulse{50%{opacity:.5}} /*$vite$:1*/ \ No newline at end of file diff --git a/server/wwwroot/js/app.js b/server/wwwroot/js/app.js index 21bb8c4..393f998 100644 --- a/server/wwwroot/js/app.js +++ b/server/wwwroot/js/app.js @@ -1 +1 @@ -(function(){var e=Object.defineProperty,t=(e,t)=>()=>(e&&(t=e(e=0)),t),n=(t,n)=>{let r={};for(var i in t)e(r,i,{get:t[i],enumerable:!0});return n||e(r,Symbol.toStringTag,{value:`Module`}),r};function r(e){let t=String(e),n=t.length-1;return L.context.id+(n?String.fromCharCode(96+n):``)+t}function i(e){L.context=e}function a(){return{...L.context,id:L.getNextContextId(),count:0}}function o(e,t){let n=V,r=z,i=e.length===0,a=t===void 0?r:t,o=i?Ae:{owned:null,cleanups:null,context:a?a.context:null,owner:a},s=i?e:()=>e(()=>h(()=>N(o)));z=o,V=null;try{return A(s,!0)}finally{V=n,z=r}}function s(e,t){t=t?Object.assign({},Ee,t):Ee;let n={value:e,observers:null,observerSlots:null,comparator:t.equals||void 0};return[ee.bind(n),e=>(typeof e==`function`&&(e=B&&B.running&&B.sources.has(n)?e(n.tValue):e(n.value)),T(n,e))]}function c(e,t,n){let r=O(e,t,!0,R);Me&&B&&B.running?H.push(r):E(r)}function l(e,t,n){let r=O(e,t,!1,R);Me&&B&&B.running?H.push(r):E(r)}function u(e,t,n){Oe=ne;let r=O(e,t,!1,R),i=Le&&C(Le);i&&(r.suspense=i),(!n||!n.render)&&(r.user=!0),U?U.push(r):E(r)}function d(e,t,n){n=n?Object.assign({},Ee,n):Ee;let r=O(e,t,!0,0);return r.observers=null,r.observerSlots=null,r.comparator=n.equals||void 0,Me&&B&&B.running?(r.tState=R,H.push(r)):E(r),ee.bind(r)}function f(e){return e&&typeof e==`object`&&`then`in e}function p(e,t,n){let r,i,a;typeof t==`function`?(r=e,i=t,a=n||{}):(r=!0,i=e,a=t||{});let o=null,l=je,u=null,p=!1,m=!1,g=`initialValue`in a,_=typeof r==`function`&&d(r),v=new Set,[y,x]=(a.storage||s)(a.initialValue),[S,w]=s(void 0),[ee,T]=s(void 0,{equals:!1}),[E,D]=s(g?`ready`:`unresolved`);L.context&&(u=L.getNextContextId(),a.ssrLoadFrom===`initial`?l=a.initialValue:L.load&&L.has(u)&&(l=L.load(u)));function O(e,t,n,r){return o===e&&(o=null,r!==void 0&&(g=!0),(e===l||t===l)&&a.onHydrated&&queueMicrotask(()=>a.onHydrated(r,{value:t})),l=je,B&&e&&p?(B.promises.delete(e),p=!1,A(()=>{B.running=!0,k(t,n)},!1)):k(t,n)),t}function k(e,t){A(()=>{t===void 0&&x(()=>e),D(t===void 0?g?`ready`:`unresolved`:`errored`),w(t);for(let e of v.keys())e.decrement();v.clear()},!1)}function j(){let e=Le&&C(Le),t=y(),n=S();if(n!==void 0&&!o)throw n;return V&&!V.user&&e&&c(()=>{ee(),o&&(e.resolved&&B&&p?B.promises.add(o):v.has(e)||(e.increment(),v.add(e)))}),t}function te(e=!0){if(e!==!1&&m)return;m=!1;let t=_?_():r;if(p=B&&B.running,t==null||t===!1){O(o,h(y));return}B&&o&&B.promises.delete(o);let n,a=l===je?h(()=>{try{return i(t,{value:y(),refetching:e})}catch(e){n=e}}):l;if(n!==void 0){O(o,void 0,oe(n),t);return}else if(!f(a))return O(o,a,void 0,t),a;return o=a,`v`in a?(a.s===1?O(o,a.v,void 0,t):O(o,void 0,oe(a.v),t),a):(m=!0,queueMicrotask(()=>m=!1),A(()=>{D(g?`refreshing`:`pending`),T()},!1),a.then(e=>O(a,e,void 0,t),e=>O(a,void 0,oe(e),t)))}Object.defineProperties(j,{state:{get:()=>E()},error:{get:()=>S()},loading:{get(){let e=E();return e===`pending`||e===`refreshing`}},latest:{get(){if(!g)return j();let e=S();if(e&&!o)throw e;return y()}}});let M=z;return _?c(()=>(M=z,te(!1))):te(!1),[j,{refetch:e=>b(M,()=>te(e)),mutate:x}]}function m(e){return A(e,!1)}function h(e){if(!Ne&&V===null)return e();let t=V;V=null;try{return Ne?Ne.untrack(e):e()}finally{V=t}}function g(e,t,n){let r=Array.isArray(e),i,a=n&&n.defer;return n=>{let o;if(r){o=Array(e.length);for(let t=0;tt(o,i,n));return i=o,s}}function _(e){u(()=>h(e))}function v(e){return z===null||(z.cleanups===null?z.cleanups=[e]:z.cleanups.push(e)),e}function y(){return z}function b(e,t){let n=z,r=V;z=e,V=null;try{return A(t,!0)}catch(e){ce(e)}finally{z=n,V=r}}function x(e){if(B&&B.running)return e(),B.done;let t=V,n=z;return Promise.resolve().then(()=>{V=t,z=n;let r;return(Me||Le)&&(r=B||={sources:new Set,effects:[],promises:new Set,disposed:new Set,queue:new Set,running:!0},r.done||=new Promise(e=>r.resolve=e),r.running=!0),A(e,!1),V=z=null,r?r.done:void 0})}function S(e,t){let n=Symbol(`context`);return{id:n,Provider:ue(n),defaultValue:e}}function C(e){let t;return z&&z.context&&(t=z.context[e.id])!==void 0?t:e.defaultValue}function w(e){let t=d(e),n=d(()=>le(t()));return n.toArray=()=>{let e=n();return Array.isArray(e)?e:e==null?[]:[e]},n}function ee(){let e=B&&B.running;if(this.sources&&(e?this.tState:this.state))if((e?this.tState:this.state)===R)E(this);else{let e=H;H=null,A(()=>re(this),!1),H=e}if(V){let e=this.observers;if(!e||e[e.length-1]!==V){let t=e?e.length:0;V.sources?(V.sources.push(this),V.sourceSlots.push(t)):(V.sources=[this],V.sourceSlots=[t]),e?(e.push(V),this.observerSlots.push(V.sources.length-1)):(this.observers=[V],this.observerSlots=[V.sources.length-1])}}return e&&B.sources.has(this)?this.tValue:this.value}function T(e,t,n){let r=B&&B.running&&B.sources.has(e)?e.tValue:e.value;if(!e.comparator||!e.comparator(r,t)){if(B){let r=B.running;(r||!n&&B.sources.has(e))&&(B.sources.add(e),e.tValue=t),r||(e.value=t)}else e.value=t;e.observers&&e.observers.length&&A(()=>{for(let t=0;t1e6)throw H=[],Error()},!1)}return t}function E(e){if(!e.fn)return;N(e);let t=Pe;D(e,B&&B.running&&B.sources.has(e)?e.tValue:e.value,t),B&&!B.running&&B.sources.has(e)&&queueMicrotask(()=>{A(()=>{B&&(B.running=!0),V=z=e,D(e,e.tValue,t),V=z=null},!1)})}function D(e,t,n){let r,i=z,a=V;V=z=e;try{r=e.fn(t)}catch(t){return e.pure&&(B&&B.running?(e.tState=R,e.tOwned&&e.tOwned.forEach(N),e.tOwned=void 0):(e.state=R,e.owned&&e.owned.forEach(N),e.owned=null)),e.updatedAt=n+1,ce(t)}finally{V=a,z=i}(!e.updatedAt||e.updatedAt<=n)&&(e.updatedAt!=null&&`observers`in e?T(e,r,!0):B&&B.running&&e.pure?(B.sources.has(e)||(e.value=r),B.sources.add(e),e.tValue=r):e.value=r,e.updatedAt=n)}function O(e,t,n,r=R,i){let a={fn:e,state:r,updatedAt:null,owned:null,sources:null,sourceSlots:null,cleanups:null,value:t,owner:z,context:z?z.context:null,pure:n};if(B&&B.running&&(a.state=0,a.tState=r),z===null||z!==Ae&&(B&&B.running&&z.pure?z.tOwned?z.tOwned.push(a):z.tOwned=[a]:z.owned?z.owned.push(a):z.owned=[a]),Ne&&a.fn){let e=a.fn,[t,n]=s(void 0,{equals:!1}),r=Ne.factory(e,n);v(()=>r.dispose());let i,o=()=>x(n).then(()=>{i&&=(i.dispose(),void 0)});a.fn=n=>(t(),B&&B.running?(i||=Ne.factory(e,o),i.track(n)):r.track(n))}return a}function k(e){let t=B&&B.running;if((t?e.tState:e.state)===0)return;if((t?e.tState:e.state)===ke)return re(e);if(e.suspense&&h(e.suspense.inFallback))return e.suspense.effects.push(e);let n=[e];for(;(e=e.owner)&&(!e.updatedAt||e.updatedAt=0;r--){if(e=n[r],t){let t=e,i=n[r+1];for(;(t=t.owner)&&t!==i;)if(B.disposed.has(t))return}if((t?e.tState:e.state)===R)E(e);else if((t?e.tState:e.state)===ke){let t=H;H=null,A(()=>re(e,n[0]),!1),H=t}}}function A(e,t){if(H)return e();let n=!1;t||(H=[]),U?n=!0:U=[],Pe++;try{let t=e();return j(n),t}catch(e){n||(U=null),H=null,ce(e)}}function j(e){if(H&&=(Me&&B&&B.running?M(H):te(H),null),e)return;let t;if(B){if(!B.promises.size&&!B.queue.size){let e=B.sources,n=B.disposed;U.push.apply(U,B.effects),t=B.resolve;for(let e of U)`tState`in e&&(e.state=e.tState),delete e.tState;B=null,A(()=>{for(let e of n)N(e);for(let t of e){if(t.value=t.tValue,t.owned)for(let e=0,n=t.owned.length;eOe(n),!1),t&&t()}function te(e){for(let t=0;t{r.delete(n),A(()=>{B.running=!0,k(n)},!1),B&&(B.running=!1)}))}}function ne(e){let t,n=0;for(t=0;t=0;t--)N(e.tOwned[t]);delete e.tOwned}if(B&&B.running&&e.pure)ae(e,!0);else if(e.owned){for(t=e.owned.length-1;t>=0;t--)N(e.owned[t]);e.owned=null}if(e.cleanups){for(t=e.cleanups.length-1;t>=0;t--)e.cleanups[t]();e.cleanups=null}B&&B.running?e.tState=0:e.state=0}function ae(e,t){if(t||(e.tState=0,B.disposed.add(e)),e.owned)for(let t=0;tn=h(()=>(z.context={...z.context,[e]:t.value},w(()=>t.children))),void 0),n}}function de(e){for(let t=0;t1?[]:null;return v(()=>de(a)),()=>{let u=e()||[],d=u.length,f,p;return u[Te],h(()=>{let e,t,s,h,g,_,v,y,b;if(d===0)c!==0&&(de(a),a=[],r=[],i=[],c=0,l&&=[]),n.fallback&&(r=[Re],i[0]=o(e=>(a[0]=e,n.fallback())),c=1);else if(c===0){for(i=Array(d),p=0;p=_&&y>=_&&r[v]===u[y];v--,y--)s[y]=i[v],h[y]=a[v],l&&(g[y]=l[v]);for(e=new Map,t=Array(y+1),p=y;p>=_;p--)b=u[p],f=e.get(b),t[p]=f===void 0?-1:f,e.set(b,p);for(f=_;f<=v;f++)b=r[f],p=e.get(b),p!==void 0&&p!==-1?(s[p]=i[f],h[p]=a[f],l&&(g[p]=l[f]),p=t[p],e.set(b,p)):a[f]();for(p=_;pe(t||{}));return i(n),r}return h(()=>e(t||{}))}function pe(){return!0}function me(e){return(e=typeof e==`function`?e():e)?e:{}}function he(){for(let e=0,t=this.length;e=0;n--){let r=me(e[n])[t];if(r!==void 0)return r}},has(t){for(let n=e.length-1;n>=0;n--)if(t in me(e[n]))return!0;return!1},keys(){let t=[];for(let n=0;n=0;t--){let i=e[t];if(!i)continue;let a=Object.getOwnPropertyNames(i);for(let e=a.length-1;e>=0;e--){let t=a[e];if(t===`__proto__`||t===`constructor`)continue;let o=Object.getOwnPropertyDescriptor(i,t);if(!r[t])r[t]=o.get?{enumerable:!0,configurable:!0,get:he.bind(n[t]=[o.get.bind(i)])}:o.value===void 0?void 0:o;else{let e=n[t];e&&(o.get?e.push(o.get.bind(i)):o.value!==void 0&&e.push(()=>o.value))}}}let i={},a=Object.keys(r);for(let e=a.length-1;e>=0;e--){let t=a[e],n=r[t];n&&n.get?Object.defineProperty(i,t,n):i[t]=n?n.value:void 0}return i}function ge(e,...t){let n=t.length;if(we&&Ce in e){let r=n>1?t.flat():t[0],i=t.map(t=>new Proxy({get(n){return t.includes(n)?e[n]:void 0},has(n){return t.includes(n)&&n in e},keys(){return t.filter(t=>t in e)}},Be));return i.push(new Proxy({get(t){return r.includes(t)?void 0:e[t]},has(t){return r.includes(t)?!1:t in e},keys(){return Object.keys(e).filter(e=>!r.includes(e))}},Be)),i}let r=[];for(let e=0;e<=n;e++)r[e]={};for(let i of Object.getOwnPropertyNames(e)){let a=n;for(let e=0;e{let a=L.context;if(a){let[r,o]=s();L.count||=0,L.count++,(n||=e()).then(e=>{!L.done&&i(a),L.count--,o(()=>e.default),i()}),t=r}else if(!t){let[r]=p(()=>(n||=e()).then(e=>e.default));t=r}let o;return d(()=>(o=t())?h(()=>{if(!a||L.done)return o(r);let e=L.context;i(a);let t=o(r);return i(e),t}):``)};return r.preload=()=>n||((n=e()).then(e=>t=()=>e.default),n),r}function ve(e){let t=`fallback`in e&&{fallback:()=>e.fallback};return d(fe(()=>e.each,e.children,t||void 0))}function I(e){let t=e.keyed,n=d(()=>e.when,void 0,void 0),r=t?n:d(n,void 0,{equals:(e,t)=>!e==!t});return d(()=>{let i=r();if(i){let a=e.children;return typeof a==`function`&&a.length>0?h(()=>a(t?i:()=>{if(!h(r))throw Ve(`Show`);return n()})):a}return e.fallback},void 0,void 0)}function ye(e){let t=w(()=>e.children),n=d(()=>{let e=t(),n=Array.isArray(e)?e:[e],r=()=>void 0;for(let e=0;ea()?void 0:i.when,void 0,void 0),s=i.keyed?o:d(o,void 0,{equals:(e,t)=>!e==!t});r=()=>a()||(s()?[t,o,i]:void 0)}return r});return d(()=>{let t=n()();if(!t)return e.fallback;let[r,i,a]=t,o=a.children;return typeof o==`function`&&o.length>0?h(()=>o(a.keyed?i():()=>{if(h(n)()?.[0]!==r)throw Ve(`Match`);return i()})):o},void 0,void 0)}function be(e){return e}function xe(){He&&[...He].forEach(e=>e())}var L,Se,Ce,we,Te,Ee,De,Oe,R,ke,Ae,je,z,B,Me,Ne,V,H,U,Pe,Fe,Ie,Le,Re,ze,Be,Ve,He,W=t((()=>{L={context:void 0,registry:void 0,effects:void 0,done:!1,getContextId(){return r(this.context.count)},getNextContextId(){return r(this.context.count++)}},Se=(e,t)=>e===t,Ce=Symbol(`solid-proxy`),we=typeof Proxy==`function`,Te=Symbol(`solid-track`),Ee={equals:Se},De=null,Oe=te,R=1,ke=2,Ae={owned:null,cleanups:null,context:null,owner:null},je={},z=null,B=null,Me=null,Ne=null,V=null,H=null,U=null,Pe=0,[Fe,Ie]=s(!1),Re=Symbol(`fallback`),ze=!1,Be={get(e,t,n){return t===Ce?n:e.get(t)},has(e,t){return t===Ce?!0:e.has(t)},set:pe,deleteProperty:pe,getOwnPropertyDescriptor(e,t){return{configurable:!0,enumerable:!0,get(){return e.get(t)},set:pe,deleteProperty:pe}},ownKeys(e){return e.keys()}},Ve=e=>`Stale read from <${e}>.`}));function Ue(e,t){let n=yt[e];return typeof n==`object`?n[t]?n.$:void 0:n}function We(e,t,n){let r=n.length,i=t.length,a=r,o=0,s=0,c=t[i-1].nextSibling,l=null;for(;or-s){let i=t[o];for(;s{i=r,t===document?e():J(t,e(),t.firstChild?null:void 0,n)},r.owner),()=>{i(),t.textContent=``}}function G(e,t,n,r){let i,a=()=>{let t=r?document.createElementNS(`http://www.w3.org/1998/Math/MathML`,`template`):document.createElement(`template`);return t.innerHTML=e,n?t.content.firstChild.firstChild:r?t.firstChild:t.content.firstChild},o=t?()=>h(()=>document.importNode(i||=a(),!0)):()=>(i||=a()).cloneNode(!0);return o.cloneNode=o,o}function K(e,t=window.document){let n=t[Ct]||(t[Ct]=new Set);for(let r=0,i=e.length;rr.call(e,n[1],t))}else e.addEventListener(t,n,typeof n!=`function`&&n)}function Xe(e,t,n={}){let r=Object.keys(t||{}),i=Object.keys(n),a,o;for(a=0,o=i.length;ai.children=ct(e,t.children,i.children)),l(()=>typeof t.ref==`function`&&et(t.ref,e)),l(()=>tt(e,t,n,!0,i,!0)),i}function et(e,t,n){return h(()=>e(t,n))}function J(e,t,n,r){if(n!==void 0&&!r&&(r=[]),typeof t!=`function`)return ct(e,t,r,n);l(r=>ct(e,t(),r,n),r)}function tt(e,t,n,r,i={},a=!1){t||={};for(let r in i)if(!(r in t)){if(r===`children`)continue;i[r]=ot(e,r,null,i[r],n,a,t)}for(let o in t){if(o===`children`){r||ct(e,t.children);continue}let s=t[o];i[o]=ot(e,o,s,i[o],n,a,t)}}function nt(e){let t,n;return!rt()||!(t=L.registry.get(n=ft()))?e():(L.completed&&L.completed.add(t),L.registry.delete(n),t)}function rt(e){return!!L.context&&!L.done&&(!e||e.isConnected)}function it(e){return e.toLowerCase().replace(/-([a-z])/g,(e,t)=>t.toUpperCase())}function at(e,t,n){let r=t.trim().split(/\s+/);for(let t=0,i=r.length;t-1&&St[t.split(`:`)[0]];r?Ke(e,r,t,n):q(e,vt[t]||t,n)}return n}function st(e){if(L.registry&&L.events&&L.events.find(([t,n])=>n===e))return;let t=e.target,n=`$$${e.type}`,r=e.target,i=e.currentTarget,a=t=>Object.defineProperty(e,"target",{configurable:!0,value:t}),o=()=>{let r=t[n];if(r&&!t.disabled){let i=t[`${n}Data`];if(i===void 0?r.call(t,e):r.call(t,i,e),e.cancelBubble)return}return t.host&&typeof t.host!=`string`&&!t.host._$host&&t.contains(e.target)&&a(t.host),!0},s=()=>{for(;o()&&(t=t._$host||t.parentNode||t.host););};if(Object.defineProperty(e,"currentTarget",{configurable:!0,get(){return t||document}}),L.registry&&!L.done&&(L.done=_$HY.done=!0),e.composedPath){let n=e.composedPath();a(n[0]);for(let e=0;e{let i=t();for(;typeof i==`function`;)i=i();n=ct(e,i,n,r)}),()=>n;else if(Array.isArray(t)){let o=[],c=n&&Array.isArray(n);if(lt(o,t,n,i))return l(()=>n=ct(e,o,n,r,!0)),()=>n;if(a){if(!o.length)return n;if(r===void 0)return n=[...e.childNodes];let t=o[0];if(t.parentNode!==e)return n;let i=[t];for(;(t=t.nextSibling)!==r;)i.push(t);return n=i}if(o.length===0){if(n=dt(e,n,r),s)return n}else c?n.length===0?ut(e,o,r):We(e,n,o):(n&&dt(e),ut(e,o));n=o}else if(t.nodeType){if(a&&t.parentNode)return n=s?[t]:t;if(Array.isArray(n)){if(s)return n=dt(e,n,r,t);dt(e,n,null,t)}else n==null||n===``||!e.firstChild?e.appendChild(t):e.replaceChild(t,e.firstChild);n=t}return n}function lt(e,t,n,r){let i=!1;for(let a=0,o=t.length;a=0;a--){let o=t[a];if(i!==o){let t=o.parentNode===e;!r&&!a?t?e.replaceChild(i,o):e.insertBefore(i,n):t&&o.remove()}else r=!0}}else e.insertBefore(i,n);return[i]}function ft(){return L.getNextContextId()}function pt(e,t=!1,n=void 0){return t?document.createElementNS(wt,e):document.createElement(e,{is:n})}function mt(e,t){let n=d(e);return d(()=>{let e=n();switch(typeof e){case`function`:return h(()=>e(t));case`string`:let n=xt.has(e),r=L.context?nt():pt(e,n,h(()=>t.is));return $e(r,t,n),r}})}function ht(e){let[,t]=ge(e,[`component`]);return mt(()=>e.component,t)}var gt,_t,vt,yt,bt,xt,St,Y,Ct,wt,X=t((()=>{W(),gt=new Set([`className`,`value`,`readOnly`,`noValidate`,`formNoValidate`,`isMap`,`noModule`,`playsInline`,`adAuctionHeaders`,`allowFullscreen`,`browsingTopics`,`defaultChecked`,`defaultMuted`,`defaultSelected`,`disablePictureInPicture`,`disableRemotePlayback`,`preservesPitch`,`shadowRootClonable`,`shadowRootCustomElementRegistry`,`shadowRootDelegatesFocus`,`shadowRootSerializable`,`sharedStorageWritable`,...`allowfullscreen.async.alpha.autofocus.autoplay.checked.controls.default.disabled.formnovalidate.hidden.indeterminate.inert.ismap.loop.multiple.muted.nomodule.novalidate.open.playsinline.readonly.required.reversed.seamless.selected.adauctionheaders.browsingtopics.credentialless.defaultchecked.defaultmuted.defaultselected.defer.disablepictureinpicture.disableremoteplayback.preservespitch.shadowrootclonable.shadowrootcustomelementregistry.shadowrootdelegatesfocus.shadowrootserializable.sharedstoragewritable`.split(`.`)]),_t=new Set([`innerHTML`,`textContent`,`innerText`,`children`]),vt=Object.assign(Object.create(null),{className:`class`,htmlFor:`for`}),yt=Object.assign(Object.create(null),{class:`className`,novalidate:{$:`noValidate`,FORM:1},formnovalidate:{$:`formNoValidate`,BUTTON:1,INPUT:1},ismap:{$:`isMap`,IMG:1},nomodule:{$:`noModule`,SCRIPT:1},playsinline:{$:`playsInline`,VIDEO:1},readonly:{$:`readOnly`,INPUT:1,TEXTAREA:1},adauctionheaders:{$:`adAuctionHeaders`,IFRAME:1},allowfullscreen:{$:`allowFullscreen`,IFRAME:1},browsingtopics:{$:`browsingTopics`,IMG:1},defaultchecked:{$:`defaultChecked`,INPUT:1},defaultmuted:{$:`defaultMuted`,AUDIO:1,VIDEO:1},defaultselected:{$:`defaultSelected`,OPTION:1},disablepictureinpicture:{$:`disablePictureInPicture`,VIDEO:1},disableremoteplayback:{$:`disableRemotePlayback`,AUDIO:1,VIDEO:1},preservespitch:{$:`preservesPitch`,AUDIO:1,VIDEO:1},shadowrootclonable:{$:`shadowRootClonable`,TEMPLATE:1},shadowrootdelegatesfocus:{$:`shadowRootDelegatesFocus`,TEMPLATE:1},shadowrootserializable:{$:`shadowRootSerializable`,TEMPLATE:1},sharedstoragewritable:{$:`sharedStorageWritable`,IFRAME:1,IMG:1}}),bt=new Set([`beforeinput`,`click`,`dblclick`,`contextmenu`,`focusin`,`focusout`,`input`,`keydown`,`keyup`,`mousedown`,`mousemove`,`mouseout`,`mouseover`,`mouseup`,`pointerdown`,`pointermove`,`pointerout`,`pointerover`,`pointerup`,`touchend`,`touchmove`,`touchstart`]),xt=new Set(`altGlyph.altGlyphDef.altGlyphItem.animate.animateColor.animateMotion.animateTransform.circle.clipPath.color-profile.cursor.defs.desc.ellipse.feBlend.feColorMatrix.feComponentTransfer.feComposite.feConvolveMatrix.feDiffuseLighting.feDisplacementMap.feDistantLight.feDropShadow.feFlood.feFuncA.feFuncB.feFuncG.feFuncR.feGaussianBlur.feImage.feMerge.feMergeNode.feMorphology.feOffset.fePointLight.feSpecularLighting.feSpotLight.feTile.feTurbulence.filter.font.font-face.font-face-format.font-face-name.font-face-src.font-face-uri.foreignObject.g.glyph.glyphRef.hkern.image.line.linearGradient.marker.mask.metadata.missing-glyph.mpath.path.pattern.polygon.polyline.radialGradient.rect.set.stop.svg.switch.symbol.text.textPath.tref.tspan.use.view.vkern`.split(`.`)),St={xlink:`http://www.w3.org/1999/xlink`,xml:`http://www.w3.org/XML/1998/namespace`},Y=e=>d(()=>e()),Ct=`_$DX_DELEGATE`,wt=`http://www.w3.org/2000/svg`}));X();function Tt(){let e=new Set;function t(t){return e.add(t),()=>e.delete(t)}let n=!1;function r(t,r){if(n)return!(n=!1);let i={to:t,options:r,defaultPrevented:!1,preventDefault:()=>i.defaultPrevented=!0};for(let a of e)a.listener({...i,from:a.location,retry:e=>{e&&(n=!0),a.navigate(t,{...r,resolve:!1})}});return!i.defaultPrevented}return{subscribe:t,confirm:r}}function Et(){(!window.history.state||window.history.state._depth==null)&&window.history.replaceState({...window.history.state,_depth:window.history.length-1},``),kt=window.history.state._depth}function Dt(e){return{...e,_depth:window.history.state&&window.history.state._depth}}function Ot(e,t){let n=!1;return()=>{let r=kt;Et();let i=r==null?null:kt-r;if(n){n=!1;return}i&&t(i)?(n=!0,window.history.go(-i)):e()}}var kt,At=t((()=>{Et()}));function jt(e,t=!1){let n=e.replace(Ht,`$1`);return n?t||/^[?#]/.test(n)?n:`/`+n:``}function Mt(e,t,n){if(Vt.test(t))return;let r=jt(e),i=n&&jt(n),a=``;return a=!i||t.startsWith(`/`)?r:i.toLowerCase().indexOf(r.toLowerCase())===0?i:r+i,(a||`/`)+jt(t,!a)}function Nt(e,t){if(e==null)throw Error(t);return e}function Pt(e,t){return jt(e).replace(/\/*(\*.*)?$/g,``)+jt(t)}function Ft(e){let t={};return e.searchParams.forEach((e,n)=>{n in t?Array.isArray(t[n])?t[n].push(e):t[n]=[t[n],e]:t[n]=e}),t}function It(e,t,n){let[r,i]=e.split(`/*`,2),a=r.split(`/`).filter(Boolean),o=a.length;return e=>{let r=e.split(`/`).filter(Boolean),s=r.length-o;if(s<0||s>0&&i===void 0&&!t)return null;let c={path:o?``:`/`,params:{}},l=e=>n===void 0?void 0:n[e];for(let e=0;et===e;return t===void 0?!0:typeof t==`string`?n(t):typeof t==`function`?t(e):Array.isArray(t)?t.some(n):t instanceof RegExp?t.test(e):!1}function Rt(e){let[t,n]=e.pattern.split(`/*`,2),r=t.split(`/`).filter(Boolean);return r.reduce((e,t)=>e+(t.startsWith(`:`)?2:3),r.length-(n===void 0?0:1))}function zt(e){let t=new Map,n=y();return new Proxy({},{get(r,i){return t.has(i)||b(n,()=>t.set(i,d(()=>e()[i]))),t.get(i)()},getOwnPropertyDescriptor(){return{enumerable:!0,configurable:!0}},ownKeys(){return Reflect.ownKeys(e())},has(t,n){return n in e()}})}function Bt(e){let t=/(\/?\:[^\/]+)\?/.exec(e);if(!t)return[e];let n=e.slice(0,t.index),r=e.slice(t.index+t[0].length),i=[n,n+=t[1]];for(;t=/^(\/\:[^\/]+)\?/.exec(r);)i.push(n+=t[1]),r=r.slice(t[0].length);return Bt(r).reduce((e,t)=>[...e,...i.map(e=>e+t)],[])}var Vt,Ht,Ut,Wt=t((()=>{W(),Vt=/^(?:[a-z0-9]+:)?\/\//i,Ht=/^\/+|(\/)\/+$/g,Ut=`http://sr`}));function Gt(e,t=``){let{component:n,preload:r,load:i,children:a,info:o}=e,s=!a||Array.isArray(a)&&!a.length,c={key:e,component:n,preload:r||i,info:o};return qt(e.path).reduce((n,r)=>{for(let i of Bt(r)){let a=Pt(t,i),o=s?a:a.split(`/*`,1)[0];o=o.split(`/`).map(e=>e.startsWith(`:`)||e.startsWith(`*`)?e:encodeURIComponent(e)).join(`/`),n.push({...c,originalPath:r,pattern:o,matcher:It(o,!s,e.matchFilters)})}return n},[])}function Kt(e,t=0){return{routes:e,score:Rt(e[e.length-1])*1e4-t,matcher(t){let n=[];for(let r=e.length-1;r>=0;r--){let i=e[r],a=i.matcher(t);if(!a)return null;n.unshift({...a,route:i})}return n}}}function qt(e){return Array.isArray(e)?e:[e]}function Jt(e,t=``,n=[],r=[]){let i=qt(e);for(let e=0,a=i.length;et.score-e.score)}function Yt(e,t){for(let n=0,r=e.length;n{let n=e();try{return new URL(n,r)}catch{return console.error(`Invalid path ${n}`),t}},r,{equals:(e,t)=>e.href===t.href}),a=d(()=>i().pathname),o=d(()=>i().search,!0),s=d(()=>i().hash),c=()=>``,l=g(o,()=>Ft(i()));return{get pathname(){return a()},get search(){return o()},get hash(){return s()},get state(){return t()},get key(){return c()},query:n?n(l):zt(l)}}function Zt(){return fn}function Qt(e){pn=e}function $t(e,t,n,r={}){let{signal:[i,a],utils:o={}}=e,c=o.parsePath||(e=>e),u=o.renderPath||(e=>e),f=o.beforeLeave||Tt(),p=Mt(``,r.base||``);if(p===void 0)throw Error(`${p} is not a valid base path`);p&&!i().value&&a({value:p,replace:!0,scroll:!1});let[_,v]=s(!1),y,S=(e,t)=>{t.value===w()&&t.state===T()||(y===void 0&&v(!0),fn=e,y=t,x(()=>{y===t&&(ee(y.value),E(y.state),xe(),k[1](e=>e.filter(e=>e.pending)))}).finally(()=>{y===t&&m(()=>{fn=void 0,e===`navigate`&&ie(y),v(!1),y=void 0})}))},[w,ee]=s(i().value),[T,E]=s(i().state),D=Xt(w,T,o.queryWrapper),O=[],k=s([]),A=d(()=>typeof r.transformUrl==`function`?Yt(t(),r.transformUrl(D.pathname)):Yt(t(),D.pathname)),j=()=>{let e=A(),t={};for(let n=0;np,outlet:()=>null,resolvePath(e){return Mt(p,e)}};return l(g(i,e=>S(`native`,e),{defer:!0})),{base:M,location:D,params:te,isRouting:_,renderPath:u,parsePath:c,navigatorFactory:re,matches:A,beforeLeave:f,preloadRoute:N,singleFlight:r.singleFlight===void 0?!0:r.singleFlight,submissions:k};function ne(e,t,n){h(()=>{if(typeof t==`number`){t&&(o.go?o.go(t):console.warn(`Router integration does not support relative routing`));return}let r=!t||t[0]===`?`,{replace:i,resolve:a,scroll:s,state:c}={replace:!1,resolve:!r,scroll:!0,...n},l=a?e.resolvePath(t):Mt(r&&D.pathname||``,t);if(l===void 0)throw Error(`Path '${t}' is not a routable path`);if(O.length>=tn)throw Error(`Too many redirects`);let u=w();(l!==u||c!==T())&&f.confirm(l,n)&&(O.push({value:u,replace:i,scroll:s,state:T()}),S(`navigate`,{value:l,state:c}))})}function re(e){return e=e||C(rn)||M,(t,n)=>ne(e,t,n)}function ie(e){let t=O[0];t&&(a({...e,replace:t.replace,scroll:t.scroll}),O.length=0)}function N(e,r){let i=Yt(t(),e.pathname),a=fn;fn=`preload`;for(let t in i){let{route:a,params:o}=i[t];a.component&&a.component.preload&&a.component.preload();let{preload:s}=a;pn=!0,r&&s&&b(n(),()=>s({params:o,location:{pathname:e.pathname,search:e.search,hash:e.hash,query:Ft(e),state:null,key:``},intent:`preload`})),pn=!1}fn=a}}function en(e,t,n,r){let{base:i,location:a,params:o}=e,{pattern:s,component:c,preload:l}=r().route,u=d(()=>r().path);c&&c.preload&&c.preload(),pn=!0;let f=l?l({params:o,location:a,intent:fn||`initial`}):void 0;return pn=!1,{parent:t,pattern:s,path:u,outlet:()=>c?P(c,{params:o,location:a,data:f,get children(){return n()}}):n(),resolvePath(e){return Mt(i.path(),e,u())}}}var tn,nn,rn,an,on,sn,cn,ln,un,dn,fn,pn,mn=t((()=>{W(),X(),At(),Wt(),tn=100,nn=S(),rn=S(),an=()=>Nt(C(nn),` and 'use' router primitives can be only used inside a Route.`),on=()=>C(rn)||an().base,sn=e=>{let t=on();return d(()=>t.resolvePath(e()))},cn=e=>{let t=an();return d(()=>{let n=e();return n===void 0?n:t.renderPath(n)})},ln=()=>an().navigatorFactory(),un=()=>an().location,dn=()=>an().params}));function hn(e){let t=e.routerState.location,n=e.routerState.params,r=d(()=>e.preload&&h(()=>{Qt(!0),e.preload({params:n,location:t,intent:Zt()||`initial`}),Qt(!1)}));return P(I,{get when(){return e.root},keyed:!0,get fallback(){return e.children},children:i=>P(i,{params:n,location:t,get data(){return r()},get children(){return e.children}})})}function gn(e){let t=[],n,r=d(g(e.routerState.matches,(i,a,s)=>{let c=a&&i.length===a.length,l=[];for(let n=0,u=i.length;n{t[n]=i,l[n]=en(e.routerState,l[n-1]||e.routerState.base,vn(()=>r()[n+1]),()=>{let t=e.routerState.matches();return t[n]??t[0]})}))}return t.splice(i.length).forEach(e=>e()),s&&c?s:(n=l[0],l)}));return vn(()=>r()&&n)()}var _n,vn,yn,bn=t((()=>{X(),W(),mn(),_n=e=>t=>{let{base:n}=t,r=w(()=>t.children),i=d(()=>Jt(r(),t.base||``)),a,o=$t(e,i,()=>a,{base:n,singleFlight:t.singleFlight,transformUrl:t.transformUrl});return e.create&&e.create(o),P(nn.Provider,{value:o,get children(){return P(hn,{routerState:o,get root(){return t.root},get preload(){return t.rootPreload||t.rootLoad},get children(){return[Y(()=>(a=y())&&null),P(gn,{routerState:o,get branches(){return i()}})]}})}})},vn=e=>()=>P(I,{get when(){return e()},keyed:!0,children:e=>P(rn.Provider,{value:e,get children(){return e.outlet()}})}),yn=e=>{let t=w(()=>e.children);return F(e,{get children(){return t()}})}}));function xn([e,t],n,r){return[n?()=>n(e()):e,r?e=>t(r(e)):t]}function Sn(e){let t=!1,n=e=>typeof e==`string`?{value:e}:e,r=xn(s(n(e.get()),{equals:(e,t)=>e.value===t.value&&e.state===t.state}),void 0,n=>(!t&&e.set(n),L.registry&&!L.done&&(L.done=!0),n));return e.init&&v(e.init((i=e.get())=>{t=!0,r[1](n(i)),t=!1})),_n({signal:r,create:e.create,utils:e.utils})}function Cn(e,t,n){return e.addEventListener(t,n),()=>e.removeEventListener(t,n)}function wn(e,t){let n=e&&document.getElementById(e);n?n.scrollIntoView():t&&window.scrollTo(0,0)}var Tn=t((()=>{W(),bn()})),En,Dn=t((()=>{En=new Map}));function On(e=!0,t=!1,n=`/_server`,r){return i=>{let a=i.base.path(),o=i.navigatorFactory(i.base),s,c;function l(e){return e.namespaceURI===`http://www.w3.org/2000/svg`}function u(e){if(e.defaultPrevented||e.button!==0||e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)return;let n=e.composedPath().find(e=>e instanceof Node&&e.nodeName.toUpperCase()===`A`);if(!n||t&&!n.hasAttribute(`link`))return;let r=l(n),i=r?n.href.baseVal:n.href;if((r?n.target.baseVal:n.target)||!i&&!n.hasAttribute(`state`))return;let o=(n.getAttribute(`rel`)||``).split(/\s+/);if(n.hasAttribute(`download`)||o&&o.includes(`external`))return;let s=r?new URL(i,document.baseURI):new URL(i);if(!(s.origin!==window.location.origin||a&&s.pathname&&!s.pathname.toLowerCase().startsWith(a.toLowerCase())))return[n,s]}function d(e){let t=u(e);if(!t)return;let[n,r]=t,a=i.parsePath(r.pathname+r.search+r.hash),s=n.getAttribute(`state`);e.preventDefault(),o(a,{resolve:!1,replace:n.hasAttribute(`replace`),scroll:!n.hasAttribute(`noscroll`),state:s?JSON.parse(s):void 0})}function f(e){let t=u(e);if(!t)return;let[n,a]=t;r&&(a.pathname=r(a.pathname)),i.preloadRoute(a,n.getAttribute(`preload`)!==`false`)}function p(e){clearTimeout(s);let t=u(e);if(!t)return c=null;let[n,a]=t;c!==n&&(r&&(a.pathname=r(a.pathname)),s=setTimeout(()=>{i.preloadRoute(a,n.getAttribute(`preload`)!==`false`),c=n},20))}function m(e){if(e.defaultPrevented)return;let t=e.submitter&&e.submitter.hasAttribute(`formaction`)?e.submitter.getAttribute(`formaction`):e.target.getAttribute(`action`);if(!t)return;if(!t.startsWith(`https://action/`)){let e=new URL(t,Ut);if(t=i.parsePath(e.pathname+e.search),!t.startsWith(n))return}if(e.target.method.toUpperCase()!==`POST`)throw Error(`Only POST forms are supported for Actions`);let r=En.get(t);if(r){e.preventDefault();let t=new FormData(e.target,e.submitter);r.call({r:i,f:e.target},e.target.enctype===`multipart/form-data`?t:new URLSearchParams(t))}}K([`click`,`submit`]),document.addEventListener(`click`,d),e&&(document.addEventListener(`mousemove`,p,{passive:!0}),document.addEventListener(`focusin`,f,{passive:!0}),document.addEventListener(`touchstart`,f,{passive:!0})),document.addEventListener(`submit`,m),v(()=>{document.removeEventListener(`click`,d),e&&(document.removeEventListener(`mousemove`,p),document.removeEventListener(`focusin`,f),document.removeEventListener(`touchstart`,f)),document.removeEventListener(`submit`,m)})}}var kn=t((()=>{X(),W(),Dn(),Wt()}));function An(e){let t=e.replace(/^.*?#/,``);if(!t.startsWith(`/`)){let[,e=`/`]=window.location.hash.split(`#`,2);return`${e}#${t}`}return t}function jn(e){let t=()=>window.location.hash.slice(1),n=Tt();return Sn({get:t,set({value:e,replace:t,scroll:n,state:r}){t?window.history.replaceState(Dt(r),``,`#`+e):window.history.pushState(r,``,`#`+e);let i=e.indexOf(`#`);wn(i>=0?e.slice(i+1):``,n),Et()},init:e=>Cn(window,`hashchange`,Ot(e,e=>!n.confirm(e&&e<0?e:t()))),create:On(e.preload,e.explicitLinks,e.actionBase),utils:{go:e=>window.history.go(e),renderPath:e=>`#${e}`,parsePath:An,beforeLeave:n}})(e)}var Mn=t((()=>{kn(),Tn(),At()})),Nn=t((()=>{bn(),Tn(),X(),kn(),At(),Mn()}));function Pn(e){e=F({inactiveClass:`inactive`,activeClass:`active`},e);let[,t]=ge(e,[`href`,`state`,`class`,`activeClass`,`inactiveClass`,`end`]),n=sn(()=>e.href),r=cn(n),i=un(),a=d(()=>{let t=n();if(t===void 0)return[!1,!1];let r=jt(t.split(/[?#]/,1)[0]).toLowerCase(),a=decodeURI(jt(i.pathname).toLowerCase());return[e.end?r===a:a.startsWith(r+`/`)||a===r,r===a]});return(()=>{var n=Fn();return $e(n,F(t,{get href(){return r()||e.href},get state(){return JSON.stringify(e.state)},get classList(){return{...e.class&&{[e.class]:!0},[e.inactiveClass]:!a()[0],[e.activeClass]:a()[0],...t.classList}},link:``,get"aria-current"(){return a()[1]?`page`:void 0}}),!1,!1),n})()}var Fn,In=t((()=>{X(),W(),mn(),Wt(),Fn=G(``)})),Ln=t((()=>{W(),X(),Dn(),mn()})),Rn=t((()=>{Nn(),In(),At(),mn(),Wt(),Ln()})),zn,Bn=t((()=>{zn={xmlns:`http://www.w3.org/2000/svg`,width:24,height:24,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,"stroke-width":2,"stroke-linecap":`round`,"stroke-linejoin":`round`}})),Vn,Hn,Un,Wn,Z,Q=t((()=>{X(),W(),Bn(),Vn=G(``),Hn=e=>e.replace(/([a-z0-9])([A-Z])/g,`$1-$2`).toLowerCase(),Un=(...e)=>e.filter((e,t,n)=>!!e&&e.trim()!==``&&n.indexOf(e)===t).join(` `).trim(),Wn=e=>{let[t,n]=ge(e,[`color`,`size`,`strokeWidth`,`children`,`class`,`name`,`iconNode`,`absoluteStrokeWidth`]);return(()=>{var e=Vn();return $e(e,F(zn,{get width(){return t.size??zn.width},get height(){return t.size??zn.height},get stroke(){return t.color??zn.stroke},get"stroke-width"(){return Y(()=>!!t.absoluteStrokeWidth)()?Number(t.strokeWidth??zn[`stroke-width`])*24/Number(t.size):Number(t.strokeWidth??zn[`stroke-width`])},get class(){return Un(`lucide`,`lucide-icon`,t.name==null?void 0:`lucide-${Hn(t?.name)}`,t.class==null?``:t.class)}},n),!0,!0),J(e,P(ve,{get each(){return t.iconNode},children:([e,t])=>P(ht,F({component:e},t))})),e})()},Z=Wn})),Gn,Kn,qn,Jn=t((()=>{X(),Q(),Gn=[[`path`,{d:`M22 12h-2.48a2 2 0 0 0-1.93 1.46l-2.35 8.36a.25.25 0 0 1-.48 0L9.24 2.18a.25.25 0 0 0-.48 0l-2.35 8.36A2 2 0 0 1 4.49 12H2`,key:`169zse`}]],Kn=e=>P(Z,F(e,{name:`Activity`,iconNode:Gn})),qn=Kn})),Yn,Xn,Zn,Qn=t((()=>{X(),Q(),Yn=[[`path`,{d:`m12 19-7-7 7-7`,key:`1l729n`}],[`path`,{d:`M19 12H5`,key:`x3x0zl`}]],Xn=e=>P(Z,F(e,{name:`ArrowLeft`,iconNode:Yn})),Zn=Xn})),$n,er,tr,nr=t((()=>{X(),Q(),$n=[[`path`,{d:`M12 7v14`,key:`1akyts`}],[`path`,{d:`M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z`,key:`ruj8y`}]],er=e=>P(Z,F(e,{name:`BookOpen`,iconNode:$n})),tr=er})),rr,ir,ar,or=t((()=>{X(),Q(),rr=[[`path`,{d:`m6 9 6 6 6-6`,key:`qrunsl`}]],ir=e=>P(Z,F(e,{name:`ChevronDown`,iconNode:rr})),ar=ir})),sr,cr,lr,ur=t((()=>{X(),Q(),sr=[[`path`,{d:`m9 18 6-6-6-6`,key:`mthhwq`}]],cr=e=>P(Z,F(e,{name:`ChevronRight`,iconNode:sr})),lr=cr})),dr,fr,pr,mr=t((()=>{X(),Q(),dr=[[`path`,{d:`m18 15-6-6-6 6`,key:`153udz`}]],fr=e=>P(Z,F(e,{name:`ChevronUp`,iconNode:dr})),pr=fr})),hr,gr,_r,vr=t((()=>{X(),Q(),hr=[[`path`,{d:`M21.801 10A10 10 0 1 1 17 3.335`,key:`yps3ct`}],[`path`,{d:`m9 11 3 3L22 4`,key:`1pflzl`}]],gr=e=>P(Z,F(e,{name:`CircleCheckBig`,iconNode:hr})),_r=gr})),yr,br,xr,Sr=t((()=>{X(),Q(),yr=[[`path`,{d:`M10.1 2.182a10 10 0 0 1 3.8 0`,key:`5ilxe3`}],[`path`,{d:`M13.9 21.818a10 10 0 0 1-3.8 0`,key:`11zvb9`}],[`path`,{d:`M17.609 3.721a10 10 0 0 1 2.69 2.7`,key:`1iw5b2`}],[`path`,{d:`M2.182 13.9a10 10 0 0 1 0-3.8`,key:`c0bmvh`}],[`path`,{d:`M20.279 17.609a10 10 0 0 1-2.7 2.69`,key:`1ruxm7`}],[`path`,{d:`M21.818 10.1a10 10 0 0 1 0 3.8`,key:`qkgqxc`}],[`path`,{d:`M3.721 6.391a10 10 0 0 1 2.7-2.69`,key:`1mcia2`}],[`path`,{d:`M6.391 20.279a10 10 0 0 1-2.69-2.7`,key:`1fvljs`}]],br=e=>P(Z,F(e,{name:`CircleDashed`,iconNode:yr})),xr=br})),Cr,wr,Tr,Er=t((()=>{X(),Q(),Cr=[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}],[`path`,{d:`m15 9-6 6`,key:`1uzhvr`}],[`path`,{d:`m9 9 6 6`,key:`z0biqf`}]],wr=e=>P(Z,F(e,{name:`CircleX`,iconNode:Cr})),Tr=wr})),Dr,Or,kr,Ar=t((()=>{X(),Q(),Dr=[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}],[`polyline`,{points:`12 6 12 12 16 14`,key:`68esgv`}]],Or=e=>P(Z,F(e,{name:`Clock`,iconNode:Dr})),kr=Or})),jr,Mr,Nr,Pr=t((()=>{X(),Q(),jr=[[`path`,{d:`M4 22h14a2 2 0 0 0 2-2V7l-5-5H6a2 2 0 0 0-2 2v4`,key:`1pf5j1`}],[`path`,{d:`M14 2v4a2 2 0 0 0 2 2h4`,key:`tnqrlb`}],[`path`,{d:`m5 12-3 3 3 3`,key:`oke12k`}],[`path`,{d:`m9 18 3-3-3-3`,key:`112psh`}]],Mr=e=>P(Z,F(e,{name:`FileCode2`,iconNode:jr})),Nr=Mr})),Fr,Ir,Lr,Rr=t((()=>{X(),Q(),Fr=[[`path`,{d:`M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z`,key:`1rqfz7`}],[`path`,{d:`M14 2v4a2 2 0 0 0 2 2h4`,key:`tnqrlb`}],[`path`,{d:`M9 15h6`,key:`cctwl0`}],[`path`,{d:`M12 18v-6`,key:`17g6i2`}]],Ir=e=>P(Z,F(e,{name:`FilePlus`,iconNode:Fr})),Lr=Ir})),zr,Br,Vr,Hr=t((()=>{X(),Q(),zr=[[`path`,{d:`M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z`,key:`1rqfz7`}],[`path`,{d:`M14 2v4a2 2 0 0 0 2 2h4`,key:`tnqrlb`}],[`path`,{d:`M10 9H8`,key:`b1mrlr`}],[`path`,{d:`M16 13H8`,key:`t4e002`}],[`path`,{d:`M16 17H8`,key:`z1uh3a`}]],Br=e=>P(Z,F(e,{name:`FileText`,iconNode:zr})),Vr=Br})),Ur,Wr,Gr,Kr=t((()=>{X(),Q(),Ur=[[`path`,{d:`m6 14 1.5-2.9A2 2 0 0 1 9.24 10H20a2 2 0 0 1 1.94 2.5l-1.54 6a2 2 0 0 1-1.95 1.5H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h3.9a2 2 0 0 1 1.69.9l.81 1.2a2 2 0 0 0 1.67.9H18a2 2 0 0 1 2 2v2`,key:`usdka0`}]],Wr=e=>P(Z,F(e,{name:`FolderOpen`,iconNode:Ur})),Gr=Wr})),qr,Jr,Yr,Xr=t((()=>{X(),Q(),qr=[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}],[`path`,{d:`M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20`,key:`13o1zl`}],[`path`,{d:`M2 12h20`,key:`9i4pu4`}]],Jr=e=>P(Z,F(e,{name:`Globe`,iconNode:qr})),Yr=Jr})),Zr,Qr,$r,ei=t((()=>{X(),Q(),Zr=[[`path`,{d:`M15 21v-8a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v8`,key:`5wwlr5`}],[`path`,{d:`M3 10a2 2 0 0 1 .709-1.528l7-5.999a2 2 0 0 1 2.582 0l7 5.999A2 2 0 0 1 21 10v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z`,key:`1d0kgt`}]],Qr=e=>P(Z,F(e,{name:`House`,iconNode:Zr})),$r=Qr})),ti,ni,ri,ii=t((()=>{X(),Q(),ti=[[`line`,{x1:`2`,x2:`22`,y1:`2`,y2:`22`,key:`a6p6uj`}],[`path`,{d:`M10.41 10.41a2 2 0 1 1-2.83-2.83`,key:`1bzlo9`}],[`line`,{x1:`13.5`,x2:`6`,y1:`13.5`,y2:`21`,key:`1q0aeu`}],[`line`,{x1:`18`,x2:`21`,y1:`12`,y2:`15`,key:`5mozeu`}],[`path`,{d:`M3.59 3.59A1.99 1.99 0 0 0 3 5v14a2 2 0 0 0 2 2h14c.55 0 1.052-.22 1.41-.59`,key:`mmje98`}],[`path`,{d:`M21 15V5a2 2 0 0 0-2-2H9`,key:`43el77`}]],ni=e=>P(Z,F(e,{name:`ImageOff`,iconNode:ti})),ri=ni})),ai,oi,si,ci=t((()=>{X(),Q(),ai=[[`rect`,{width:`18`,height:`18`,x:`3`,y:`3`,rx:`2`,ry:`2`,key:`1m3agn`}],[`circle`,{cx:`9`,cy:`9`,r:`2`,key:`af1f0g`}],[`path`,{d:`m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21`,key:`1xmnt7`}]],oi=e=>P(Z,F(e,{name:`Image`,iconNode:ai})),si=oi})),li,ui,di,fi=t((()=>{X(),Q(),li=[[`path`,{d:`m5 8 6 6`,key:`1wu5hv`}],[`path`,{d:`m4 14 6-6 2-3`,key:`1k1g8d`}],[`path`,{d:`M2 5h12`,key:`or177f`}],[`path`,{d:`M7 2h1`,key:`1t2jsx`}],[`path`,{d:`m22 22-5-10-5 10`,key:`don7ne`}],[`path`,{d:`M14 18h6`,key:`1m8k6r`}]],ui=e=>P(Z,F(e,{name:`Languages`,iconNode:li})),di=ui})),pi,mi,hi,gi=t((()=>{X(),Q(),pi=[[`path`,{d:`M12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 1.66 0l8.58-3.9a1 1 0 0 0 0-1.83z`,key:`zw3jo`}],[`path`,{d:`M2 12a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 12`,key:`1wduqc`}],[`path`,{d:`M2 17a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 17`,key:`kqbvx6`}]],mi=e=>P(Z,F(e,{name:`Layers`,iconNode:pi})),hi=mi})),_i,vi,yi,bi=t((()=>{X(),Q(),_i=[[`rect`,{width:`20`,height:`14`,x:`2`,y:`3`,rx:`2`,key:`48i651`}],[`line`,{x1:`8`,x2:`16`,y1:`21`,y2:`21`,key:`1svkeh`}],[`line`,{x1:`12`,x2:`12`,y1:`17`,y2:`21`,key:`vw1qmm`}]],vi=e=>P(Z,F(e,{name:`Monitor`,iconNode:_i})),yi=vi})),xi,Si,Ci,wi=t((()=>{X(),Q(),xi=[[`path`,{d:`M12 20h9`,key:`t2du7b`}],[`path`,{d:`M16.376 3.622a1 1 0 0 1 3.002 3.002L7.368 18.635a2 2 0 0 1-.855.506l-2.872.838a.5.5 0 0 1-.62-.62l.838-2.872a2 2 0 0 1 .506-.854z`,key:`1ykcvy`}]],Si=e=>P(Z,F(e,{name:`PenLine`,iconNode:xi})),Ci=Si})),Ti,Ei,Di,Oi=t((()=>{X(),Q(),Ti=[[`path`,{d:`M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z`,key:`1a8usu`}]],Ei=e=>P(Z,F(e,{name:`Pen`,iconNode:Ti})),Di=Ei})),ki,Ai,ji,Mi=t((()=>{X(),Q(),ki=[[`path`,{d:`M5 12h14`,key:`1ays0h`}],[`path`,{d:`M12 5v14`,key:`s699le`}]],Ai=e=>P(Z,F(e,{name:`Plus`,iconNode:ki})),ji=Ai})),Ni,Pi,Fi,Ii=t((()=>{X(),Q(),Ni=[[`path`,{d:`M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8`,key:`v9h5vc`}],[`path`,{d:`M21 3v5h-5`,key:`1q7to0`}],[`path`,{d:`M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16`,key:`3uifl3`}],[`path`,{d:`M8 16H3v5`,key:`1cv678`}]],Pi=e=>P(Z,F(e,{name:`RefreshCw`,iconNode:Ni})),Fi=Pi})),Li,Ri,zi,Bi=t((()=>{X(),Q(),Li=[[`path`,{d:`M3 7V5a2 2 0 0 1 2-2h2`,key:`aa7l1z`}],[`path`,{d:`M17 3h2a2 2 0 0 1 2 2v2`,key:`4qcy5o`}],[`path`,{d:`M21 17v2a2 2 0 0 1-2 2h-2`,key:`6vwrx8`}],[`path`,{d:`M7 21H5a2 2 0 0 1-2-2v-2`,key:`ioqczr`}],[`path`,{d:`M7 8h8`,key:`1jbsf9`}],[`path`,{d:`M7 12h10`,key:`b7w52i`}],[`path`,{d:`M7 16h6`,key:`1vyc9m`}]],Ri=e=>P(Z,F(e,{name:`ScanText`,iconNode:Li})),zi=Ri})),Vi,Hi,Ui,Wi=t((()=>{X(),Q(),Vi=[[`circle`,{cx:`12`,cy:`12`,r:`3`,key:`1v7zrd`}],[`path`,{d:`M4.5 10H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v4a2 2 0 0 1-2 2h-.5`,key:`tn8das`}],[`path`,{d:`M4.5 14H4a2 2 0 0 0-2 2v4a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-4a2 2 0 0 0-2-2h-.5`,key:`1g2pve`}],[`path`,{d:`M6 6h.01`,key:`1utrut`}],[`path`,{d:`M6 18h.01`,key:`uhywen`}],[`path`,{d:`m15.7 13.4-.9-.3`,key:`1jwmzr`}],[`path`,{d:`m9.2 10.9-.9-.3`,key:`qapnim`}],[`path`,{d:`m10.6 15.7.3-.9`,key:`quwk0k`}],[`path`,{d:`m13.6 15.7-.4-1`,key:`cb9xp7`}],[`path`,{d:`m10.8 9.3-.4-1`,key:`1uaiz5`}],[`path`,{d:`m8.3 13.6 1-.4`,key:`s6srou`}],[`path`,{d:`m14.7 10.8 1-.4`,key:`4d31cq`}],[`path`,{d:`m13.4 8.3-.3.9`,key:`1bm987`}]],Hi=e=>P(Z,F(e,{name:`ServerCog`,iconNode:Vi})),Ui=Hi})),Gi,Ki,qi,Ji=t((()=>{X(),Q(),Gi=[[`path`,{d:`M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z`,key:`oel41y`}],[`path`,{d:`M12 8v4`,key:`1got3b`}],[`path`,{d:`M12 16h.01`,key:`1drbdi`}]],Ki=e=>P(Z,F(e,{name:`ShieldAlert`,iconNode:Gi})),qi=Ki})),Yi,Xi,Zi,Qi=t((()=>{X(),Q(),Yi=[[`path`,{d:`M9.937 15.5A2 2 0 0 0 8.5 14.063l-6.135-1.582a.5.5 0 0 1 0-.962L8.5 9.936A2 2 0 0 0 9.937 8.5l1.582-6.135a.5.5 0 0 1 .963 0L14.063 8.5A2 2 0 0 0 15.5 9.937l6.135 1.581a.5.5 0 0 1 0 .964L15.5 14.063a2 2 0 0 0-1.437 1.437l-1.582 6.135a.5.5 0 0 1-.963 0z`,key:`4pj2yx`}],[`path`,{d:`M20 3v4`,key:`1olli1`}],[`path`,{d:`M22 5h-4`,key:`1gvqau`}],[`path`,{d:`M4 17v2`,key:`vumght`}],[`path`,{d:`M5 18H3`,key:`zchphs`}]],Xi=e=>P(Z,F(e,{name:`Sparkles`,iconNode:Yi})),Zi=Xi})),$i,ea,ta,na=t((()=>{X(),Q(),$i=[[`path`,{d:`M3 6h18`,key:`d0wm0j`}],[`path`,{d:`M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6`,key:`4alrt4`}],[`path`,{d:`M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2`,key:`v07s0e`}],[`line`,{x1:`10`,x2:`10`,y1:`11`,y2:`17`,key:`1uufr5`}],[`line`,{x1:`14`,x2:`14`,y1:`11`,y2:`17`,key:`xtxkd`}]],ea=e=>P(Z,F(e,{name:`Trash2`,iconNode:$i})),ta=ea})),ra,ia,aa,oa=t((()=>{X(),Q(),ra=[[`path`,{d:`M18 6 6 18`,key:`1bl5f8`}],[`path`,{d:`m6 6 12 12`,key:`d8bk6v`}]],ia=e=>P(Z,F(e,{name:`X`,iconNode:ra})),aa=ia})),sa=t((()=>{Jn(),Qn(),nr(),or(),ur(),mr(),Sr(),Ar(),Pr(),Rr(),Hr(),Kr(),Xr(),ii(),ci(),fi(),gi(),bi(),wi(),Mi(),Ii(),Bi(),Wi(),Ji(),Qi(),na(),oa()})),ca=t((()=>{vr()})),la=t((()=>{Er()})),ua=t((()=>{ei()})),da=t((()=>{Oi()})),fa=t((()=>{ca(),la(),ua(),da()})),pa=t((()=>{})),ma=t((()=>{})),ha=t((()=>{fa(),pa(),ma()})),ga=t((()=>{})),_a=t((()=>{sa(),ha(),ga()}));X(),Rn(),_a();var va=G(`
`),ya=G(``),ba=G(``),xa=[{href:`/`,label:`Home`,Icon:$r},{href:`/status`,label:`Server Status`,Icon:Ui},{href:`/openapi`,label:`OpenAPI`,Icon:Nr},{href:`/library`,label:`Library`,Icon:tr},{href:`/jobs`,label:`Jobs`,Icon:si}];function Sa(e){return(()=>{var t=va(),n=t.firstChild;n.firstChild;var r=n.nextSibling;return J(n,()=>xa.map(e=>P(Pn,{get href(){return e.href},get end(){return e.href===`/`},class:`flex items-center gap-3 rounded-lg px-1 py-2 text-slate-400 transition-colors hover:bg-slate-800 hover:text-white`,activeClass:`bg-slate-800 text-white!`,get children(){return[(()=>{var t=ya();return J(t,P(e.Icon,{class:`h-5 w-5`})),t})(),(()=>{var t=ba();return J(t,()=>e.label),t})()]}})),null),J(r,()=>e.children),t})()}X(),W();var Ca=G(`
W

WebOCR

Manga OCR · Translation · Typesetting`),wa=G(`v`);function Ta(){let[e,t]=s(null);return _(()=>{fetch(`/health`).then(e=>e.ok?e.json():Promise.reject()).then(t).catch(()=>{})}),(()=>{var t=Ca();return t.firstChild.nextSibling,J(t,P(I,{get when(){return e()},children:e=>(()=>{var t=wa();return t.firstChild,J(t,()=>e().version,null),t})()}),null),t})()}function Ea(e){return P(I,{get when(){return e.ok!==null},get fallback(){return(()=>{var t=ka();return J(t,P(xr,{class:`h-3 w-3 animate-spin`}),null),J(t,()=>e.label,null),t})()},get children(){return P(I,{get when(){return e.ok},get fallback(){return(()=>{var t=Aa();return J(t,P(Tr,{class:`h-3 w-3`}),null),J(t,()=>e.label,null),t})()},get children(){var t=Oa();return J(t,P(_r,{class:`h-3 w-3`}),null),J(t,()=>e.label,null),t}})}})}function Da(){let[e,t]=s(null),[n,r]=s(null),[i,a]=s(!1),[o,c]=s(null);_(u);function u(){r(null),fetch(`/health`).then(e=>{if(!e.ok)throw Error(`HTTP ${e.status}`);return e.json()}).then(e=>{t(e),c(e.preferred_translation_engine)}).catch(e=>r(String(e)))}let d=()=>{if(n())return!1;let t=e();return t?t.status===`ok`:null},f=()=>{if(n())return`error`;let t=e();return t?t.status:`connecting`};async function p(t){if(!(!e()||i())){r(null),a(!0);try{let e=await fetch(`/api/settings/engine`,{method:`PATCH`,headers:{"Content-Type":`application/json`},body:JSON.stringify({engine:t})});if(!e.ok)throw Error(`Failed to save settings (HTTP ${e.status})`);c(t)}catch(e){r(String(e))}finally{a(!1)}}}return(()=>{var t=Ma(),r=t.firstChild,a=r.firstChild.firstChild,s=a.nextSibling.nextSibling;return J(a,P(yi,{class:`h-6 w-6 text-slate-700`})),J(s,P(Ea,{get ok(){return d()},get label(){return f()}})),J(r,P(I,{get when(){return n()},get children(){var e=ja();return J(e,P(qi,{class:`h-4 w-4 shrink-0`}),null),J(e,n,null),e}}),null),J(r,P(I,{get when(){return e()},children:e=>[(()=>{var t=Na(),n=t.firstChild.nextSibling.firstChild,r=n.firstChild.nextSibling,i=n.nextSibling,a=i.firstChild,o=a.firstChild,s=a.nextSibling,c=i.nextSibling.firstChild,l=c.firstChild,u=c.nextSibling;return J(r,()=>e().version),J(a,P(Gr,{class:`h-3.5 w-3.5`}),o),J(s,()=>e().data_dir),J(c,P(Yr,{class:`h-3.5 w-3.5`}),l),J(u,P(Ea,{get ok(){return e().deepl_available},get label(){return e().deepl_available?`configured`:`not configured`}})),t})(),(()=>{var t=Pa(),n=t.firstChild.nextSibling;return J(n,P(ve,{get each(){return Object.entries(e().models)},children:([e,t])=>{let n=Va[e]??{label:e,Icon:qn},r=n.Icon;return(()=>{var e=La(),i=e.firstChild,a=i.nextSibling,o=a.nextSibling;return J(i,P(r,{class:`h-4 w-4`})),J(a,()=>n.label),J(o,()=>t.name),J(e,P(I,{get when(){return t.enabled},get fallback(){return Ra()},get children(){return P(Ea,{get ok(){return t.ready},get label(){return t.ready?`ready`:`loading`}})}}),null),e})()}})),t})(),(()=>{var t=Ia(),n=t.firstChild.nextSibling.firstChild.nextSibling;return J(n,P(ve,{each:[{value:`auto`,label:`Auto`,desc:`DeepL if configured, else local`},{value:`local`,label:`Local`,desc:`Opus-MT (on-device)`},{value:`deepl`,label:`DeepL`,desc:`DeepL API`}],children:t=>{let n=()=>o()===t.value,r=()=>t.value===`deepl`&&!e().deepl_available;return(()=>{var e=Ba();return e.$$click=()=>p(t.value),J(e,()=>t.label,null),J(e,P(I,{get when(){return n()},get children(){return za()}}),null),l(a=>{var o=r()||i(),s=r()?`DeepL API key not configured`:t.desc,c=`rounded-lg border px-4 py-2 text-sm transition-colors ${n()?`border-violet-500 bg-violet-50 font-semibold text-violet-700`:`border-slate-200 bg-white text-slate-600 hover:border-slate-300 hover:bg-slate-50`} ${r()?`cursor-not-allowed opacity-40`:``}`;return o!==a.e&&(e.disabled=a.e=o),s!==a.t&&q(e,`title`,a.t=s),c!==a.a&&Je(e,a.a=c),a},{e:void 0,t:void 0,a:void 0}),e})()}}),null),J(n,P(I,{get when(){return i()},get children(){var e=Fa(),t=e.firstChild;return J(e,P(xr,{class:`h-3 w-3 animate-spin`}),t),e}}),null),t})()]}),null),t})()}var Oa,ka,Aa,ja,Ma,Na,Pa,Fa,Ia,La,Ra,za,Ba,Va,Ha=t((()=>{X(),W(),_a(),Oa=G(``),ka=G(``),Aa=G(``),ja=G(`

`),Ma=G(`

Server Status

ASP.NET Core · Manga-OCR · Opus-MT

`),Na=G(`
Server info
Version
Data dir
DeepL
`),Pa=G(`
Models
    `),Fa=G(` Saving…`),Ia=G(`
    Translation engine

    Default engine used when a request doesn't specify one. Takes effect immediately; no restart needed.

    `),La=G(`
  • `),Ra=G(`disabled`),za=G(`✓`),Ba=G(`