Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e8fd022373 | ||
|
|
8d043a5038 | ||
|
|
3e7e6323c2 | ||
|
|
5652fee422 | ||
|
|
1c9fd05251 |
@@ -3,9 +3,6 @@ root = true
|
|||||||
# All files
|
# All files
|
||||||
[*]
|
[*]
|
||||||
indent_style = space
|
indent_style = space
|
||||||
|
|
||||||
# Xml files
|
|
||||||
[*.xml]
|
|
||||||
indent_size = 2
|
indent_size = 2
|
||||||
|
|
||||||
# C# files
|
# C# files
|
||||||
@@ -14,7 +11,6 @@ indent_size = 2
|
|||||||
#### Core EditorConfig Options ####
|
#### Core EditorConfig Options ####
|
||||||
|
|
||||||
# Indentation and spacing
|
# Indentation and spacing
|
||||||
indent_size = 2
|
|
||||||
tab_width = 2
|
tab_width = 2
|
||||||
|
|
||||||
# New line preferences
|
# New line preferences
|
||||||
|
|||||||
Vendored
+7
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"cSpell.words": [
|
||||||
|
"onclick",
|
||||||
|
"prerender",
|
||||||
|
"rendermode"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -45,6 +45,21 @@ builder.Services.AddRateLimiter(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// TODO: Make CORS more restrictive
|
||||||
|
// only allow requests from the web
|
||||||
|
// client
|
||||||
|
const string corsPolicyName = "cors";
|
||||||
|
|
||||||
|
builder.Services.AddCors(o =>
|
||||||
|
{
|
||||||
|
o.AddPolicy(corsPolicyName, p =>
|
||||||
|
{
|
||||||
|
p.AllowAnyOrigin()
|
||||||
|
.AllowAnyOrigin()
|
||||||
|
.AllowAnyMethod();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
|
|
||||||
if (app.Environment.IsDevelopment())
|
if (app.Environment.IsDevelopment())
|
||||||
@@ -57,6 +72,8 @@ if (app.Environment.IsProduction())
|
|||||||
app.UseRateLimiter();
|
app.UseRateLimiter();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
app.UseCors(corsPolicyName);
|
||||||
|
|
||||||
app.UseHttpsRedirection();
|
app.UseHttpsRedirection();
|
||||||
|
|
||||||
app.UseStatusCodePages();
|
app.UseStatusCodePages();
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
namespace AltGen.Console.Generate;
|
namespace AltGen.Console.Generate;
|
||||||
|
|
||||||
|
// TODO: Use shared library version of this service
|
||||||
sealed class AltGenService(HttpClient client) : IAltGenService
|
sealed class AltGenService(HttpClient client) : IAltGenService
|
||||||
{
|
{
|
||||||
static readonly JsonSerializerOptions JsonOptions = new()
|
static readonly JsonSerializerOptions JsonOptions = new()
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
using System.Net.Http.Headers;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
namespace AltGen.Shared.Generate;
|
||||||
|
|
||||||
|
public sealed class AltGenService(HttpClient client) : IAltGenService
|
||||||
|
{
|
||||||
|
static readonly JsonSerializerOptions JsonOptions = new()
|
||||||
|
{
|
||||||
|
PropertyNameCaseInsensitive = true
|
||||||
|
};
|
||||||
|
readonly HttpClient _client = client;
|
||||||
|
|
||||||
|
public async Task<string> GenerateAltTextAsync(GenerateAltTextRequest req)
|
||||||
|
{
|
||||||
|
var byteContent = new ByteArrayContent(req.Image);
|
||||||
|
byteContent.Headers.ContentType = new MediaTypeHeaderValue(req.ContentType);
|
||||||
|
|
||||||
|
var request = new HttpRequestMessage(HttpMethod.Post, "/generate")
|
||||||
|
{
|
||||||
|
Content = new MultipartFormDataContent
|
||||||
|
{
|
||||||
|
{ new StringContent(req.Provider), "provider" },
|
||||||
|
{ new StringContent(req.ProviderKey), "providerKey" },
|
||||||
|
{ byteContent, "file", req.FileName }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var response = await _client.SendAsync(request);
|
||||||
|
var content = await response.Content.ReadAsStringAsync();
|
||||||
|
|
||||||
|
if (response.IsSuccessStatusCode is false)
|
||||||
|
{
|
||||||
|
throw new AltTextException("Failed to generate alt text.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var altTextResponse = JsonSerializer.Deserialize<AltTextResponse>(content, JsonOptions) ?? throw new AltTextException("Failed to deserialize response.");
|
||||||
|
|
||||||
|
return altTextResponse.AltText;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
record AltTextResponse(string AltText);
|
||||||
|
|
||||||
|
class AltTextException(string message) : Exception(message)
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
namespace AltGen.Shared.Generate;
|
||||||
|
|
||||||
|
public record GenerateAltTextRequest(
|
||||||
|
string Provider,
|
||||||
|
string ProviderKey,
|
||||||
|
string FileName,
|
||||||
|
byte[] Image,
|
||||||
|
string ContentType
|
||||||
|
);
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
namespace AltGen.Shared.Generate;
|
||||||
|
|
||||||
|
public interface IAltGenService
|
||||||
|
{
|
||||||
|
public Task<string> GenerateAltTextAsync(GenerateAltTextRequest req);
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<NoDefaultLaunchSettingsFile>true</NoDefaultLaunchSettingsFile>
|
||||||
|
<StaticWebAssetProjectMode>Default</StaticWebAssetProjectMode>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.3" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.3" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\AltGen.Shared\AltGen.Shared.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,126 @@
|
|||||||
|
@rendermode @(new InteractiveWebAssemblyRenderMode(prerender: false))
|
||||||
|
@inject IJSRuntime JsRuntime
|
||||||
|
@implements IAsyncDisposable
|
||||||
|
|
||||||
|
@* TODO: Create providers context.
|
||||||
|
- Load providers and present in table
|
||||||
|
- Allow form to add new provider
|
||||||
|
- Handle changing default provider
|
||||||
|
*@
|
||||||
|
|
||||||
|
<header>
|
||||||
|
<button type="button" @onclick="HandleConfigButtonClick" class="config-button">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
|
||||||
|
<path
|
||||||
|
d="M495.9 166.6c3.2 8.7 .5 18.4-6.4 24.6l-43.3 39.4c1.1 8.3 1.7 16.8 1.7 25.4s-.6 17.1-1.7 25.4l43.3 39.4c6.9 6.2 9.6 15.9 6.4 24.6c-4.4 11.9-9.7 23.3-15.8 34.3l-4.7 8.1c-6.6 11-14 21.4-22.1 31.2c-5.9 7.2-15.7 9.6-24.5 6.8l-55.7-17.7c-13.4 10.3-28.2 18.9-44 25.4l-12.5 57.1c-2 9.1-9 16.3-18.2 17.8c-13.8 2.3-28 3.5-42.5 3.5s-28.7-1.2-42.5-3.5c-9.2-1.5-16.2-8.7-18.2-17.8l-12.5-57.1c-15.8-6.5-30.6-15.1-44-25.4L83.1 425.9c-8.8 2.8-18.6 .3-24.5-6.8c-8.1-9.8-15.5-20.2-22.1-31.2l-4.7-8.1c-6.1-11-11.4-22.4-15.8-34.3c-3.2-8.7-.5-18.4 6.4-24.6l43.3-39.4C64.6 273.1 64 264.6 64 256s.6-17.1 1.7-25.4L22.4 191.2c-6.9-6.2-9.6-15.9-6.4-24.6c4.4-11.9 9.7-23.3 15.8-34.3l4.7-8.1c6.6-11 14-21.4 22.1-31.2c5.9-7.2 15.7-9.6 24.5-6.8l55.7 17.7c13.4-10.3 28.2-18.9 44-25.4l12.5-57.1c2-9.1 9-16.3 18.2-17.8C227.3 1.2 241.5 0 256 0s28.7 1.2 42.5 3.5c9.2 1.5 16.2 8.7 18.2 17.8l12.5 57.1c15.8 6.5 30.6 15.1 44 25.4l55.7-17.7c8.8-2.8 18.6-.3 24.5 6.8c8.1 9.8 15.5 20.2 22.1 31.2l4.7 8.1c6.1 11 11.4 22.4 15.8 34.3zM256 336a80 80 0 1 0 0-160 80 80 0 1 0 0 160z" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<dialog @ref="dialogElement">
|
||||||
|
<div class="providers-container">
|
||||||
|
<button type="button" @onclick="HandleCloseButtonClick" class="close-button">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
|
||||||
|
<path
|
||||||
|
d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM175 175c9.4-9.4 24.6-9.4 33.9 0l47 47 47-47c9.4-9.4 24.6-9.4 33.9 0s9.4 24.6 0 33.9l-47 47 47 47c9.4 9.4 9.4 24.6 0 33.9s-24.6 9.4-33.9 0l-47-47-47 47c-9.4 9.4-24.6 9.4-33.9 0s-9.4-24.6 0-33.9l47-47-47-47c-9.4-9.4-9.4-24.6 0-33.9z" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<div class="providers-table-container">
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Provider</th>
|
||||||
|
<th>Provider Key</th>
|
||||||
|
<th>Default</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@for (int i = 0; i < 5; i++)
|
||||||
|
{
|
||||||
|
<tr>
|
||||||
|
<td>Provider @(i + 1)</td>
|
||||||
|
<td>Key @(i + 1)</td>
|
||||||
|
<td><input type="checkbox" /></td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="form-container">
|
||||||
|
<form>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="provider">Provider</label>
|
||||||
|
<input type="text" name="provider" id="provider">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Provider Key</label>
|
||||||
|
<input type="password" name="providerKey" id="providerKey">
|
||||||
|
</div>
|
||||||
|
<div class="form-group row">
|
||||||
|
<input type="checkbox" name="defaultProvider" id="defaultProvider">
|
||||||
|
<label for="">Default</label>
|
||||||
|
</div>
|
||||||
|
<button type="submit">Add</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</dialog>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
private IJSObjectReference? _jsModule;
|
||||||
|
private ElementReference dialogElement;
|
||||||
|
|
||||||
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||||
|
{
|
||||||
|
if (firstRender)
|
||||||
|
{
|
||||||
|
_jsModule = await JsRuntime.InvokeAsync<IJSObjectReference>(JsFunctions.Import, ComponentAssets.JsModule);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task HandleConfigButtonClick()
|
||||||
|
{
|
||||||
|
if (_jsModule is null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await _jsModule.InvokeVoidAsync(JsFunctions.ShowDialog, dialogElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task HandleCloseButtonClick()
|
||||||
|
{
|
||||||
|
if (_jsModule is null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await _jsModule.InvokeVoidAsync(JsFunctions.CloseDialog, dialogElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
async ValueTask IAsyncDisposable.DisposeAsync()
|
||||||
|
{
|
||||||
|
if (_jsModule is not null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _jsModule.DisposeAsync();
|
||||||
|
}
|
||||||
|
catch (JSDisconnectedException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class ComponentAssets
|
||||||
|
{
|
||||||
|
public const string JsModule = "./Components/Layout/Header.razor.js";
|
||||||
|
}
|
||||||
|
|
||||||
|
static class JsFunctions
|
||||||
|
{
|
||||||
|
public const string Import = "import";
|
||||||
|
public const string ShowDialog = "showDialog";
|
||||||
|
public const string CloseDialog = "closeDialog";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
.config-button,
|
||||||
|
.close-button{
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 0.25rem;
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.config-button svg,
|
||||||
|
.close-button svg {
|
||||||
|
height: 1rem;
|
||||||
|
width: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.providers-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1.25rem;
|
||||||
|
padding: 1.25rem;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-button {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
background-color: transparent;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-container form {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group.row {
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group label {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
/**
|
||||||
|
* @summary Shows a dialog element by calling the `showModal` method on it.
|
||||||
|
* @param {HTMLDialogElement} dialog - The dialog element to show
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
export function showDialog(dialog) {
|
||||||
|
dialog.showModal();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary Closes a dialog element by calling the `close` method on it.
|
||||||
|
* @param {HTMLDialogElement} dialog - The dialog element to close
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
export function closeDialog(dialog) {
|
||||||
|
dialog.close();
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
@using AltGen.Shared.Generate
|
||||||
|
@inject ILogger<Home> Logger
|
||||||
|
@inject IAltGenService AltGenService
|
||||||
|
@page "/"
|
||||||
|
@rendermode @(new InteractiveWebAssemblyRenderMode(prerender: false))
|
||||||
|
|
||||||
|
@* TODO: Present configured providers a dropdown.
|
||||||
|
If there is a default provider, use it.
|
||||||
|
*@
|
||||||
|
|
||||||
|
<PageTitle>AltGen.Web</PageTitle>
|
||||||
|
|
||||||
|
<h1>AI Generated Alt Text</h1>
|
||||||
|
|
||||||
|
<form method="post">
|
||||||
|
<div>
|
||||||
|
<label for="provider">Provider</label>
|
||||||
|
<input type="text" id="provider" name="provider" required @bind="_provider" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="providerKey">Provider Key</label>
|
||||||
|
<input type="password" id="providerKey" name="providerKey" required @bind="_providerKey" />
|
||||||
|
</div>
|
||||||
|
<InputFile OnChange="HandleFileChange" />
|
||||||
|
</form>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
private string? _provider;
|
||||||
|
private string? _providerKey;
|
||||||
|
|
||||||
|
private async Task HandleFileChange(InputFileChangeEventArgs e)
|
||||||
|
{
|
||||||
|
var file = e.File;
|
||||||
|
|
||||||
|
if (_provider is null || _providerKey is null || file is null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var fileBytes = new byte[file.Size];
|
||||||
|
using var stream = file.OpenReadStream();
|
||||||
|
await stream.ReadAsync(fileBytes, 0, (int)file.Size);
|
||||||
|
|
||||||
|
var altText = await AltGenService.GenerateAltTextAsync(new(
|
||||||
|
_provider,
|
||||||
|
_providerKey,
|
||||||
|
file.Name,
|
||||||
|
fileBytes,
|
||||||
|
file.ContentType
|
||||||
|
));
|
||||||
|
|
||||||
|
// TODO: Present on the screen to the user
|
||||||
|
Logger.LogInformation(altText);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
|
||||||
|
using AltGen.Shared.Generate;
|
||||||
|
|
||||||
|
var builder = WebAssemblyHostBuilder.CreateDefault(args);
|
||||||
|
|
||||||
|
// TODO: Read base url
|
||||||
|
// from configuration
|
||||||
|
builder.Services.AddHttpClient<IAltGenService, AltGenService>(c => c.BaseAddress = new("https://localhost:7297"));
|
||||||
|
|
||||||
|
await builder.Build().RunAsync();
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
@using System.Net.Http
|
||||||
|
@using System.Net.Http.Json
|
||||||
|
@using Microsoft.AspNetCore.Components.Forms
|
||||||
|
@using Microsoft.AspNetCore.Components.Routing
|
||||||
|
@using Microsoft.AspNetCore.Components.Web
|
||||||
|
@using static Microsoft.AspNetCore.Components.Web.RenderMode
|
||||||
|
@using Microsoft.AspNetCore.Components.Web.Virtualization
|
||||||
|
@using Microsoft.JSInterop
|
||||||
|
@using AltGen.Web.Client
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\AltGen.Web.Client\AltGen.Web.Client.csproj" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="9.0.3" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\AltGen.Shared\AltGen.Shared.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<base href="/" />
|
||||||
|
<link rel="stylesheet" href="@Assets["app.css"]" />
|
||||||
|
<link rel="stylesheet" href="@Assets["AltGen.Web.styles.css"]" />
|
||||||
|
<ImportMap />
|
||||||
|
<HeadOutlet />
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<Routes />
|
||||||
|
<script src="_framework/blazor.web.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
@inherits LayoutComponentBase
|
||||||
|
@using AltGen.Web.Client.Components.Layout
|
||||||
|
|
||||||
|
<Header />
|
||||||
|
|
||||||
|
<main>
|
||||||
|
@Body
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<div id="blazor-error-ui" data-nosnippet>
|
||||||
|
An unhandled error has occurred.
|
||||||
|
<a href="." class="reload">Reload</a>
|
||||||
|
<span class="dismiss">🗙</span>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
#blazor-error-ui {
|
||||||
|
color-scheme: light only;
|
||||||
|
background: lightyellow;
|
||||||
|
bottom: 0;
|
||||||
|
box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: none;
|
||||||
|
left: 0;
|
||||||
|
padding: 0.6rem 1.25rem 0.7rem 1.25rem;
|
||||||
|
position: fixed;
|
||||||
|
width: 100%;
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
#blazor-error-ui .dismiss {
|
||||||
|
cursor: pointer;
|
||||||
|
position: absolute;
|
||||||
|
right: 0.75rem;
|
||||||
|
top: 0.5rem;
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
@page "/Error"
|
||||||
|
@using System.Diagnostics
|
||||||
|
|
||||||
|
<PageTitle>Error</PageTitle>
|
||||||
|
|
||||||
|
<h1 class="text-danger">Error.</h1>
|
||||||
|
<h2 class="text-danger">An error occurred while processing your request.</h2>
|
||||||
|
|
||||||
|
@if (ShowRequestId)
|
||||||
|
{
|
||||||
|
<p>
|
||||||
|
<strong>Request ID:</strong> <code>@RequestId</code>
|
||||||
|
</p>
|
||||||
|
}
|
||||||
|
|
||||||
|
<h3>Development Mode</h3>
|
||||||
|
<p>
|
||||||
|
Swapping to <strong>Development</strong> environment will display more detailed information about the error that
|
||||||
|
occurred.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<strong>The Development environment shouldn't be enabled for deployed applications.</strong>
|
||||||
|
It can result in displaying sensitive information from exceptions to end users.
|
||||||
|
For local debugging, enable the <strong>Development</strong> environment by setting the
|
||||||
|
<strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
|
||||||
|
and restarting the app.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
[CascadingParameter]
|
||||||
|
private HttpContext? HttpContext { get; set; }
|
||||||
|
|
||||||
|
private string? RequestId { get; set; }
|
||||||
|
private bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
|
||||||
|
|
||||||
|
protected override void OnInitialized() =>
|
||||||
|
RequestId = Activity.Current?.Id ?? HttpContext?.TraceIdentifier;
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<Router AppAssembly="typeof(Program).Assembly" AdditionalAssemblies="new[] { typeof(Client._Imports).Assembly }">
|
||||||
|
<Found Context="routeData">
|
||||||
|
<RouteView RouteData="routeData" DefaultLayout="typeof(Layout.MainLayout)" />
|
||||||
|
<FocusOnNavigate RouteData="routeData" Selector="h1" />
|
||||||
|
</Found>
|
||||||
|
<NotFound>
|
||||||
|
<LayoutView Layout="typeof(Layout.MainLayout)">
|
||||||
|
<p>Sorry, there's nothing at this address.</p>
|
||||||
|
</LayoutView>
|
||||||
|
</NotFound>
|
||||||
|
</Router>
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
@using System.Net.Http
|
||||||
|
@using System.Net.Http.Json
|
||||||
|
@using Microsoft.AspNetCore.Components.Forms
|
||||||
|
@using Microsoft.AspNetCore.Components.Routing
|
||||||
|
@using Microsoft.AspNetCore.Components.Web
|
||||||
|
@using static Microsoft.AspNetCore.Components.Web.RenderMode
|
||||||
|
@using Microsoft.AspNetCore.Components.Web.Virtualization
|
||||||
|
@using Microsoft.JSInterop
|
||||||
|
@using AltGen.Web
|
||||||
|
@using AltGen.Web.Client
|
||||||
|
@using AltGen.Web.Components
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
using AltGen.Web.Components;
|
||||||
|
|
||||||
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
|
builder.Services.AddRazorComponents()
|
||||||
|
.AddInteractiveWebAssemblyComponents();
|
||||||
|
|
||||||
|
var app = builder.Build();
|
||||||
|
|
||||||
|
// Configure the HTTP request pipeline.
|
||||||
|
if (app.Environment.IsDevelopment())
|
||||||
|
{
|
||||||
|
app.UseWebAssemblyDebugging();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
app.UseExceptionHandler("/Error", createScopeForErrors: true);
|
||||||
|
app.UseHsts();
|
||||||
|
}
|
||||||
|
|
||||||
|
app.UseHttpsRedirection();
|
||||||
|
|
||||||
|
app.UseAntiforgery();
|
||||||
|
|
||||||
|
app.MapStaticAssets();
|
||||||
|
app.MapRazorComponents<App>()
|
||||||
|
.AddInteractiveWebAssemblyRenderMode()
|
||||||
|
.AddAdditionalAssemblies(typeof(AltGen.Web.Client._Imports).Assembly);
|
||||||
|
|
||||||
|
app.Run();
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://json.schemastore.org/launchsettings.json",
|
||||||
|
"profiles": {
|
||||||
|
"https": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"dotnetRunMessages": true,
|
||||||
|
"launchBrowser": true,
|
||||||
|
"inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
|
||||||
|
"applicationUrl": "https://localhost:7198;http://localhost:5183",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"http": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"dotnetRunMessages": true,
|
||||||
|
"launchBrowser": true,
|
||||||
|
"inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
|
||||||
|
"applicationUrl": "http://localhost:5183",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
h1:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.valid.modified:not([type=checkbox]) {
|
||||||
|
outline: 1px solid #26b050;
|
||||||
|
}
|
||||||
|
|
||||||
|
.invalid {
|
||||||
|
outline: 1px solid #e50000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.validation-message {
|
||||||
|
color: #e50000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blazor-error-boundary {
|
||||||
|
background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
|
||||||
|
padding: 1rem 1rem 1rem 3.7rem;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blazor-error-boundary::after {
|
||||||
|
content: "An error has occurred."
|
||||||
|
}
|
||||||
|
|
||||||
|
.darker-border-checkbox.form-check-input {
|
||||||
|
border-color: #929292;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-floating>.form-control-plaintext::placeholder,
|
||||||
|
.form-floating>.form-control::placeholder {
|
||||||
|
color: var(--bs-secondary-color);
|
||||||
|
text-align: end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-floating>.form-control-plaintext:focus::placeholder,
|
||||||
|
.form-floating>.form-control:focus::placeholder {
|
||||||
|
text-align: start;
|
||||||
|
}
|
||||||
|
|
||||||
|
dialog {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
@@ -11,6 +11,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AltGen.Console", "AltGen.Co
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AltGen.Console.Tests", "AltGen.Console.Tests\AltGen.Console.Tests.csproj", "{F7DC6A70-907B-448A-A69D-4E2E80FA3FD3}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AltGen.Console.Tests", "AltGen.Console.Tests\AltGen.Console.Tests.csproj", "{F7DC6A70-907B-448A-A69D-4E2E80FA3FD3}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AltGen.Web", "AltGen.Web\AltGen.Web.csproj", "{CD09C06D-AB87-4C4E-8AD7-2694ED9BF5C6}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AltGen.Web.Client", "AltGen.Web.Client\AltGen.Web.Client.csproj", "{DCE07E79-23A4-490E-A796-EA140D2040C1}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AltGen.Shared", "AltGen.Shared\AltGen.Shared.csproj", "{68D4BC5D-A6FE-41CF-B744-1DB83CCFBC89}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@@ -36,5 +42,17 @@ Global
|
|||||||
{F7DC6A70-907B-448A-A69D-4E2E80FA3FD3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{F7DC6A70-907B-448A-A69D-4E2E80FA3FD3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{F7DC6A70-907B-448A-A69D-4E2E80FA3FD3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{F7DC6A70-907B-448A-A69D-4E2E80FA3FD3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{F7DC6A70-907B-448A-A69D-4E2E80FA3FD3}.Release|Any CPU.Build.0 = Release|Any CPU
|
{F7DC6A70-907B-448A-A69D-4E2E80FA3FD3}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{CD09C06D-AB87-4C4E-8AD7-2694ED9BF5C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{CD09C06D-AB87-4C4E-8AD7-2694ED9BF5C6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{CD09C06D-AB87-4C4E-8AD7-2694ED9BF5C6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{CD09C06D-AB87-4C4E-8AD7-2694ED9BF5C6}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{DCE07E79-23A4-490E-A796-EA140D2040C1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{DCE07E79-23A4-490E-A796-EA140D2040C1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{DCE07E79-23A4-490E-A796-EA140D2040C1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{DCE07E79-23A4-490E-A796-EA140D2040C1}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{68D4BC5D-A6FE-41CF-B744-1DB83CCFBC89}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{68D4BC5D-A6FE-41CF-B744-1DB83CCFBC89}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{68D4BC5D-A6FE-41CF-B744-1DB83CCFBC89}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{68D4BC5D-A6FE-41CF-B744-1DB83CCFBC89}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
|||||||
Reference in New Issue
Block a user