SeemGen/Components/Pages/LogoGenerator.razor

379 lines
13 KiB
Plaintext

@page "/create-logo"
@using BLAIzor.Components.Layout
@attribute [Authorize]
@layout AdminLayout
@using System.Net.Http.Headers
@using System.Text.Json
@using BLAIzor.Services
@using Microsoft.AspNetCore.Components.Authorization
@using SixLabors.ImageSharp
@using SixLabors.ImageSharp.Processing
@inject IJSRuntime JS
@inject IHttpClientFactory HttpClientFactory
@inject WhisperTranscriptionService WhisperService
@inject ReplicateService ReplicateService
@inject AuthenticationStateProvider AuthenticationStateProvider
@inject CustomAuthenticationStateProvider CustomAuthProvider
<div class="card p-3 shadow-sm bg-panel-gradient text-white" style="max-width:100%; width: 700px; height: 70vh; margin: 0 auto; border-radius: 20px;">
<div class="card-header text-center text-white">
<h3 class="mb-3">Let's build your website step by step</h3>
</div>
<div class="card-body" style="overflow: scroll">
<p class="text-white-50 small mb-3">
Step @CurrentStep of @Steps.Count (@(CurrentStep * 100 / Steps.Count)% complete)
</p>
@if (CurrentStep < Steps.Count)
{
<AnimateOnRender CssClass="animate__animated animate__backInUp" @key="CurrentStep">
<div class="my-3 text-center">
<label class="form-label">@Steps[CurrentStep].Question</label>
<p class="text-muted">@Steps[CurrentStep].Description</p>
<InputText class="form-control" @bind-Value="Steps[CurrentStep].Answer" />
</div>
</AnimateOnRender>
<div class="d-flex justify-content-between mt-3">
<button class="btn btn-secondary" @onclick="PreviousStep" disabled="@IsFirstStep">Back</button>
<button class="btn btn-primary" @onclick="NextStep">Next</button>
</div>
}
else
{
<h5 class="mt-4">Site Description Preview</h5>
<div class="p-3 bg-panel-gradient-highlight text-dark mb-3 rounded">
@((MarkupString)generatedDescription)
<button class="btn btn-success" @onclick="ProceedToLogoStep">Use this description</button>
</div>
}
@if (ShowLogoStep)
{
<div class="text-center my-4">
<h5>Would you like to upload a logo or generate one?</h5>
<div class="my-3">
@* <input type="file" @onchange="UploadLogo" class="form-control" /> *@
<InputFile class="btn btn-default" type="file" multiple OnChange=HandleFileUpload accept=".jpg,.png" />
</div>
<div class="my-3">
<button class="btn btn-outline-primary" @onclick="GenerateLogo" disabled="@IsGeneratingLogo">
@(logoGenerationCount == 0 ? "Generate Logo with AI" : "Regenerate Logo")
</button>
@if (logoGenerationCount > 0 && logoGenerationCount < MaxLogoGenerations)
{
<p class="text-muted small mt-2">
You can regenerate the logo @(MaxLogoGenerations - logoGenerationCount) more time(s).
</p>
}
</div>
@if (!string.IsNullOrWhiteSpace(GeneratedLogoUrl))
{
<div class="my-3">
<img src="@GeneratedLogoUrl" class="img-fluid rounded shadow" style="max-height: 512px;" />
</div>
}
</div>
<div class="p-3 bg-panel-gradient-highlight text-dark mb-3 rounded">
<button class="btn btn-success" @onclick="SaveDescription">Save</button>
</div>
}
</div>
<div class="card-footer my-4 d-flex justify-content-between align-items-center text-white small">
@for (int i = 0; i < Steps.Count; i++)
{
<div class="text-center flex-fill">
<div class="mb-1">
<div class="@GetStepCircleClass(i)">
@(i + 1)
</div>
</div>
@* <div style="min-height: 36px;">@Steps[i].Question.Split('?')[0]</div> *@
@* @if (i < Steps.Count - 1)
{ *@
<div class="progress-line mx-auto"></div>
@* } *@
</div>
}
</div>
</div>
@code {
public string UserId { get; set; } = string.Empty;
private string userName = string.Empty;
private AuthenticationState authState;
private int CurrentStep = 0;
private bool IsFirstStep => CurrentStep == 0;
private bool ShowLogoStep = false;
private string? GeneratedLogoUrl;
private string? logoUrl;
private int logoGenerationCount = 0;
private const int MaxLogoGenerations = 10;
private bool IsGeneratingLogo = false;
private List<QuestionStep> Steps = new()
{
new("What is the name of your site?", "The domain name or the brand name"),
new("Is it for a brand, a person, a cause, a blog, a service, a store, a beauty salon or something else?", "The entity that the site will introduce to the users. "),
new("What kind of feel or atmosphere should the site have? (e.g. friendly, professional, mysterious, playful)", "How should your website communicate?"),
new("What color(s) do you associate with your brand or prefer for the visuals?", "The colors you have in mind. This is needed for design suggestions and photo generation"),
};
private string generatedDescription = "";
private string siteName = "";
private string entity = "";
private string persona = "";
private string colors = "";
protected override async Task OnInitializedAsync()
{
_instance = this;
authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
if (authState.User.Identity?.IsAuthenticated == true)
{
UserId = CustomAuthProvider.GetUserId();
userName = CustomAuthProvider.GetUserName();
}
}
private void NextStep()
{
if (CurrentStep < Steps.Count)
CurrentStep++;
if (CurrentStep == Steps.Count)
GenerateSiteDescription();
}
private void PreviousStep()
{
if (CurrentStep > 0)
CurrentStep--;
}
private void GenerateSiteDescription()
{
var name = Steps[0].Answer;
var siteEntity = Steps[1].Answer;
var feel = Steps[2].Answer;
colors = Steps[3].Answer;
siteName = name;
entity = siteEntity;
persona = feel;
generatedDescription = $@"
<p><strong>{name}</strong> is a {entity.ToLower()}.</p>
<p>The logo should offer {feel.ToLower()} experience for its viewers.</p>
<p>The design should reflect {colors.ToLower()} tones for visual consistency.</p>
";
}
private void ProceedToLogoStep()
{
ShowLogoStep = true;
}
private async Task GenerateLogo()
{
if (logoGenerationCount >= MaxLogoGenerations)
return;
IsGeneratingLogo = true;
var logoPrompt = $"Logo for a {entity} named {siteName}, with a {persona} tone, using {colors} colors. DO NOT ADD taglines, or any text other than the brand name: {siteName}.";
GeneratedLogoUrl = await ReplicateService.GenerateLogoAsync(logoPrompt, true);
logoUrl = GeneratedLogoUrl;
logoGenerationCount++;
IsGeneratingLogo = false;
}
private async Task SaveDescription()
{
if (!string.IsNullOrWhiteSpace(GeneratedLogoUrl) && GeneratedLogoUrl.StartsWith("http"))
{
var savedPath = await DownloadAndSaveImage(GeneratedLogoUrl);
if (!string.IsNullOrEmpty(savedPath))
{
logoUrl = savedPath;
}
}
string[] siteInfo = new string[6];
siteInfo[0] = siteName;
siteInfo[1] = generatedDescription;
siteInfo[2] = entity;
siteInfo[3] = persona;
siteInfo[5] = logoUrl;
}
private class QuestionStep
{
public string Question { get; }
public string Description { get; }
public string Answer { get; set; } = "";
public QuestionStep(string question, string description)
{
Question = question;
Description = description;
}
}
private string GetStepCircleClass(int index)
{
if (index < CurrentStep)
return "step-circle completed";
else if (index == CurrentStep)
return "step-circle current";
else
return "step-circle";
}
private async Task HandleFileUpload(InputFileChangeEventArgs e)
{
if (e.FileCount == 0) return;
try
{
var uploadPath = Path.Combine("wwwroot", "uploads", UserId);
foreach (var file in e.GetMultipleFiles())
{
var folder = GetFolderForFile(file.ContentType);
var folderPath = Path.Combine(uploadPath, folder);
// Create target directory
Directory.CreateDirectory(folderPath);
var filePath = Path.Combine(folderPath, file.Name);
await using (var stream = new FileStream(filePath, FileMode.Create))
{
await file.OpenReadStream(50 * 1024 * 1024).CopyToAsync(stream);
}
var relativePath = $"/uploads/{UserId}/{folder}/{file.Name}";
AppendFilePathToContent(file.ContentType, relativePath);
// Generate thumbnail if it's an image
string? thumbnailRelativePath = null;
if (file.ContentType.StartsWith("image/"))
{
var thumbnailFolder = Path.Combine(folderPath, "thumbnails");
Directory.CreateDirectory(thumbnailFolder);
var thumbnailPath = Path.Combine(thumbnailFolder, file.Name);
using var image = await Image.LoadAsync(file.OpenReadStream());
image.Mutate(x => x.Resize(new ResizeOptions
{
Size = new Size(300, 0),
Mode = ResizeMode.Max
}));
await image.SaveAsync(thumbnailPath);
thumbnailRelativePath = $"/uploads/{UserId}/{folder}/thumbnails/{file.Name}";
}
AppendFilePathToContent(file.ContentType, relativePath, thumbnailRelativePath);
}
}
catch (Exception ex)
{
Console.WriteLine($"Error uploading files: {ex.Message}");
}
finally
{
//IsLoading = false;
}
}
private string GetFolderForFile(string contentType)
{
return contentType switch
{
var type when type.StartsWith("image/") => "images",
var type when type.StartsWith("video/") => "videos",
var type when type.StartsWith("audio/") => "audio",
_ => "others"
};
}
private void AppendFilePathToContent(string contentType, string relativePath, string? thumbnailPath = null)
{
if (contentType.StartsWith("image/"))
{
logoUrl = relativePath;
GeneratedLogoUrl = relativePath;
var ThumbnailUrl = thumbnailPath ?? string.Empty;
}
StateHasChanged();
}
private async Task<string?> DownloadAndSaveImage(string imageUrl)
{
try
{
var uploadPath = Path.Combine("wwwroot", "uploads", UserId, "images");
Directory.CreateDirectory(uploadPath);
var fileName = $"logo_{Guid.NewGuid().ToString().Substring(0, 8)}.jpg";
var filePath = Path.Combine(uploadPath, fileName);
var httpClient = HttpClientFactory.CreateClient();
var imageBytes = await httpClient.GetByteArrayAsync(imageUrl);
await File.WriteAllBytesAsync(filePath, imageBytes);
// Generate thumbnail
var thumbnailFolder = Path.Combine(uploadPath, "thumbnails");
Directory.CreateDirectory(thumbnailFolder);
var thumbnailPath = Path.Combine(thumbnailFolder, fileName);
using var image = Image.Load(imageBytes);
image.Mutate(x => x.Resize(new ResizeOptions
{
Size = new Size(300, 0),
Mode = ResizeMode.Max
}));
await image.SaveAsync(thumbnailPath);
return $"/uploads/{UserId}/images/{fileName}";
}
catch (Exception ex)
{
Console.WriteLine($"Error saving logo: {ex.Message}");
return null;
}
}
// STT Hook
private static LogoGenerator? _instance;
}