@using System.Net.Http.Headers @using System.Text.Json @using BLAIzor.Interfaces @using BLAIzor.Services @using SixLabors.ImageSharp @using SixLabors.ImageSharp.Processing @inject IJSRuntime JS @inject IHttpClientFactory HttpClientFactory @inject WhisperTranscriptionService WhisperService @inject ReplicateService ReplicateService

Let's build your website step by step

Step @CurrentStep of @Steps.Count (@(CurrentStep * 100 / Steps.Count)% complete)

@if (CurrentStep < Steps.Count) {

@Steps[CurrentStep].Description

@if (CurrentStep == 5) { } else if (CurrentStep == 7) {
} else if (CurrentStep == 8) // Last step: Facebook { } else { }
} else {
Site Description Preview
@((MarkupString)generatedDescriptionToShow)
} @if (ShowLogoStep) {
Would you like to upload a logo or generate one?
@* *@
@if (logoGenerationCount > 0 && logoGenerationCount < MaxLogoGenerations) {

You can regenerate the logo @(MaxLogoGenerations - logoGenerationCount) more time(s).

}
@if (!string.IsNullOrWhiteSpace(GeneratedLogoUrl)) {
}
}
@code { [Parameter] public EventCallback OnDescriptionFinalized { get; set; } [Parameter] public string UserId { get; set; } 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 = 5; private bool IsGeneratingLogo = false; private List 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 is the main purpose of the site?", "What is your goal with the website. Promote the brand, or acquire new customers? This is needed for SEO, and content generation."), new("What kind of feel or atmosphere should the site have? (e.g. friendly, professional, mysterious, playful)", "How should your website communicate?"), new("Where is it located (country or city)?", "This is for content generation and SEO purposes."), new("What is your preferred language?", "The default language of your website. As it is AI driven, AI can answer in almost any language to questions"), 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"), new("Who is your target audience?", "This is needed to generate have a better overall understanding during content generation."), new("Do you already have a Facebook page for your business?", "Provide the URL of your Facebook page if you have one. We'll use it to generate content more accurately.") }; private string targetGender = ""; private string targetAge = ""; private string targetLocation = ""; private string targetInterests = ""; private string generatedDescription = ""; private string generatedDescriptionToShow = ""; private string siteName = ""; private string entity = ""; private string persona = ""; private string defaultLanguage = ""; private string facebookPageUrl = ""; // 3. Trigger Brightdata scraping after generating site description private async Task GenerateSiteDescription() { var name = Steps[0].Answer; var siteEntity = Steps[1].Answer; var purpose = Steps[2].Answer; var feel = Steps[3].Answer; var location = Steps[4].Answer; var language = Steps[5].Answer; var color = Steps[6].Answer; var facebook = Steps[8].Answer; var audience = $"people who are {(string.IsNullOrEmpty(targetGender) ? "all genders" : targetGender.ToLower())} aged {(string.IsNullOrEmpty(targetAge) ? "any age" : targetAge)}, located in {targetLocation}, interested in {targetInterests.ToLower()}"; siteName = name; entity = siteEntity; persona = feel; defaultLanguage = language; generatedDescription = $@" {name} is a {entity.ToLower()} based in {location}, created to {purpose.ToLower()}. The site aims to offer a {feel.ToLower()} experience for its visitors, primarily targeting {audience}. The preferred language is {language}, and the design should reflect {color.ToLower()} tones for visual consistency. "; generatedDescriptionToShow = $@"

{name} is a {entity.ToLower()} based in {location}, created to {purpose.ToLower()}.

The site aims to offer a {feel.ToLower()} experience for its visitors, primarily targeting {audience}.

The preferred language is {language}, and the design should reflect {color.ToLower()} tones for visual consistency.

"; // --- New: Scrape Facebook posts if provided --- } protected override void OnInitialized() { _instance = this; } 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 purpose = Steps[2].Answer; // var feel = Steps[3].Answer; // var location = Steps[4].Answer; // var language = Steps[5].Answer; // var color = Steps[6].Answer; // var audience = $"people who are {(string.IsNullOrEmpty(targetGender) ? "all genders" : targetGender.ToLower())} aged {(string.IsNullOrEmpty(targetAge) ? "any age" : targetAge)}, located in {targetLocation}, interested in {targetInterests.ToLower()}"; // siteName = name; // entity = siteEntity; // persona = feel; // defaultLanguage = language; // generatedDescription = $@" // {name} is a {entity.ToLower()} based in {location}, created to {purpose.ToLower()}. // The site aims to offer a {feel.ToLower()} experience for its visitors, primarily targeting {audience}. // The preferred language is {language}, and the design should reflect {color.ToLower()} tones for visual consistency. // "; // generatedDescriptionToShow = $@" //

{name} is a {entity.ToLower()} based in {location}, created to {purpose.ToLower()}.

//

The site aims to offer a {feel.ToLower()} experience for its visitors, primarily targeting {audience}.

//

The preferred language is {language}, and the design should reflect {color.ToLower()} tones for visual consistency.

// "; // } private void ProceedToLogoStep() { ShowLogoStep = true; } // private async Task GenerateLogo() // { // var logoPrompt = $"Logo for a {entity} named {siteName}, with a {persona} tone, using {Steps[6].Answer} colors."; // GeneratedLogoUrl = await ReplicateService.GenerateImageAsync(logoPrompt); // logoUrl = GeneratedLogoUrl; // } private async Task GenerateLogo() { if (logoGenerationCount >= MaxLogoGenerations) return; IsGeneratingLogo = true; var logoPrompt = $"Logo for a {entity} named {siteName}, with a {persona} tone, using {Steps[6].Answer} 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[7]; siteInfo[0] = siteName; siteInfo[1] = generatedDescription; siteInfo[2] = entity; siteInfo[3] = persona; siteInfo[4] = defaultLanguage; siteInfo[5] = logoUrl; siteInfo[6] = facebookPageUrl; await OnDescriptionFinalized.InvokeAsync(siteInfo); } private async Task StartRecording() => await JS.InvokeVoidAsync("startRecording"); private async Task StopRecording() => await JS.InvokeVoidAsync("stopRecording"); 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 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 CreateSiteWizard? _instance; [JSInvokable] public static async Task SendAudioToServer(List audioData) { if (_instance is null) return; var result = await _instance.WhisperService.TranscribeAsync(audioData.ToArray()); if (!string.IsNullOrWhiteSpace(result)) { _instance.Steps[_instance.CurrentStep].Answer = result; _instance.StateHasChanged(); } } }