Muuuuuch faster, local vector search, local embedding service and api

This commit is contained in:
Adam 2025-06-17 20:22:49 +02:00
parent 4effccf3a7
commit 492fa9209b
42 changed files with 78604 additions and 1262 deletions

View File

@ -1,6 +1,6 @@
@page "/"
@page "/menu/{topic?}"
@inherits SharedDisplayLogic
@inherits MainPageBase
@using BLAIzor.Models
@using BLAIzor.Services
@using BLAIzor.Components.Partials
@ -10,21 +10,15 @@
@using System.Net
@using System.Text.Json
@using Sidio.Sitemap.Blazor
@* @inject AIService ChatGptService *@
@rendermode InteractiveServer
@* @inject IJSRuntime jsRuntime; *@
@* @inject IConfiguration configuration *@
@inject ContentService _contentService
@* @inject ContentEditorService _contentEditorService *@
@* @inject ScopedContentService _scopedContentService *@
@* @inject IEmailSender _emailService *@
@inject NavigationManager _navigationManager
@inject IHttpContextAccessor HttpContextAccessor
@inject DesignTemplateService DesignTemplateService
@inject CssTemplateService CssTemplateService
@inject CssInjectorService CssService
@* @inject HttpClient Http *@
@attribute [Sitemap]
<ErrorBoundary>
<ChildContent>
<div class="page" style="z-index: 1">
@ -264,7 +258,7 @@
// Load the CSS template for the selected brand from the database
var designTemplate = await DesignTemplateService.GetByIdAsync((int)SiteInfo.TemplateId!);
var cssTemplate = await CssTemplateService.GetByDesignTemplateIdAsync((int)SiteInfo.TemplateId);
CollectionName = designTemplate.QDrandCollectionName;
TemplateCollectionName = designTemplate.QDrandCollectionName;
if (cssTemplate != null)
{
@ -284,11 +278,11 @@
if(!string.IsNullOrWhiteSpace(topic))
{
UserInput = topic;
await ChatGptService.ProcessContentRequest(SessionId, UserInput, SiteId, (int)SiteInfo.TemplateId!, CollectionName, Menu, true);
await ChatGptService.ProcessContentRequest(SessionId, UserInput, SiteId, (int)SiteInfo.TemplateId!, TemplateCollectionName, Menu, true);
}
else
{
await ChatGptService.GetChatGptWelcomeMessage(SessionId, SiteId, Menu);
await ChatGptService.GetChatGptWelcomeMessage(SessionId, SiteId, TemplateCollectionName, Menu);
}
// HtmlContent = await ChatGptService.GetChatGptWelcomeMessage();
// UserInput = "Sumerize for me, what is this website about, and what can I do on this website?";
@ -413,7 +407,7 @@
var menu = await GetMenuList(SiteId);
HtmlContent.Clear();
string input = "Please tell me more about: " + UserInput;
await ChatGptService.ProcessUserIntent(SessionId, input, SiteId, (int)SiteInfo.TemplateId!, CollectionName, menu);
await ChatGptService.ProcessUserIntent(SessionId, input, SiteId, (int)SiteInfo.TemplateId!, TemplateCollectionName, menu);
UserInput = string.Empty;
}
}

View File

@ -12,7 +12,7 @@ using UglyToad.PdfPig.DocumentLayoutAnalysis;
namespace BLAIzor.Components.Pages
{
public class SharedDisplayLogic : ComponentBase
public class MainPageBase : ComponentBase
{
public string SelectedBrandName = "default";
[Inject] protected ScopedContentService _scopedContentService { get; set; }
@ -22,10 +22,10 @@ namespace BLAIzor.Components.Pages
[Inject] protected IJSRuntime jsRuntime { get; set; }
[Inject] protected AIService ChatGptService { get; set; }
public static readonly Dictionary<string, SharedDisplayLogic> _instances = new();
public static readonly Dictionary<string, MainPageBase> _instances = new();
public string SessionId;
public static SharedDisplayLogic myHome;
public static MainPageBase myHome;
public int SiteId;
public SiteInfo SiteInfo;
@ -33,7 +33,7 @@ namespace BLAIzor.Components.Pages
public string TextContent = "";
public string StatusContent = "";
public string UserInput = string.Empty;
public string CollectionName = "html_snippets";
public string TemplateCollectionName = "html_snippets";
public bool VoiceEnabled;
public bool TTSEnabled;
@ -123,9 +123,9 @@ namespace BLAIzor.Components.Pages
var requestJson = JsonSerializer.Serialize(requestContent);
string voiceId;
if (SiteInfo.voiceId != null)
if (SiteInfo.VoiceId != null)
{
voiceId = SiteInfo.voiceId;
voiceId = SiteInfo.VoiceId;
}
else
{
@ -199,7 +199,7 @@ namespace BLAIzor.Components.Pages
{
HtmlContent.Clear();
var menu = await GetMenuList(SiteId);
await ChatGptService.ProcessUserIntent(SessionId, UserInput, SiteId, (int)SiteInfo.TemplateId!, CollectionName, menu);
await ChatGptService.ProcessUserIntent(SessionId, UserInput, SiteId, (int)SiteInfo.TemplateId!, TemplateCollectionName, menu);
UserInput = string.Empty;
}
}
@ -214,11 +214,11 @@ namespace BLAIzor.Components.Pages
var menuItem = (await GetMenuItems(SiteId)).Where(m => m.Name == input).FirstOrDefault();
if (menuItem == null)
{
await ChatGptService.ProcessContentRequest(SessionId, input, SiteId, (int)SiteInfo.TemplateId!, CollectionName, menu, forceUnmodified);
await ChatGptService.ProcessContentRequest(SessionId, input, SiteId, (int)SiteInfo.TemplateId!, TemplateCollectionName, menu, forceUnmodified);
}
else
{
await ChatGptService.ProcessContentRequest(SessionId, menuItem, SiteId, (int)SiteInfo.TemplateId!, CollectionName, menu, forceUnmodified);
await ChatGptService.ProcessContentRequest(SessionId, menuItem, SiteId, (int)SiteInfo.TemplateId!, TemplateCollectionName, menu, forceUnmodified);
}
UserInput = string.Empty;
}
@ -302,7 +302,7 @@ namespace BLAIzor.Components.Pages
Console.WriteLine("Button clicked!");
var menu = await GetMenuList(SiteId);
HtmlContent.Clear();
await ChatGptService.ProcessUserIntent(SessionId, UserInput, SiteId, (int)SiteInfo.TemplateId!, CollectionName, menu);
await ChatGptService.ProcessUserIntent(SessionId, UserInput, SiteId, (int)SiteInfo.TemplateId!, TemplateCollectionName, menu);
UserInput = string.Empty;
}

View File

@ -1,6 +1,6 @@
@page "/preview/{siteid:int}"
@page "/preview/{siteid:int}/{topic?}"
@inherits SharedDisplayLogic
@inherits MainPageBase
@using BLAIzor.Models
@using BLAIzor.Services
@using Google.Cloud.Speech.V1
@ -10,21 +10,14 @@
@using System.Text.Json
@using System.Net
@* @inject AIService ChatGptService *@
@rendermode InteractiveServer
@* @inject IJSRuntime jsRuntime; *@
@* @inject IConfiguration configuration *@
@inject ContentService _contentService
@* @inject ContentEditorService _contentEditorService *@
@* @inject ScopedContentService _scopedContentService *@
@inject IEmailSender _emailService
@inject NavigationManager _navigationManager
@inject IHttpContextAccessor HttpContextAccessor
@inject DesignTemplateService DesignTemplateService
@inject CssTemplateService CssTemplateService
@inject CssInjectorService CssService
@* @inject HttpClient Http *@
<div class="page" style="z-index: 1">
<NavMenu MenuString="@Menu" OnMenuClicked=@MenuClick></NavMenu>
@ -99,9 +92,7 @@
@{
if (!string.IsNullOrEmpty(HtmlContent.ToString()))
{
// <div class="pt-5 @FirstColumnClass">
// @((MarkupString)HtmlContent.ToString())
// </div>
if (isEmailFormVisible)
{
<div class="conteiner-fluid">
@ -330,7 +321,7 @@
// Load the CSS template for the selected brand from the database
var designTemplate = await DesignTemplateService.GetByIdAsync((int)SiteInfo.TemplateId!);
var cssTemplate = await CssTemplateService.GetByDesignTemplateIdAsync((int)SiteInfo.TemplateId);
CollectionName = designTemplate.QDrandCollectionName;
TemplateCollectionName = designTemplate.QDrandCollectionName;
if (cssTemplate != null)
{
@ -354,11 +345,11 @@
if (!string.IsNullOrWhiteSpace(topic))
{
UserInput = topic;
await ChatGptService.ProcessContentRequest(SessionId, UserInput, SiteId, (int)SiteInfo.TemplateId!, CollectionName, Menu, true);
await ChatGptService.ProcessContentRequest(SessionId, UserInput, SiteId, (int)SiteInfo.TemplateId!, TemplateCollectionName, Menu, true);
}
else
{
await ChatGptService.GetChatGptWelcomeMessage(SessionId, SiteId, Menu);
await ChatGptService.GetChatGptWelcomeMessage(SessionId, SiteId, TemplateCollectionName, Menu);
}
}
@ -429,7 +420,7 @@
{
HtmlContent.Clear();
var menu = await GetMenuList(SiteId);
await ChatGptService.ProcessUserIntent(SessionId, UserInput, SiteId, (int)SiteInfo.TemplateId!, CollectionName, menu);
await ChatGptService.ProcessUserIntent(SessionId, UserInput, SiteId, (int)SiteInfo.TemplateId!, TemplateCollectionName, menu);
UserInput = string.Empty;
}
}

View File

@ -60,7 +60,7 @@ else if (ExtractedMenuItems.Any())
<MenuItemContentEditor Subject=@subject MenuItem="item" WordFile=@document SessionId="SessionId" OnContentUpdated="UpdateMenuItem" />
}
<div class="card-footer">
<button class="btn btn-default btn-sm mt-2" @onclick="() => AddMenuItem()">Add a new menu item after this one</button>
<button class="btn btn-danger btn-sm mt-2" @onclick="() => RemoveMenuItem(item)">Remove</button>
</div>
}
@ -101,7 +101,7 @@ else if (ExtractedMenuItems.Any())
</div>
<button class="btn btn-default btn-sm mt-2" @onclick="() => AddMenuItem()">Add a new menu item after this one</button>
<button class="btn btn-success mt-3" @onclick="() => SaveMenuItems(true)">Save All</button>
}
else if (!string.IsNullOrEmpty(ErrorMessage))
@ -163,6 +163,7 @@ else if (!string.IsNullOrEmpty(ErrorMessage))
if (selectedPoint != null)
{
model.Content = selectedPoint.result.payload.content;
model.ContentDescription = selectedPoint.result.payload.description;
Console.Write($"Found point: {selectedPoint.result.payload.content}");
}
@ -246,6 +247,7 @@ else if (!string.IsNullOrEmpty(ErrorMessage))
if (item != null)
{
//item.MenuItem.
item.MenuItem.Name = updatedItem.MenuItem.Name;
item.Content = updatedItem.Content;
}
}
@ -287,10 +289,12 @@ else if (!string.IsNullOrEmpty(ErrorMessage))
foreach (var menuItemToReorder in menuItems)
{
menuItemToReorder.SortOrder = menuItems.IndexOf(menuItemToReorder);
Console.WriteLine($"{menuItemToReorder.Name}, {menuItemToReorder.SortOrder}");
}
foreach (var extractedMenuItemToReorder in ExtractedMenuItems)
{
extractedMenuItemToReorder.MenuItem.SortOrder = ExtractedMenuItems.IndexOf(extractedMenuItemToReorder);
Console.WriteLine($"{extractedMenuItemToReorder.MenuItem.Name}, {extractedMenuItemToReorder.MenuItem.SortOrder}");
}
JSRuntime.InvokeVoidAsync("eval", $"document.querySelector('.my-class').classList.remove('my-class')");
}));

View File

@ -62,7 +62,7 @@ else if (extractedMenuItems.Any())
<MenuItemContentEditor Subject=@subject MenuItem="item" OnContentUpdated="UpdateMenuItem" SessionId="SessionId" />
<div class="card-footer">
<button class="btn btn-danger btn-sm mt-2" @onclick="() => RemoveMenuItem(item)">Remove</button>
<button class="btn btn-default btn-sm mt-2" @onclick="() => AddMenuItem()">Add a new menu item after this one</button>
</div>
}
}
@ -77,7 +77,7 @@ else if (extractedMenuItems.Any())
</div>
</div>
<button class="btn btn-default btn-sm mt-2" @onclick="() => AddMenuItem()">Add a new menu item after this one</button>
<button class="btn btn-success mt-3" @onclick="() => SaveMenuItems(true)">Save All</button>
}
@ -133,6 +133,7 @@ else if (!string.IsNullOrEmpty(errorMessage))
if (selectedPoint != null)
{
model.Content = selectedPoint.result.payload.content;
model.ContentDescription = selectedPoint.result.payload.description;
Console.Write($"Found point: {selectedPoint.result.payload.content}");
}
@ -182,6 +183,7 @@ else if (!string.IsNullOrEmpty(errorMessage))
var newItem = new MenuItemModel("New menu item", "");
//TODO Fix
extractedMenuItems.Add(newItem);
isLoading = false;
}

File diff suppressed because one or more lines are too long

View File

@ -17,14 +17,20 @@
}
<button class="btn btn-primary mb-2" @onclick="GenerateContent" disabled="@IsLoading">Generate by AI</button>
@* <textarea class="form-control border-0 text-white" @bind="GeneratedContent" rows="5"></textarea> *@
<div class="row">
<RadzenTextArea @bind-Value=@MenuItem.ContentDescription></RadzenTextArea>
</div>
<div class="row">
<RadzenHtmlEditor @bind-Value=@GeneratedContent style="height: 450px; color:#000; background-color: rgba(255,255,255,0.4)" Input=@OnInput Change=@OnChange Paste=@OnPaste UploadComplete=@OnUploadComplete Execute=@OnExecute UploadUrl="upload/image">
<RadzenHtmlEditorUndo />
<RadzenHtmlEditorRedo />
<RadzenHtmlEditorSource />
</RadzenHtmlEditor>
</div>
<InputFile class="btn btn-default" type="file" multiple OnChange=HandleFileUpload accept=".mp3,.mp4,.jpg,.png" />
<EventConsole @ref=@console />
@* <EventConsole @ref=@console /> *@
@if (IsLoading)
{
<p>Loading content...</p>
@ -40,15 +46,17 @@
private bool IsLoading = false;
private string GeneratedContent = string.Empty;
// private string Description = string.Empty;
private string? userId;
private string? userName;
private AuthenticationState? authState;
EventConsole console;
//EventConsole console;
protected override async Task OnParametersSetAsync()
{
GeneratedContent = MenuItem.Content;
// Description = MenuItem.ContentDescription;
authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
if (authState.User.Identity?.IsAuthenticated == true)
{
@ -103,33 +111,33 @@
async Task OnPaste(HtmlEditorPasteEventArgs args)
{
console.Log($"Paste: {args.Html}");
// console.Log($"Paste: {args.Html}");
MenuItem.Content = args.Html;
await OnContentUpdated.InvokeAsync(MenuItem);
}
async Task OnChange(string html)
{
console.Log($"Change: {html}");
// console.Log($"Change: {html}");
MenuItem.Content = html;
await OnContentUpdated.InvokeAsync(MenuItem);
}
async Task OnInput(string html)
{
console.Log($"Input: {html}");
// console.Log($"Input: {html}");
MenuItem.Content = html;
await OnContentUpdated.InvokeAsync(MenuItem);
}
void OnExecute(HtmlEditorExecuteEventArgs args)
{
console.Log($"Execute: {args.CommandName}");
//console.Log($"Execute: {args.CommandName}");
}
void OnUploadComplete(UploadCompleteEventArgs args)
{
console.Log($"Upload complete: {args.RawResponse}");
//console.Log($"Upload complete: {args.RawResponse}");
}
private async Task HandleFileUpload(InputFileChangeEventArgs e)
@ -167,7 +175,7 @@
}
catch (Exception ex)
{
console.Log($"Error uploading files: {ex.Message}");
// console.Log($"Error uploading files: {ex.Message}");
}
finally
{

View File

@ -0,0 +1,645 @@
// <auto-generated />
using System;
using BLAIzor.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace BLAIzor.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20250615203806_EmbeddingServiceAndDefaultLanguage")]
partial class EmbeddingServiceAndDefaultLanguage
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "9.0.3")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
modelBuilder.Entity("BLAIzor.Models.CssTemplate", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("CssContent")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<int>("DesignTemplateId")
.HasColumnType("int");
b.Property<DateTime>("LastUpdated")
.HasColumnType("datetime2");
b.HasKey("Id");
b.HasIndex("DesignTemplateId")
.IsUnique();
b.ToTable("CssTemplates");
});
modelBuilder.Entity("BLAIzor.Models.DesignTemplate", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime2");
b.Property<string>("Description")
.HasColumnType("nvarchar(max)");
b.Property<bool>("IsDeprecated")
.HasColumnType("bit");
b.Property<bool>("IsPrivate")
.HasColumnType("bit");
b.Property<bool>("IsPublished")
.HasColumnType("bit");
b.Property<string>("QDrandCollectionName")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("Status")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("Tags")
.HasColumnType("nvarchar(max)");
b.Property<string>("TemplateName")
.HasColumnType("nvarchar(max)");
b.Property<string>("TemplatePhotoUrl")
.HasColumnType("nvarchar(max)");
b.Property<DateTime?>("UpdatedAt")
.HasColumnType("datetime2");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("nvarchar(450)");
b.Property<int>("Version")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("DesignTemplates");
b.HasData(
new
{
Id = 1,
CreatedAt = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified),
Description = "The default template",
IsDeprecated = false,
IsPrivate = false,
IsPublished = false,
QDrandCollectionName = "html_snippets",
Status = "Draft",
Tags = "system",
TemplateName = "Default Site",
TemplatePhotoUrl = "/images/default-logo.png",
UserId = "0988758e-e16c-4c2c-8c1e-efa3ac5f0274",
Version = 1
});
});
modelBuilder.Entity("BLAIzor.Models.FormDefinition", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime2");
b.Property<string>("Description")
.HasColumnType("nvarchar(max)");
b.Property<string>("JsonDefinition")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<int>("SiteInfoId")
.HasColumnType("int");
b.Property<string>("Slug")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<string>("Title")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<int>("Version")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("SiteInfoId", "Slug")
.IsUnique();
b.ToTable("FormDefinitions");
});
modelBuilder.Entity("BLAIzor.Models.MenuItem", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("Name")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<int>("PointId")
.HasColumnType("int");
b.Property<Guid?>("QdrantPointId")
.HasColumnType("uniqueidentifier");
b.Property<bool>("ShowInMainMenu")
.HasColumnType("bit");
b.Property<int>("SiteInfoId")
.HasColumnType("int");
b.Property<int>("SortOrder")
.HasColumnType("int");
b.Property<string>("StoredHtml")
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.HasIndex("SiteInfoId");
b.ToTable("MenuItems");
});
modelBuilder.Entity("BLAIzor.Models.SiteInfo", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("BackgroundVideo")
.HasColumnType("nvarchar(max)");
b.Property<string>("BrandLogoUrl")
.HasColumnType("nvarchar(max)");
b.Property<string>("DefaultColor")
.HasColumnType("nvarchar(max)");
b.Property<string>("DefaultLanguage")
.HasColumnType("nvarchar(max)");
b.Property<string>("DefaultUrl")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("DomainUrl")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("EmbeddingService")
.HasColumnType("nvarchar(max)");
b.Property<string>("Entity")
.HasColumnType("nvarchar(max)");
b.Property<bool>("IsPublished")
.HasColumnType("bit");
b.Property<string>("Persona")
.HasColumnType("nvarchar(max)");
b.Property<bool>("STTActive")
.HasColumnType("bit");
b.Property<string>("SiteDescription")
.HasColumnType("nvarchar(max)");
b.Property<string>("SiteName")
.HasColumnType("nvarchar(max)");
b.Property<bool>("TTSActive")
.HasColumnType("bit");
b.Property<int?>("TemplateId")
.HasColumnType("int");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("nvarchar(450)");
b.Property<string>("VoiceId")
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.HasIndex("TemplateId");
b.HasIndex("UserId");
b.ToTable("SiteInfos");
b.HasData(
new
{
Id = 1,
BrandLogoUrl = "/images/default-logo.png",
DefaultColor = "#FFFFFF",
DefaultUrl = "https://ai.poppixel.cloud",
DomainUrl = "poppixel.cloud",
IsPublished = false,
STTActive = false,
SiteName = "Default Site",
TTSActive = false,
TemplateId = 1,
UserId = "0988758e-e16c-4c2c-8c1e-efa3ac5f0274"
});
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
{
b.Property<string>("Id")
.HasColumnType("nvarchar(450)");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("nvarchar(max)");
b.Property<string>("Name")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("NormalizedName")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasDatabaseName("RoleNameIndex")
.HasFilter("[NormalizedName] IS NOT NULL");
b.ToTable("AspNetRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType")
.HasColumnType("nvarchar(max)");
b.Property<string>("ClaimValue")
.HasColumnType("nvarchar(max)");
b.Property<string>("RoleId")
.IsRequired()
.HasColumnType("nvarchar(450)");
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b =>
{
b.Property<string>("Id")
.HasColumnType("nvarchar(450)");
b.Property<int>("AccessFailedCount")
.HasColumnType("int");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("nvarchar(max)");
b.Property<string>("Email")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<bool>("EmailConfirmed")
.HasColumnType("bit");
b.Property<bool>("LockoutEnabled")
.HasColumnType("bit");
b.Property<DateTimeOffset?>("LockoutEnd")
.HasColumnType("datetimeoffset");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("NormalizedUserName")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("PasswordHash")
.HasColumnType("nvarchar(max)");
b.Property<string>("PhoneNumber")
.HasColumnType("nvarchar(max)");
b.Property<bool>("PhoneNumberConfirmed")
.HasColumnType("bit");
b.Property<string>("SecurityStamp")
.HasColumnType("nvarchar(max)");
b.Property<bool>("TwoFactorEnabled")
.HasColumnType("bit");
b.Property<string>("UserName")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasDatabaseName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasDatabaseName("UserNameIndex")
.HasFilter("[NormalizedUserName] IS NOT NULL");
b.ToTable("AspNetUsers", (string)null);
b.HasData(
new
{
Id = "0988758e-e16c-4c2c-8c1e-efa3ac5f0274",
AccessFailedCount = 0,
ConcurrencyStamp = "a2836246-0303-4370-b283-e53a9a3f2813",
Email = "adam.g@aycode.com",
EmailConfirmed = true,
LockoutEnabled = false,
NormalizedEmail = "ADAM.G@AYCODE.COM",
NormalizedUserName = "ADAM.G@AYCODE.COM",
PasswordHash = "AQAAAAIAAYagAAAAEChxKCu+ReGvcZFR/6kPASbpnQdMp1MJuepeRyR4bfHTkUk8SfNAqmckGXvuw+GaGA==",
PhoneNumberConfirmed = false,
SecurityStamp = "7ecf121a-b0e7-4e30-a1f1-299eeaf0a9cc",
TwoFactorEnabled = false,
UserName = "adam.g@aycode.com"
});
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType")
.HasColumnType("nvarchar(max)");
b.Property<string>("ClaimValue")
.HasColumnType("nvarchar(max)");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("nvarchar(450)");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.Property<string>("LoginProvider")
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<string>("ProviderKey")
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<string>("ProviderDisplayName")
.HasColumnType("nvarchar(max)");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("nvarchar(450)");
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("nvarchar(450)");
b.Property<string>("RoleId")
.HasColumnType("nvarchar(450)");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("nvarchar(450)");
b.Property<string>("LoginProvider")
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<string>("Name")
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<string>("Value")
.HasColumnType("nvarchar(max)");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens", (string)null);
});
modelBuilder.Entity("BLAIzor.Models.CssTemplate", b =>
{
b.HasOne("BLAIzor.Models.DesignTemplate", "DesignTemplate")
.WithOne("CssTemplate")
.HasForeignKey("BLAIzor.Models.CssTemplate", "DesignTemplateId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("DesignTemplate");
});
modelBuilder.Entity("BLAIzor.Models.DesignTemplate", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", "User")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("BLAIzor.Models.FormDefinition", b =>
{
b.HasOne("BLAIzor.Models.SiteInfo", "SiteInfo")
.WithMany("FormDefinitions")
.HasForeignKey("SiteInfoId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("SiteInfo");
});
modelBuilder.Entity("BLAIzor.Models.MenuItem", b =>
{
b.HasOne("BLAIzor.Models.SiteInfo", "SiteInfo")
.WithMany("MenuItems")
.HasForeignKey("SiteInfoId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("SiteInfo");
});
modelBuilder.Entity("BLAIzor.Models.SiteInfo", b =>
{
b.HasOne("BLAIzor.Models.DesignTemplate", "Template")
.WithMany("Sites")
.HasForeignKey("TemplateId")
.OnDelete(DeleteBehavior.Restrict);
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", "User")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Template");
b.Navigation("User");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("BLAIzor.Models.DesignTemplate", b =>
{
b.Navigation("CssTemplate")
.IsRequired();
b.Navigation("Sites");
});
modelBuilder.Entity("BLAIzor.Models.SiteInfo", b =>
{
b.Navigation("FormDefinitions");
b.Navigation("MenuItems");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,83 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace BLAIzor.Migrations
{
/// <inheritdoc />
public partial class EmbeddingServiceAndDefaultLanguage : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.RenameColumn(
name: "voiceId",
table: "SiteInfos",
newName: "VoiceId");
migrationBuilder.AddColumn<string>(
name: "BackgroundVideo",
table: "SiteInfos",
type: "nvarchar(max)",
nullable: true);
migrationBuilder.AddColumn<string>(
name: "DefaultLanguage",
table: "SiteInfos",
type: "nvarchar(max)",
nullable: true);
migrationBuilder.AddColumn<string>(
name: "EmbeddingService",
table: "SiteInfos",
type: "nvarchar(max)",
nullable: true);
migrationBuilder.AlterColumn<string>(
name: "StoredHtml",
table: "MenuItems",
type: "nvarchar(max)",
nullable: true,
oldClrType: typeof(string),
oldType: "nvarchar(max)");
migrationBuilder.UpdateData(
table: "SiteInfos",
keyColumn: "Id",
keyValue: 1,
columns: new[] { "BackgroundVideo", "DefaultLanguage", "EmbeddingService" },
values: new object[] { null, null, null });
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "BackgroundVideo",
table: "SiteInfos");
migrationBuilder.DropColumn(
name: "DefaultLanguage",
table: "SiteInfos");
migrationBuilder.DropColumn(
name: "EmbeddingService",
table: "SiteInfos");
migrationBuilder.RenameColumn(
name: "VoiceId",
table: "SiteInfos",
newName: "voiceId");
migrationBuilder.AlterColumn<string>(
name: "StoredHtml",
table: "MenuItems",
type: "nvarchar(max)",
nullable: false,
defaultValue: "",
oldClrType: typeof(string),
oldType: "nvarchar(max)",
oldNullable: true);
}
}
}

View File

@ -193,7 +193,6 @@ namespace BLAIzor.Migrations
.HasColumnType("int");
b.Property<string>("StoredHtml")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
@ -211,12 +210,18 @@ namespace BLAIzor.Migrations
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("BackgroundVideo")
.HasColumnType("nvarchar(max)");
b.Property<string>("BrandLogoUrl")
.HasColumnType("nvarchar(max)");
b.Property<string>("DefaultColor")
.HasColumnType("nvarchar(max)");
b.Property<string>("DefaultLanguage")
.HasColumnType("nvarchar(max)");
b.Property<string>("DefaultUrl")
.IsRequired()
.HasColumnType("nvarchar(max)");
@ -225,6 +230,9 @@ namespace BLAIzor.Migrations
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("EmbeddingService")
.HasColumnType("nvarchar(max)");
b.Property<string>("Entity")
.HasColumnType("nvarchar(max)");
@ -253,7 +261,7 @@ namespace BLAIzor.Migrations
.IsRequired()
.HasColumnType("nvarchar(450)");
b.Property<string>("voiceId")
b.Property<string>("VoiceId")
.HasColumnType("nvarchar(max)");
b.HasKey("Id");

View File

@ -20,6 +20,7 @@
public string? Variant { get; set; } // 👈 NEW: e.g. "image-left", "image-right", "no-image"
public string SampleHtml { get; set; } //for design purposes
public List<string> Slots { get; set; } // e.g. ["title", "subtitle", "image", "cta"]
public float[] Vectors { get; set; }
}

View File

@ -17,7 +17,7 @@ namespace BLAIzor.Models
public bool ShowInMainMenu { get; set; } = false;
public string StoredHtml { get; set; }
public string? StoredHtml { get; set; }
public Guid? QdrantPointId { get; set; }

View File

@ -7,6 +7,7 @@ namespace BLAIzor.Models
{
public MenuItem MenuItem { get; set; }
public string Content { get; set; } = string.Empty;
public string ContentDescription { get; set; } = string.Empty;
public MenuItemModel(string name)
{

View File

@ -27,10 +27,13 @@ namespace BLAIzor.Models
public int? TemplateId { get; set; }
public bool TTSActive { get; set; } = false;
public bool STTActive { get; set; } = false;
public string? voiceId { get; set; }
public string? VoiceId { get; set; }
public string? Persona { get; set; }
public string? Entity { get; set; }
public string? DefaultLanguage { get; set; }
public string? BackgroundVideo { get; set; }
public string? EmbeddingService { get; set; }
// Navigation property for IdentityUser
public IdentityUser User { get; set; }

View File

@ -11,6 +11,7 @@ namespace BLAIzor.Models
public string Name { get; set; }
public string Description { get; set; }
public string Content { get; set; }
public float[] Vectors { get; set; }
public DateTime LastUpdated { get; set; }
}
}

View File

@ -48,6 +48,7 @@ builder.Services.AddSingleton<ContentService>();
builder.Services.AddScoped<ScopedContentService>();
builder.Services.AddScoped<QDrantService>();
builder.Services.AddScoped<OpenAIEmbeddingService>();
builder.Services.AddScoped<LocalEmbeddingService>();
builder.Services.AddScoped<HtmlSnippetProcessor>();
builder.Services
.AddHttpContextAccessor()
@ -64,6 +65,7 @@ builder.Services.AddScoped<DeepSeekApiService>();
builder.Services.AddScoped<OpenAiRealtimeService>();
builder.Services.AddScoped<CerebrasAPIService>();
builder.Services.AddScoped<CssInjectorService>();
builder.Services.AddScoped<LocalVectorSearchService>();
builder.Services.AddHostedService<TempFileCleanupService>();
builder.Services.AddServerSideBlazor().AddCircuitOptions(options => options.DetailedErrors = true).AddHubOptions(options =>
{

File diff suppressed because one or more lines are too long

View File

@ -13,8 +13,11 @@ namespace BLAIzor.Services
private readonly QDrantService _qDrantService;
private readonly HtmlSnippetProcessor _htmlSnippetProcessor;
private readonly IServiceScopeFactory _serviceScopeFactory;
private readonly ScopedContentService _scopedContentService;
public ContentEditorService(/*AIService aiService,*/ OpenAIApiService openAIApiService, /*ApplicationDbContext context,*/ QDrantService qDrantService, HtmlSnippetProcessor htmlSnippetProcessor, IServiceScopeFactory serviceScopeFactory)
public static IConfiguration? _configuration;
public ContentEditorService(/*AIService aiService,*/ OpenAIApiService openAIApiService, /*ApplicationDbContext context,*/ QDrantService qDrantService, HtmlSnippetProcessor htmlSnippetProcessor, IServiceScopeFactory serviceScopeFactory, ScopedContentService scopedContentService, IConfiguration? configuration)
{
//_aiService = aiService;
_openAIApiService = openAIApiService;
@ -22,8 +25,14 @@ namespace BLAIzor.Services
_qDrantService = qDrantService;
_htmlSnippetProcessor = htmlSnippetProcessor;
_serviceScopeFactory = serviceScopeFactory;
_scopedContentService = scopedContentService;
_configuration = configuration;
}
private string GetAiEmbeddingSettings() =>
_configuration?.GetSection("AiSettings")?.GetValue<string>("EmbeddingService") ?? string.Empty;
// Existing methods
public async Task<List<string>> GetMenuSuggestionsAsync(string sessionId, string prompt)
{
@ -152,7 +161,7 @@ namespace BLAIzor.Services
{
try
{
//check if Collection is created already
if (!hasCollection)
{
@ -160,6 +169,28 @@ namespace BLAIzor.Services
await _qDrantService.CreateQdrantCollectionAsync("Site" + SiteId.ToString());
hasCollection = true;
}
else
{
//check if Collection is really created already
var collection = await _qDrantService.GetCollectionCount("Site" + SiteId.ToString());
if(collection != null)
{
var siteInfo = await _scopedContentService.GetSiteInfoByIdAsync(SiteId);
if(siteInfo.EmbeddingService != null)
{
var currentEmbeddingServiceName = GetAiEmbeddingSettings();
if (siteInfo.EmbeddingService == currentEmbeddingServiceName)
{
//drop existing collection
await _qDrantService.DeleteCollectionAsync("Site" + SiteId.ToString());
//create will automatically handle the new vector size
await _qDrantService.CreateQdrantCollectionAsync("Site" + SiteId.ToString());
}
}
}
}
var updateQdrantList = ExtractedMenuItems.OrderBy(mi => mi.MenuItem.PointId).ToList();
for (int i = 0; i < updateQdrantList.Count(); i++)
@ -180,16 +211,23 @@ namespace BLAIzor.Services
content.Type = "page";
content.SiteId = SiteId;
content.Name = ExtractedMenuItems[i].MenuItem.Name;
if (ExtractedMenuItems[i].ContentDescription == null)
{
content.Description = $"A section called {ExtractedMenuItems[i].MenuItem.Name} of a website of {subject}";
}
else
{
content.Description = ExtractedMenuItems[i].ContentDescription;
}
content.Content = ExtractedMenuItems[i].Content;
content.LastUpdated = DateTime.UtcNow;
menuItem.QdrantPointId = guid;
menuItem.PointId = i;
await _htmlSnippetProcessor.ProcessAndStoreWebContentAsync(i, content, SiteId);
}
else
{
menuItem.PointId = i;
menuItem.QdrantPointId = ExtractedMenuItems[i].MenuItem.QdrantPointId;
}
@ -214,6 +252,7 @@ namespace BLAIzor.Services
}
else
{
thisItem.PointId = menuItem.PointId;
thisItem.QdrantPointId = menuItem.QdrantPointId;
thisItem.SortOrder = ExtractedMenuItems[i].MenuItem.SortOrder;
thisItem.ShowInMainMenu = ExtractedMenuItems[i].MenuItem.ShowInMainMenu;

View File

@ -1,6 +1,7 @@

using System;
using System.Collections.Generic;
using System.Numerics;
using System.Threading.Tasks;
using BLAIzor.Models;
using DocumentFormat.OpenXml.Office2010.Excel;
@ -16,16 +17,18 @@ namespace BLAIzor.Services
public class HtmlSnippetProcessor
{
private readonly OpenAIEmbeddingService _embeddingService;
private readonly OpenAIEmbeddingService _openAIEmbeddingService;
private readonly LocalEmbeddingService _localEmbeddingService;
private readonly HttpClient _httpClient;
private readonly QDrantService _drantService;
public static IConfiguration? _configuration;
private string _qdrantApiKey;
public HtmlSnippetProcessor(QDrantService drantService, IConfiguration? configuration)
public HtmlSnippetProcessor(QDrantService drantService, OpenAIEmbeddingService openAIEmbeddingService, LocalEmbeddingService localEmbeddingService, IConfiguration? configuration)
{
_drantService = drantService;
_embeddingService = new OpenAIEmbeddingService();
_openAIEmbeddingService = openAIEmbeddingService;
_localEmbeddingService = localEmbeddingService;
_httpClient = new HttpClient();
_configuration = configuration;
}
@ -35,6 +38,9 @@ namespace BLAIzor.Services
return _configuration?.GetSection("QDrant")?.GetValue<string>("ApiKey") ?? string.Empty;
}
private string GetAiEmbeddingSettings() =>
_configuration?.GetSection("AiSettings")?.GetValue<string>("EmbeddingService") ?? string.Empty;
//public async Task ProcessAndStoreSnippetsAsync()
//{
// _qdrantApiKey = GetApiKey();
@ -101,7 +107,20 @@ namespace BLAIzor.Services
$"Variant: {snippet.Variant ?? "default"}. " +
$"Tags: {snippet.Tags}. " +
$"HTML: {snippet.Html}";
var embedding = await _embeddingService.GenerateEmbeddingAsync(combinedText);
float[] embedding = [];
var embeddingServiceProvider = GetAiEmbeddingSettings();
//if (embeddingServiceProvider == "local")
//{
// embedding = await _localEmbeddingService.GenerateEmbeddingAsync(combinedText);
//}
//else
//{
// embedding = await _openAIEmbeddingService.GenerateEmbeddingAsync(combinedText);
//}
embedding = await _openAIEmbeddingService.GenerateEmbeddingAsync(combinedText);
// Add data for batch insertion
ids.Add(snippet.Id);
@ -133,69 +152,88 @@ namespace BLAIzor.Services
}
}
public async Task ProcessAndStoreWebContentsAsync(List<WebPageContent> pageContentList, int siteId)
{
_qdrantApiKey = GetApiKey();
//public async Task ProcessAndStoreWebContentsAsync(List<WebPageContent> pageContentList, int siteId)
//{
// _qdrantApiKey = GetApiKey();
var ids = new List<int>();
var vectors = new List<float[]>();
var payloads = new List<MapField<string, Value>>();
// var ids = new List<int>();
// var vectors = new List<float[]>();
// var payloads = new List<MapField<string, Value>>();
foreach (var content in pageContentList)
{
try
{
// Combine details to generate the embedding
var combinedText = $"{content.Name}: Description: {content.Description}, complete text: {content.Content}";
var embedding = await _embeddingService.GenerateEmbeddingAsync(combinedText);
// foreach (var content in pageContentList)
// {
// try
// {
// // Combine details to generate the embedding
// var combinedText = $"{content.Name}: Description: {content.Description}, complete text: {content.Content}";
// Add data for batch insertion
ids.Add(content.Id);
vectors.Add(embedding);
// float[] embedding = [];
// var embeddingServiceProvider = GetAiEmbeddingSettings();
// if (embeddingServiceProvider == "local")
// {
// embedding = await _localEmbeddingService.GenerateEmbeddingAsync(combinedText);
// }
// else
// {
// embedding = await _openAIEmbeddingService.GenerateEmbeddingAsync(combinedText);
// }
// // Add data for batch insertion
// ids.Add(content.Id);
// vectors.Add(embedding);
payloads.Add(new MapField<string, Value>
{
["uid"] = content.UId,
["type"] = content.Type,
["siteId"] = content.SiteId,
//["menuItemId"] = content.MenuItemId,
["name"] = content.Name,
["description"] = content.Description,
["content"] = content.Content,
["lastUpdated"] = content.LastUpdated.ToString()
});
}
catch (Exception ex)
{
Console.WriteLine($"Error processing content {content.Name}: {ex.Message}");
}
}
// payloads.Add(new MapField<string, Value>
// {
// ["uid"] = content.UId,
// ["type"] = content.Type,
// ["siteId"] = content.SiteId,
// //["menuItemId"] = content.MenuItemId,
// ["name"] = content.Name,
// ["description"] = content.Description,
// ["content"] = content.Content,
// ["lastUpdated"] = content.LastUpdated.ToString()
// });
// }
// catch (Exception ex)
// {
// Console.WriteLine($"Error processing content {content.Name}: {ex.Message}");
// }
// }
if (ids.Count > 0)
{
await _drantService.QDrantInsertManyAsync(ids, vectors, payloads, "Site"+siteId);
}
else
{
Console.WriteLine("No points were processed successfully.");
}
}
// if (ids.Count > 0)
// {
// await _drantService.QDrantInsertManyAsync(ids, vectors, payloads, "Site" + siteId);
// }
// else
// {
// Console.WriteLine("No points were processed successfully.");
// }
//}
public async Task ProcessAndStoreWebContentAsync(int id, WebPageContent pageContent, int siteId)
{
_qdrantApiKey = GetApiKey();
float[] vectors = [];
var payload = new MapField<string, Value>();
try
{
// Combine details to generate the embedding
var combinedText = $"{pageContent.Name}: {pageContent.Description} - {pageContent.Content}";
var embedding = await _embeddingService.GenerateEmbeddingAsync(combinedText);
float[] embedding = [];
var embeddingServiceProvider = GetAiEmbeddingSettings();
if (embeddingServiceProvider == "local")
{
embedding = await _localEmbeddingService.GenerateEmbeddingAsync(combinedText);
}
else
{
embedding = await _openAIEmbeddingService.GenerateEmbeddingAsync(combinedText);
}
// Add data for batch insertion

View File

@ -0,0 +1,45 @@
using System.Text;
using Newtonsoft.Json;
public class LocalEmbeddingService
{
private readonly HttpClient _httpClient;
private readonly string _localOllamaUrl = "http://localhost:11434/api/embeddings";
public LocalEmbeddingService()
{
_httpClient = new HttpClient();
}
public async Task<float[]> GenerateEmbeddingAsync(string text)
{
var payload = new
{
model = "bge-m3",
prompt = text
};
var content = new StringContent(JsonConvert.SerializeObject(payload), Encoding.UTF8, "application/json");
var response = await _httpClient.PostAsync(_localOllamaUrl, content);
if (response.IsSuccessStatusCode)
{
var responseContent = await response.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<OllamaEmbeddingResponse>(responseContent);
return result.Embedding;
}
else
{
var errorText = await response.Content.ReadAsStringAsync();
throw new Exception($"Failed to generate embedding: {response.StatusCode} - {errorText}");
}
}
}
public class OllamaEmbeddingResponse
{
[JsonProperty("embedding")]
public float[] Embedding { get; set; }
}

View File

@ -0,0 +1,89 @@
using BLAIzor.Models;
using System;
using System.Collections.Generic;
using System.Linq;
namespace BLAIzor.Services
{
public class LocalVectorSearchService
{
//private List<WebPageContent> _cachedContents = new();
//private List<HtmlSnippet> _cachedSnippets = new();
private int _cachedContentVersion = -1;
private int _cachedSnippetVersion = -1;
// Inject any needed services (e.g. DbContext, versioning service)
public LocalVectorSearchService()
{
}
//public void SetContentCache(List<WebPageContent> contents, int contentVersion)
//{
// _cachedContents = contents;
// _cachedContentVersion = contentVersion;
//}
//public void SetSnippetCache(List<HtmlSnippet> snippets, int snippetVersion)
//{
// _cachedSnippets = snippets;
// _cachedSnippetVersion = snippetVersion;
//}
public bool IsContentVersionOutdated(int currentVersion)
=> currentVersion != _cachedContentVersion;
public bool IsSnippetVersionOutdated(int currentVersion)
=> currentVersion != _cachedSnippetVersion;
public List<WebPageContent> SearchContent(float[] queryVector, List<WebPageContent> cachedContents, int maxResults = 5)
{
return cachedContents
.Select(c => new
{
Content = c,
Similarity = CosineSimilarity(queryVector, c.Vectors)
})
.OrderByDescending(x => x.Similarity)
.Take(maxResults)
.Select(x => x.Content)
.ToList();
}
public List<HtmlSnippet> SearchSnippets(float[] queryVector, List<HtmlSnippet> cachedSnippets, int maxResults = 5)
{
return cachedSnippets
.Select(s => new
{
Snippet = s,
Similarity = CosineSimilarity(queryVector, s.Vectors)
})
.OrderByDescending(x => x.Similarity)
.Take(maxResults)
.Select(x => x.Snippet)
.ToList();
}
private float CosineSimilarity(float[] vectorA, float[] vectorB)
{
if (vectorA == null || vectorB == null || vectorA.Length != vectorB.Length)
return 0f;
float dot = 0f, magA = 0f, magB = 0f;
for (int i = 0; i < vectorA.Length; i++)
{
dot += vectorA[i] * vectorB[i];
magA += vectorA[i] * vectorA[i];
magB += vectorB[i] * vectorB[i];
}
if (magA == 0 || magB == 0)
return 0f;
return dot / ((float)Math.Sqrt(magA) * (float)Math.Sqrt(magB));
}
}
}

View File

@ -3,6 +3,7 @@ using Google.Protobuf.Collections;
using Newtonsoft.Json;
using Qdrant.Client;
using Qdrant.Client.Grpc;
using System.Numerics;
using System.Text;
namespace BLAIzor.Services
@ -20,6 +21,9 @@ namespace BLAIzor.Services
_configuration = configuration;
}
private string GetAiEmbeddingSettings() =>
_configuration?.GetSection("AiSettings")?.GetValue<string>("EmbeddingService") ?? string.Empty;
public string GetApiKey()
{
if (_configuration == null)
@ -47,32 +51,51 @@ namespace BLAIzor.Services
return Convert.ToInt32(result);
}
public async Task CreateQdrantCollectionAsync()
{
_apiKey = GetApiKey();
var httpClient = new HttpClient();
//public async Task CreateQdrantCollectionAsync()
//{
// _apiKey = GetApiKey();
// var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Clear();
httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {_apiKey}");
// httpClient.DefaultRequestHeaders.Clear();
// httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {_apiKey}");
var collectionName = "web_content";
var createCollectionPayload = new
{
vectors = new { size = 1536, distance = "Cosine" } // Adjust size based on embedding model
};
// var collectionName = "web_content";
var content = new StringContent(JsonConvert.SerializeObject(createCollectionPayload), Encoding.UTF8, "application/json");
var response = await httpClient.PutAsync($"{qdrantUrl}/collections/{collectionName}", content);
// var createCollectionPayload = new
// {
// vectors = new { size = 0, distance = "" } // Adjust size based on embedding model
// };
if (response.IsSuccessStatusCode)
{
Console.WriteLine("Collection created successfully!" + response.Content.ReadAsStringAsync());
}
else
{
Console.WriteLine($"Failed to create collection: {response.StatusCode}");
}
}
// var embeddingServiceProvider = GetAiEmbeddingSettings();
// if (embeddingServiceProvider == "local")
// {
// createCollectionPayload = new
// {
// vectors = new { size = 1024, distance = "Cosine" } // Adjust size based on embedding model
// };
// }
// else
// {
// createCollectionPayload = new
// {
// vectors = new { size = 1536, distance = "Cosine" } // Adjust size based on embedding model
// };
// }
// var content = new StringContent(JsonConvert.SerializeObject(createCollectionPayload), Encoding.UTF8, "application/json");
// var response = await httpClient.PutAsync($"{qdrantUrl}/collections/{collectionName}", content);
// if (response.IsSuccessStatusCode)
// {
// Console.WriteLine("Collection created successfully!" + response.Content.ReadAsStringAsync());
// }
// else
// {
// Console.WriteLine($"Failed to create collection: {response.StatusCode}");
// }
//}
public async Task<string> GetCollectionBySiteIdAsync(int siteId)
{
@ -126,9 +149,25 @@ namespace BLAIzor.Services
httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {_apiKey}");
var createCollectionPayload = new
{
vectors = new { size = 0, distance = "" } // Adjust size based on embedding model
};
var embeddingServiceProvider = GetAiEmbeddingSettings();
if (embeddingServiceProvider == "local")
{
createCollectionPayload = new
{
vectors = new { size = 1024, distance = "Cosine" } // Adjust size based on embedding model
};
}
else
{
createCollectionPayload = new
{
vectors = new { size = 1536, distance = "Cosine" } // Adjust size based on embedding model
};
}
var content = new StringContent(JsonConvert.SerializeObject(createCollectionPayload), Encoding.UTF8, "application/json");
var response = await httpClient.PutAsync($"{qdrantUrl}/collections/{collectionName}", content);
@ -256,15 +295,6 @@ namespace BLAIzor.Services
_apiKey = GetApiKey();
var httpClient = new HttpClient();
//httpClient.DefaultRequestHeaders.Clear();
//httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {_apiKey}");
//var queryPayload = new
//{
// vector = queryVector,
// limit = limit
//};
var client = new QdrantClient(
host: _qdrantHost,
https: true,
@ -408,6 +438,15 @@ namespace BLAIzor.Services
}
public async Task DeleteCollectionAsync(string collectionName)
{
_apiKey = GetApiKey();
var client = new QdrantClient(_qdrantHost, 6334, true, _apiKey);
await client.DeleteCollectionAsync(collectionName);
}
}
public class PointResult
@ -438,6 +477,11 @@ namespace BLAIzor.Services
public int id { get; set; }
public HtmlSnippet payload { get; set; }
public List<double> vector { get; set; }
public float[] GetFloatVector()
{
return vector?.Select(d => (float)d).ToArray();
}
}
@ -460,6 +504,10 @@ namespace BLAIzor.Services
public int id { get; set; }
public ContentPayload payload { get; set; }
public List<double> vector { get; set; }
public float[] GetFloatVector()
{
return vector?.Select(d => (float)d).ToArray();
}
}
public class ContentPayload

View File

@ -18,7 +18,6 @@ namespace BLAIzor.Services
public string CurrentDOM { get; set; }
public string currentLocale { get; set; }
public string SelectedDocument { get; set; } = "Poppixel.docx";
private string _selectedBrandName;
public string SelectedBrandName
{
@ -33,6 +32,8 @@ namespace BLAIzor.Services
}
}
public List<HtmlSnippet> AvailableTemplateSnippets { get; set; }
public List<WebPageContent> AvailableSiteContent { get; set; }
public event Action OnBrandNameChanged;
public int SelectedSiteId { get; set; } = 1;
//public string SelectedDocument { get; set; } = "Poppixel.docx";
@ -64,8 +65,6 @@ namespace BLAIzor.Services
}
}
}
public async Task<SiteInfo?> GetSiteInfoWithFormsByIdAsync(int siteId)

View File

@ -23,7 +23,9 @@
"AiSettings": {
"Provider": "cerebras",
//"Provider": "chatgpt",
"VoiceActivated": true
"VoiceActivated": true,
"EmbeddingService": "openai"
//"EmbeddingService": "local"
},
"DeepSeek": {
"ApiKey": "sk-b97350ccb28c4129b5df08835bf2ea5f"
@ -33,6 +35,7 @@
"Model": "llama-3.3-70b"
//"Model": "llama-4-scout-17b-16e-instruct"
//"Model": "qwen-3-32b"
//"Model": "deepseek-r1-distill-llama-70b"
//"Model": "llama3.1-8b"
},
"OpenAI": {

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,471 @@
@import url('https://fonts.googleapis.com/css2?family=Quicksand&display=swap');
/*search*/
p {
font-size: x-large;
}
label {
display: none;
}
li {
list-style: none;
}
.btn {
background: rgba(255, 255, 255, 0.5);
border: 0;
color: #000000;
/* width: 98%; */
font-weight: bold;
border-radius: 10px;
/*min-height: 50px;*/
transition: all 0.2s ease;
padding: 10px;
margin: 10px;
}
.menubtn {
background: rgba(255, 255, 255, 0.5);
border: 0;
color: #000000;
/* width: 98%; */
font-weight: bold;
border-radius: 10px;
height: 40px;
transition: all 0.2s ease;
padding: 10px;
margin: 10px;
}
.btn:active {
background: rgba(255, 255, 255, 1);
}
.btn:hover {
background: rgba(255, 255, 255, 0.8);
}
img {
border-radius: 20px !important;
}
input.search_bar{
border: none;
outline: none;
width: 75px;
border-radius: 55px;
margin: 0 auto;
font-size: 1.3em;
color: #0d2840;
padding: 15px 30px 15px 45px;
transition: all .3s cubic-bezier(0,0,.5,1.5);
box-shadow: 0 3px 10px -2px rgba(0,0,0,.1);
background: rgba(255, 255, 255, 0.3) url(https://i.imgur.com/seveWIw.png) no-repeat center center;
}
input.search_bar:focus{
width: 100%;
background-position: calc(100% - 35px) center
}
/*Removes default x in search fields (webkit only i guess)*/
input[type=search]::-webkit-search-cancel-button {
-webkit-appearance: none;
}
/*Changes the color of the placeholder*/
::-webkit-input-placeholder {
color: #0d2840;
opacity: .5;
}
:-moz-placeholder {
color: #0d2840;
opacity: .5;
}
::-moz-placeholder {
color: #0d2840;
opacity: .5;
}
:-ms-input-placeholder {
color: #0d2840;
opacity: .5;
}
/*search*/
/*Search2*/
.searchBox {
width: 60px;
background: rgba(255, 255, 255, 0.3);
height: 60px;
border-radius: 40px;
padding: 10px;
margin: 0 auto;
transition: 0.8s;
}
.searchInput:active > .searchBox{
width:100%
}
.searchInput:focus > .searchBox {
width: 100%
}
.searchInput::placeholder {
color: #fff;
}
.searchBox:hover {
width: 100%;
}
.searchBox:hover > .searchInput {
width: calc(100% - 60px);
padding: 0 6px;
}
.searchBox:hover > .searchButton {
background: white;
color: #2f3640;
}
.searchButton {
color: white;
float: right;
width: 40px;
height: 40px;
border-radius: 50%;
background-color: #e9bb86;
display: flex;
justify-content: center;
align-items: center;
}
.searchInput {
border: none;
background: none;
outline: none;
font-size: 1.3em !important;
color: #e9bb86 !important;
float: left;
padding: 0;
color: white;
font-size: 16px;
transition: 0.4s;
line-height: 40px;
width: 0px;
}
/*Search2*/
.event {
border-radius: 20px !important;
background-color: rgba(255, 255, 255, 0.2) !important;
backdrop-filter: blur(20px);
border: 0;
box-shadow: 0 2px 20px rgba(0, 0, 0, 0.06), 0 2px 4px rgba(0, 0, 0, 0.07);
transition: all 0.15s ease;
}
/*card design*/
.card {
border-radius: 10px !important;
overflow: hidden;
background-color: rgba(255, 255, 255, 0.2) !important;
backdrop-filter: blur(20px);
border: 0;
box-shadow: 0 2px 20px rgba(0, 0, 0, 0.06), 0 2px 4px rgba(0, 0, 0, 0.07);
transition: all 0.15s ease;
margin: 10px;
}
.card:hover {
box-shadow: 0 6px 30px rgba(0, 0, 0, 0.1), 0 10px 8px rgba(0, 0, 0, 0.015);
}
.card-body .card-title {
font-family: 'Lato', sans-serif;
font-weight: 700;
letter-spacing: 0.3px;
font-size: 24px;
color: #121212;
}
.card-text {
font-family: 'Lato', sans-serif;
font-weight: 400;
font-size: 15px;
letter-spacing: 0.3px;
color: #fff;
}
.card .container {
width: 88%;
/*background: #F0EEF8;*/
border-radius: 30px;
/*height: 140px;*/
display: flex;
align-items: center;
justify-content: center;
}
.container:hover > img {
transform: scale(1.2);
}
.container img {
/*padding: 75px;*/
/*margin-top: -40px;
margin-bottom: -40px;*/
transition: 0.4s ease;
cursor: pointer;
}
.btn:hover {
background: #9b7d5a;
}
.btn:focus {
background: #9b7d5a;
outline: 0;
}
/*card design*/
/*bg*/
:root {
font-size: 15px;
}
body {
font-family: 'Quicksand', sans-serif;
color: #f2d8bb;
margin: 0;
min-height: 100vh;
background-color: #000;
/*background-image: radial-gradient(closest-side, rgba(235, 105, 78, 1), rgba(235, 105, 78, 0)), radial-gradient(closest-side, rgba(243, 11, 164, 1), rgba(243, 11, 164, 0)), radial-gradient(closest-side, rgba(254, 234, 131, 1), rgba(254, 234, 131, 0)), radial-gradient(closest-side, rgba(170, 142, 245, 1), rgba(170, 142, 245, 0)), radial-gradient(closest-side, rgba(248, 192, 147, 1), rgba(248, 192, 147, 0));*/
/*background-size: 130vmax 130vmax, 80vmax 80vmax, 90vmax 90vmax, 110vmax 110vmax, 90vmax 90vmax;*/
/*background-position: -80vmax -80vmax, 60vmax -30vmax, 10vmax 10vmax, -30vmax -10vmax, 50vmax 50vmax;*/
background-repeat: no-repeat;
/*animation: 10s movement linear infinite;*/
}
body::after {
content: '';
display: block;
position: fixed;
width: 100%;
height: 100%;
top: 0;
left: 0;
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
}
.myspan {
position: relative;
z-index: 10;
display: flex;
min-height: 100vh;
width: 100%;
justify-content: center;
align-items: center;
font-size: 5rem;
color: transparent;
text-shadow: 0px 0px 1px rgba(255, 255, 255, .6), 0px 4px 4px rgba(0, 0, 0, .05);
letter-spacing: .2rem;
}
/*@keyframes movement {
0%, 100% {
background-size: 130vmax 130vmax, 80vmax 80vmax, 90vmax 90vmax, 110vmax 110vmax, 90vmax 90vmax;
background-position: -80vmax -80vmax, 60vmax -30vmax, 10vmax 10vmax, -30vmax -10vmax, 50vmax 50vmax;
}
25% {
background-size: 100vmax 100vmax, 90vmax 90vmax, 100vmax 100vmax, 90vmax 90vmax, 60vmax 60vmax;
background-position: -60vmax -90vmax, 50vmax -40vmax, 0vmax -20vmax, -40vmax -20vmax, 40vmax 60vmax;
}
50% {
background-size: 80vmax 80vmax, 110vmax 110vmax, 80vmax 80vmax, 60vmax 60vmax, 80vmax 80vmax;
background-position: -50vmax -70vmax, 40vmax -30vmax, 10vmax 0vmax, 20vmax 10vmax, 30vmax 70vmax;
}
75% {
background-size: 90vmax 90vmax, 90vmax 90vmax, 100vmax 100vmax, 90vmax 90vmax, 70vmax 70vmax;
background-position: -50vmax -40vmax, 50vmax -30vmax, 20vmax 0vmax, -10vmax 10vmax, 40vmax 60vmax;
}
}*/
/*bg*/
.mytextarea {
background-color: rgba(255, 255, 255, 0.3);
backdrop-filter: blur(20px);
padding: 10px;
border-radius: 10px;
border-width: 0px;
height: unset !important;
}
.mytextarea:active {
border-width: 0px;
}
.mytextarea:focus-visible {
background-color: rgba(255, 255, 255, 0.5);
border-width: 0px !important;
outline: -webkit-focus-ring-color auto 0px;
outline-color: transparent;
}
.navbar-toggler {
color: #fff;
}
.navbar-brand {
font-size: 1.7rem;
color: #e9bb86;
}
.form-select {
background-color: rgba(255, 255, 255, 0.2);
border-radius: 15px;
display: unset !important;
}
.form-select > option {
background-color: rgba(255, 255, 255, 0.2)
}
.contactform-overlay {
position: fixed;
z-index: 100;
height: 100vh;
width: 100%;
padding: 100px;
top: 0px;
left: 0px;
/* padding-top: 10vh; */
backdrop-filter: blur(20px);
/* background-color: rgba(1, 1, 1, .4); */
}
.form-control {
background-color: rgba(255,255,255,0.4);
border-radius: 15px;
height: 50px;
}
form-control::placeholder{
color: #fff;
}
.contactform-close-overlay {
position: relative;
height: 10vh;
}
.contactform-popup-content {
height: 80vh;
margin: 0px;
padding: 0px;
}
.contactform-popup-close {
position: relative;
height: 10vh;
z-index: 80;
}
.calendly-overlay {
position: absolute;
z-index: 100;
height: 100vh;
width: 100%;
top: 0px;
/* padding-top: 10vh; */
backdrop-filter: blur(20px);
/* background-color: rgba(1, 1, 1, .4); */
}
.calendly-close-overlay {
position: relative;
height: 10vh;
}
.calendly-popup-content {
height: 80vh;
margin: 0px;
padding: 0px;
}
.calendly-popup-close {
position: relative;
height: 10vh;
z-index: 80;
}
#myVideo {
position: fixed;
right: 0;
bottom: 0;
min-width: 100%;
min-height: 100%;
}
.table {
color: #f2d8bb !important;
}
.navbar-collapse {
height: 100vh;
/*display: flex;
align-items: center;
justify-content: center;*/
text-align: center !important;
align-content: center;
}
.navbar-collapse .nav-link {
font-size: 1.4em;
letter-spacing: 2px;
}
.navbar-collapse .nav-item:not(:last-child) {
border-bottom: 0px solid white;
padding: 0.2em 1em;
}
.navbar {
background-color: #1c120c;
color: #fff;
}
.nav-link {
color: #f2d8bb !important;
}
.content {
top: 60px;
}
.text-dark {
color: #fff !important;
}
.img-fluid {
max-height: 50vh !important;
width: auto;
}

View File

@ -0,0 +1,471 @@
@import url('https://fonts.googleapis.com/css2?family=Quicksand&display=swap');
/*search*/
p {
font-size: x-large;
}
label {
display: none;
}
li {
list-style: none;
}
.btn {
background: rgba(255, 255, 255, 0.5);
border: 0;
color: #000000;
/* width: 98%; */
font-weight: bold;
border-radius: 10px;
/*min-height: 50px;*/
transition: all 0.2s ease;
padding: 10px;
margin: 10px;
}
.menubtn {
background: rgba(255, 255, 255, 0.5);
border: 0;
color: #000000;
/* width: 98%; */
font-weight: bold;
border-radius: 10px;
height: 40px;
transition: all 0.2s ease;
padding: 10px;
margin: 10px;
}
.btn:active {
background: rgba(255, 255, 255, 1);
}
.btn:hover {
background: rgba(255, 255, 255, 0.8);
}
img {
border-radius: 20px !important;
}
input.search_bar{
border: none;
outline: none;
width: 75px;
border-radius: 55px;
margin: 0 auto;
font-size: 1.3em;
color: #0d2840;
padding: 15px 30px 15px 45px;
transition: all .3s cubic-bezier(0,0,.5,1.5);
box-shadow: 0 3px 10px -2px rgba(0,0,0,.1);
background: rgba(255, 255, 255, 0.3) url(https://i.imgur.com/seveWIw.png) no-repeat center center;
}
input.search_bar:focus{
width: 100%;
background-position: calc(100% - 35px) center
}
/*Removes default x in search fields (webkit only i guess)*/
input[type=search]::-webkit-search-cancel-button {
-webkit-appearance: none;
}
/*Changes the color of the placeholder*/
::-webkit-input-placeholder {
color: #0d2840;
opacity: .5;
}
:-moz-placeholder {
color: #0d2840;
opacity: .5;
}
::-moz-placeholder {
color: #0d2840;
opacity: .5;
}
:-ms-input-placeholder {
color: #0d2840;
opacity: .5;
}
/*search*/
/*Search2*/
.searchBox {
width: 60px;
background: rgba(255, 255, 255, 0.3);
height: 60px;
border-radius: 40px;
padding: 10px;
margin: 0 auto;
transition: 0.8s;
}
.searchInput:active > .searchBox{
width:100%
}
.searchInput:focus > .searchBox {
width: 100%
}
.searchInput::placeholder {
color: #fff;
}
.searchBox:hover {
width: 100%;
}
.searchBox:hover > .searchInput {
width: calc(100% - 60px);
padding: 0 6px;
}
.searchBox:hover > .searchButton {
background: white;
color: #2f3640;
}
.searchButton {
color: white;
float: right;
width: 40px;
height: 40px;
border-radius: 50%;
background-color: #e9bb86;
display: flex;
justify-content: center;
align-items: center;
}
.searchInput {
border: none;
background: none;
outline: none;
font-size: 1.3em !important;
color: #e9bb86 !important;
float: left;
padding: 0;
color: white;
font-size: 16px;
transition: 0.4s;
line-height: 40px;
width: 0px;
}
/*Search2*/
.event {
border-radius: 20px !important;
background-color: rgba(255, 255, 255, 0.2) !important;
backdrop-filter: blur(20px);
border: 0;
box-shadow: 0 2px 20px rgba(0, 0, 0, 0.06), 0 2px 4px rgba(0, 0, 0, 0.07);
transition: all 0.15s ease;
}
/*card design*/
.card {
border-radius: 10px !important;
overflow: hidden;
background-color: rgba(255, 255, 255, 0.2) !important;
backdrop-filter: blur(20px);
border: 0;
box-shadow: 0 2px 20px rgba(0, 0, 0, 0.06), 0 2px 4px rgba(0, 0, 0, 0.07);
transition: all 0.15s ease;
margin: 10px;
}
.card:hover {
box-shadow: 0 6px 30px rgba(0, 0, 0, 0.1), 0 10px 8px rgba(0, 0, 0, 0.015);
}
.card-body .card-title {
font-family: 'Lato', sans-serif;
font-weight: 700;
letter-spacing: 0.3px;
font-size: 24px;
color: #121212;
}
.card-text {
font-family: 'Lato', sans-serif;
font-weight: 400;
font-size: 15px;
letter-spacing: 0.3px;
color: #fff;
}
.card .container {
width: 88%;
/*background: #F0EEF8;*/
border-radius: 30px;
/*height: 140px;*/
display: flex;
align-items: center;
justify-content: center;
}
.container:hover > img {
transform: scale(1.2);
}
.container img {
/*padding: 75px;*/
/*margin-top: -40px;
margin-bottom: -40px;*/
transition: 0.4s ease;
cursor: pointer;
}
.btn:hover {
background: #9b7d5a;
}
.btn:focus {
background: #9b7d5a;
outline: 0;
}
/*card design*/
/*bg*/
:root {
font-size: 15px;
}
body {
font-family: 'Quicksand', sans-serif;
color: #f2d8bb;
margin: 0;
min-height: 100vh;
background-color: #000;
/*background-image: radial-gradient(closest-side, rgba(235, 105, 78, 1), rgba(235, 105, 78, 0)), radial-gradient(closest-side, rgba(243, 11, 164, 1), rgba(243, 11, 164, 0)), radial-gradient(closest-side, rgba(254, 234, 131, 1), rgba(254, 234, 131, 0)), radial-gradient(closest-side, rgba(170, 142, 245, 1), rgba(170, 142, 245, 0)), radial-gradient(closest-side, rgba(248, 192, 147, 1), rgba(248, 192, 147, 0));*/
/*background-size: 130vmax 130vmax, 80vmax 80vmax, 90vmax 90vmax, 110vmax 110vmax, 90vmax 90vmax;*/
/*background-position: -80vmax -80vmax, 60vmax -30vmax, 10vmax 10vmax, -30vmax -10vmax, 50vmax 50vmax;*/
background-repeat: no-repeat;
/*animation: 10s movement linear infinite;*/
}
body::after {
content: '';
display: block;
position: fixed;
width: 100%;
height: 100%;
top: 0;
left: 0;
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
}
.myspan {
position: relative;
z-index: 10;
display: flex;
min-height: 100vh;
width: 100%;
justify-content: center;
align-items: center;
font-size: 5rem;
color: transparent;
text-shadow: 0px 0px 1px rgba(255, 255, 255, .6), 0px 4px 4px rgba(0, 0, 0, .05);
letter-spacing: .2rem;
}
/*@keyframes movement {
0%, 100% {
background-size: 130vmax 130vmax, 80vmax 80vmax, 90vmax 90vmax, 110vmax 110vmax, 90vmax 90vmax;
background-position: -80vmax -80vmax, 60vmax -30vmax, 10vmax 10vmax, -30vmax -10vmax, 50vmax 50vmax;
}
25% {
background-size: 100vmax 100vmax, 90vmax 90vmax, 100vmax 100vmax, 90vmax 90vmax, 60vmax 60vmax;
background-position: -60vmax -90vmax, 50vmax -40vmax, 0vmax -20vmax, -40vmax -20vmax, 40vmax 60vmax;
}
50% {
background-size: 80vmax 80vmax, 110vmax 110vmax, 80vmax 80vmax, 60vmax 60vmax, 80vmax 80vmax;
background-position: -50vmax -70vmax, 40vmax -30vmax, 10vmax 0vmax, 20vmax 10vmax, 30vmax 70vmax;
}
75% {
background-size: 90vmax 90vmax, 90vmax 90vmax, 100vmax 100vmax, 90vmax 90vmax, 70vmax 70vmax;
background-position: -50vmax -40vmax, 50vmax -30vmax, 20vmax 0vmax, -10vmax 10vmax, 40vmax 60vmax;
}
}*/
/*bg*/
.mytextarea {
background-color: rgba(255, 255, 255, 0.3);
backdrop-filter: blur(20px);
padding: 10px;
border-radius: 10px;
border-width: 0px;
height: unset !important;
}
.mytextarea:active {
border-width: 0px;
}
.mytextarea:focus-visible {
background-color: rgba(255, 255, 255, 0.5);
border-width: 0px !important;
outline: -webkit-focus-ring-color auto 0px;
outline-color: transparent;
}
.navbar-toggler {
color: #fff;
}
.navbar-brand {
font-size: 1.7rem;
color: #e9bb86;
}
.form-select {
background-color: rgba(255, 255, 255, 0.2);
border-radius: 15px;
display: unset !important;
}
.form-select > option {
background-color: rgba(255, 255, 255, 0.2)
}
.contactform-overlay {
position: fixed;
z-index: 100;
height: 100vh;
width: 100%;
padding: 100px;
top: 0px;
left: 0px;
/* padding-top: 10vh; */
backdrop-filter: blur(20px);
/* background-color: rgba(1, 1, 1, .4); */
}
.form-control {
background-color: rgba(255,255,255,0.4);
border-radius: 15px;
height: 50px;
}
form-control::placeholder{
color: #fff;
}
.contactform-close-overlay {
position: relative;
height: 10vh;
}
.contactform-popup-content {
height: 80vh;
margin: 0px;
padding: 0px;
}
.contactform-popup-close {
position: relative;
height: 10vh;
z-index: 80;
}
.calendly-overlay {
position: absolute;
z-index: 100;
height: 100vh;
width: 100%;
top: 0px;
/* padding-top: 10vh; */
backdrop-filter: blur(20px);
/* background-color: rgba(1, 1, 1, .4); */
}
.calendly-close-overlay {
position: relative;
height: 10vh;
}
.calendly-popup-content {
height: 80vh;
margin: 0px;
padding: 0px;
}
.calendly-popup-close {
position: relative;
height: 10vh;
z-index: 80;
}
#myVideo {
position: fixed;
right: 0;
bottom: 0;
min-width: 100%;
min-height: 100%;
}
.table {
color: #f2d8bb !important;
}
.navbar-collapse {
height: 100vh;
/*display: flex;
align-items: center;
justify-content: center;*/
text-align: center !important;
align-content: center;
}
.navbar-collapse .nav-link {
font-size: 1.4em;
letter-spacing: 2px;
}
.navbar-collapse .nav-item:not(:last-child) {
border-bottom: 0px solid white;
padding: 0.2em 1em;
}
.navbar {
background-color: #1c120c;
color: #fff;
}
.nav-link {
color: #f2d8bb !important;
}
.content {
top: 60px;
}
.text-dark {
color: #fff !important;
}
.img-fluid {
max-height: 50vh !important;
width: auto;
}

View File

@ -1,168 +0,0 @@
@import url('https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100..900;1,100..900&display=swap');
.card {
background-color: rgba(255, 255, 255, 0.3);
backdrop-filter: blur(2px);
}
.table {
color: #f2d8bb !important;
}
.navbar-collapse {
height: 100vh;
text-align: center !important;
align-content: center;
}
.navbar-collapse .nav-link {
font-size: 1.2rem;
letter-spacing: 2px;
}
.navbar-collapse .nav-item:not(:last-child) {
padding: 0.2em 1em;
}
.navbar {
background-color: #022c28;
color: #d0eae9;
}
.nav-link {
color: #d0eae9 !important;
}
.content {
top: 60px;
}
body {
background-color: #022c28;
background-attachment: fixed;
background-position: center;
background-size: cover;
color: aqua;
font-family: "Montserrat", sans-serif;
font-optical-sizing: auto;
font-weight: 300;
font-style: normal;
font-size: 1rem; /* Base size */
}
h1 {
font-weight: 700;
font-size: 2.5rem;
padding-top: 30px;
padding-bottom: 10px;
}
h2 {
font-weight: 500;
font-size: 2rem;
padding-top: 15px;
padding-bottom: 10px;
}
h3 {
font-weight: 400;
font-size: 1.6rem;
padding-top: 10px;
padding-bottom: 10px;
}
p {
color: #fff;
font-size: 1.1rem;
}
li {
color: #fff;
font-size: 1rem;
text-align: center;
}
.btn-primary {
color: #d0eae9;
background-color: #014d4e;
border: 0px;
margin: 5px;
}
.btn-primary:hover {
color: #fff;
background-color: #086262;
border: 0px;
}
.row {
padding-bottom: 30px;
margin: 0 auto !important;
}
.searchInput::placeholder {
color: #d0eae9;
}
#myVideo {
position: fixed;
right: 0;
top: -100px;
min-width: 100%;
min-height: 100%;
transform: translateX(calc((100% - 100vw) / 2));
}
.img-fluid {
max-height: 50vh;
width: auto;
border-radius: 15px;
}
.sp-img {
box-shadow: 10px 10px 30px 0px rgba(0,0,0,0.75);
-webkit-box-shadow: 10px 10px 30px 0px rgba(0,0,0,0.75);
-moz-box-shadow: 10px 10px 30px 0px rgba(0,0,0,0.75);
}
.col {
align-items: center;
display: flex;
}
.form-select {
background-color: rgba(255, 255, 255, 0.2);
border-radius: 5px;
display: unset !important;
color: #fff;
}
.list-group-item,
.bg-light {
background-color: rgb(11 24 23 / 76%) !important;
backdrop-filter: blur(8px) !important;
}
.text-primary {
--bs-text-opacity: 1;
color: aqua;
}
/* 🔽 Mobile Responsiveness */
@media (max-width: 768px) {
h1 { font-size: 2rem; }
h2 { font-size: 1.6rem; }
h3 { font-size: 1.4rem; }
p, li { font-size: 1rem; }
.navbar-collapse .nav-link { font-size: 1.1rem; }
p {text-align: justify;}
}
@media (max-width: 480px) {
h1 { font-size: 1.6rem; }
h2 { font-size: 1.3rem; }
h3 { font-size: 1.1rem; }
p, li { font-size: 0.95rem; }
.navbar-collapse .nav-link { font-size: 1rem; }
p {text-align: justify;}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,168 +0,0 @@
@import url('https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100..900;1,100..900&display=swap');
.card {
background-color: rgba(255, 255, 255, 0.3);
backdrop-filter: blur(2px);
}
.table {
color: #f2d8bb !important;
}
.navbar-collapse {
height: 100vh;
text-align: center !important;
align-content: center;
}
.navbar-collapse .nav-link {
font-size: 1.2rem;
letter-spacing: 2px;
}
.navbar-collapse .nav-item:not(:last-child) {
padding: 0.2em 1em;
}
.navbar {
background-color: #022c28;
color: #d0eae9;
}
.nav-link {
color: #d0eae9 !important;
}
.content {
top: 60px;
}
body {
background-color: #022c28;
background-attachment: fixed;
background-position: center;
background-size: cover;
color: aqua;
font-family: "Montserrat", sans-serif;
font-optical-sizing: auto;
font-weight: 300;
font-style: normal;
font-size: 1rem; /* Base size */
}
h1 {
font-weight: 700;
font-size: 2.5rem;
padding-top: 30px;
padding-bottom: 10px;
}
h2 {
font-weight: 500;
font-size: 2rem;
padding-top: 15px;
padding-bottom: 10px;
}
h3 {
font-weight: 400;
font-size: 1.6rem;
padding-top: 10px;
padding-bottom: 10px;
}
p {
color: #fff;
font-size: 1.1rem;
}
li {
color: #fff;
font-size: 1rem;
text-align: center;
}
.btn-primary {
color: #d0eae9;
background-color: #014d4e;
border: 0px;
margin: 5px;
}
.btn-primary:hover {
color: #fff;
background-color: #086262;
border: 0px;
}
.row {
padding-bottom: 30px;
margin: 0 auto !important;
}
.searchInput::placeholder {
color: #d0eae9;
}
#myVideo {
position: fixed;
right: 0;
top: -100px;
min-width: 100%;
min-height: 100%;
transform: translateX(calc((100% - 100vw) / 2));
}
.img-fluid {
max-height: 50vh;
width: auto;
border-radius: 15px;
}
.sp-img {
box-shadow: 10px 10px 30px 0px rgba(0,0,0,0.75);
-webkit-box-shadow: 10px 10px 30px 0px rgba(0,0,0,0.75);
-moz-box-shadow: 10px 10px 30px 0px rgba(0,0,0,0.75);
}
.col {
align-items: center;
display: flex;
}
.form-select {
background-color: rgba(255, 255, 255, 0.2);
border-radius: 5px;
display: unset !important;
color: #fff;
}
.list-group-item,
.bg-light {
background-color: rgb(11 24 23 / 76%) !important;
backdrop-filter: blur(8px) !important;
}
.text-primary {
--bs-text-opacity: 1;
color: aqua;
}
/* 🔽 Mobile Responsiveness */
@media (max-width: 768px) {
h1 { font-size: 2rem; }
h2 { font-size: 1.6rem; }
h3 { font-size: 1.4rem; }
p, li { font-size: 1rem; }
.navbar-collapse .nav-link { font-size: 1.1rem; }
p {text-align: justify;}
}
@media (max-width: 480px) {
h1 { font-size: 1.6rem; }
h2 { font-size: 1.3rem; }
h3 { font-size: 1.1rem; }
p, li { font-size: 0.95rem; }
.navbar-collapse .nav-link { font-size: 1rem; }
p {text-align: justify;}
}

View File

@ -1,168 +0,0 @@
@import url('https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100..900;1,100..900&display=swap');
.card {
background-color: rgba(255, 255, 255, 0.3);
backdrop-filter: blur(2px);
}
.table {
color: #f2d8bb !important;
}
.navbar-collapse {
height: 100vh;
text-align: center !important;
align-content: center;
}
.navbar-collapse .nav-link {
font-size: 1.2rem;
letter-spacing: 2px;
}
.navbar-collapse .nav-item:not(:last-child) {
padding: 0.2em 1em;
}
.navbar {
background-color: #022c28;
color: #d0eae9;
}
.nav-link {
color: #d0eae9 !important;
}
.content {
top: 60px;
}
body {
background-color: #022c28;
background-attachment: fixed;
background-position: center;
background-size: cover;
color: aqua;
font-family: "Montserrat", sans-serif;
font-optical-sizing: auto;
font-weight: 300;
font-style: normal;
font-size: 1rem; /* Base size */
}
h1 {
font-weight: 700;
font-size: 2.5rem;
padding-top: 30px;
padding-bottom: 10px;
}
h2 {
font-weight: 500;
font-size: 2rem;
padding-top: 15px;
padding-bottom: 10px;
}
h3 {
font-weight: 400;
font-size: 1.6rem;
padding-top: 10px;
padding-bottom: 10px;
}
p {
color: #fff;
font-size: 1.1rem;
}
li {
color: #fff;
font-size: 1rem;
text-align: center;
}
.btn-primary {
color: #d0eae9;
background-color: #014d4e;
border: 0px;
margin: 5px;
}
.btn-primary:hover {
color: #fff;
background-color: #086262;
border: 0px;
}
.row {
padding-bottom: 30px;
margin: 0 auto !important;
}
.searchInput::placeholder {
color: #d0eae9;
}
#myVideo {
position: fixed;
right: 0;
top: -100px;
min-width: 100%;
min-height: 100%;
transform: translateX(calc((100% - 100vw) / 2));
}
.img-fluid {
max-height: 50vh;
width: auto;
border-radius: 15px;
}
.sp-img {
box-shadow: 10px 10px 30px 0px rgba(0,0,0,0.75);
-webkit-box-shadow: 10px 10px 30px 0px rgba(0,0,0,0.75);
-moz-box-shadow: 10px 10px 30px 0px rgba(0,0,0,0.75);
}
.col {
align-items: center;
display: flex;
}
.form-select {
background-color: rgba(255, 255, 255, 0.2);
border-radius: 5px;
display: unset !important;
color: #fff;
}
.list-group-item,
.bg-light {
background-color: rgb(11 24 23 / 76%) !important;
backdrop-filter: blur(8px) !important;
}
.text-primary {
--bs-text-opacity: 1;
color: aqua;
}
/* 🔽 Mobile Responsiveness */
@media (max-width: 768px) {
h1 { font-size: 2rem; }
h2 { font-size: 1.6rem; }
h3 { font-size: 1.4rem; }
p, li { font-size: 1rem; }
.navbar-collapse .nav-link { font-size: 1.1rem; }
p {text-align: justify;}
}
@media (max-width: 480px) {
h1 { font-size: 1.6rem; }
h2 { font-size: 1.3rem; }
h3 { font-size: 1.1rem; }
p, li { font-size: 0.95rem; }
.navbar-collapse .nav-link { font-size: 1rem; }
p {text-align: justify;}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,168 +0,0 @@
@import url('https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100..900;1,100..900&display=swap');
.card {
background-color: rgba(255, 255, 255, 0.3);
backdrop-filter: blur(2px);
}
.table {
color: #f2d8bb !important;
}
.navbar-collapse {
height: 100vh;
text-align: center !important;
align-content: center;
}
.navbar-collapse .nav-link {
font-size: 1.2rem;
letter-spacing: 2px;
}
.navbar-collapse .nav-item:not(:last-child) {
padding: 0.2em 1em;
}
.navbar {
background-color: #022c28;
color: #d0eae9;
}
.nav-link {
color: #d0eae9 !important;
}
.content {
top: 60px;
}
body {
background-color: #022c28;
background-attachment: fixed;
background-position: center;
background-size: cover;
color: aqua;
font-family: "Montserrat", sans-serif;
font-optical-sizing: auto;
font-weight: 300;
font-style: normal;
font-size: 1rem; /* Base size */
}
h1 {
font-weight: 700;
font-size: 2.5rem;
padding-top: 30px;
padding-bottom: 10px;
}
h2 {
font-weight: 500;
font-size: 2rem;
padding-top: 15px;
padding-bottom: 10px;
}
h3 {
font-weight: 400;
font-size: 1.6rem;
padding-top: 10px;
padding-bottom: 10px;
}
p {
color: #fff;
font-size: 1.1rem;
}
li {
color: #fff;
font-size: 1rem;
text-align: center;
}
.btn-primary {
color: #d0eae9;
background-color: #014d4e;
border: 0px;
margin: 5px;
}
.btn-primary:hover {
color: #fff;
background-color: #086262;
border: 0px;
}
.row {
padding-bottom: 30px;
margin: 0 auto !important;
}
.searchInput::placeholder {
color: #d0eae9;
}
#myVideo {
position: fixed;
right: 0;
top: -100px;
min-width: 100%;
min-height: 100%;
transform: translateX(calc((100% - 100vw) / 2));
}
.img-fluid {
max-height: 50vh;
width: auto;
border-radius: 15px;
}
.sp-img {
box-shadow: 10px 10px 30px 0px rgba(0,0,0,0.75);
-webkit-box-shadow: 10px 10px 30px 0px rgba(0,0,0,0.75);
-moz-box-shadow: 10px 10px 30px 0px rgba(0,0,0,0.75);
}
.col {
align-items: center;
display: flex;
}
.form-select {
background-color: rgba(255, 255, 255, 0.2);
border-radius: 5px;
display: unset !important;
color: #fff;
}
.list-group-item,
.bg-light {
background-color: rgb(11 24 23 / 76%) !important;
backdrop-filter: blur(8px) !important;
}
.text-primary {
--bs-text-opacity: 1;
color: aqua;
}
/* 🔽 Mobile Responsiveness */
@media (max-width: 768px) {
h1 { font-size: 2rem; }
h2 { font-size: 1.6rem; }
h3 { font-size: 1.4rem; }
p, li { font-size: 1rem; }
.navbar-collapse .nav-link { font-size: 1.1rem; }
p {text-align: justify;}
}
@media (max-width: 480px) {
h1 { font-size: 1.6rem; }
h2 { font-size: 1.3rem; }
h3 { font-size: 1.1rem; }
p, li { font-size: 0.95rem; }
.navbar-collapse .nav-link { font-size: 1rem; }
p {text-align: justify;}
}

View File

@ -1,168 +0,0 @@
@import url('https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100..900;1,100..900&display=swap');
.card {
background-color: rgba(255, 255, 255, 0.3);
backdrop-filter: blur(2px);
}
.table {
color: #f2d8bb !important;
}
.navbar-collapse {
height: 100vh;
text-align: center !important;
align-content: center;
}
.navbar-collapse .nav-link {
font-size: 1.2rem;
letter-spacing: 2px;
}
.navbar-collapse .nav-item:not(:last-child) {
padding: 0.2em 1em;
}
.navbar {
background-color: #022c28;
color: #d0eae9;
}
.nav-link {
color: #d0eae9 !important;
}
.content {
top: 60px;
}
body {
background-color: #022c28;
background-attachment: fixed;
background-position: center;
background-size: cover;
color: aqua;
font-family: "Montserrat", sans-serif;
font-optical-sizing: auto;
font-weight: 300;
font-style: normal;
font-size: 1rem; /* Base size */
}
h1 {
font-weight: 700;
font-size: 2.5rem;
padding-top: 30px;
padding-bottom: 10px;
}
h2 {
font-weight: 500;
font-size: 2rem;
padding-top: 15px;
padding-bottom: 10px;
}
h3 {
font-weight: 400;
font-size: 1.6rem;
padding-top: 10px;
padding-bottom: 10px;
}
p {
color: #fff;
font-size: 1.1rem;
}
li {
color: #fff;
font-size: 1rem;
text-align: center;
}
.btn-primary {
color: #d0eae9;
background-color: #014d4e;
border: 0px;
margin: 5px;
}
.btn-primary:hover {
color: #fff;
background-color: #086262;
border: 0px;
}
.row {
padding-bottom: 30px;
margin: 0 auto !important;
}
.searchInput::placeholder {
color: #d0eae9;
}
#myVideo {
position: fixed;
right: 0;
top: -100px;
min-width: 100%;
min-height: 100%;
transform: translateX(calc((100% - 100vw) / 2));
}
.img-fluid {
max-height: 50vh;
width: auto;
border-radius: 15px;
}
.sp-img {
box-shadow: 10px 10px 30px 0px rgba(0,0,0,0.75);
-webkit-box-shadow: 10px 10px 30px 0px rgba(0,0,0,0.75);
-moz-box-shadow: 10px 10px 30px 0px rgba(0,0,0,0.75);
}
.col {
align-items: center;
display: flex;
}
.form-select {
background-color: rgba(255, 255, 255, 0.2);
border-radius: 5px;
display: unset !important;
color: #fff;
}
.list-group-item,
.bg-light {
background-color: rgb(11 24 23 / 76%) !important;
backdrop-filter: blur(8px) !important;
}
.text-primary {
--bs-text-opacity: 1;
color: aqua;
}
/* 🔽 Mobile Responsiveness */
@media (max-width: 768px) {
h1 { font-size: 2rem; }
h2 { font-size: 1.6rem; }
h3 { font-size: 1.4rem; }
p, li { font-size: 1rem; }
.navbar-collapse .nav-link { font-size: 1.1rem; }
p {text-align: justify;}
}
@media (max-width: 480px) {
h1 { font-size: 1.6rem; }
h2 { font-size: 1.3rem; }
h3 { font-size: 1.1rem; }
p, li { font-size: 0.95rem; }
.navbar-collapse .nav-link { font-size: 1rem; }
p {text-align: justify;}
}

View File

@ -1,168 +0,0 @@
@import url('https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100..900;1,100..900&display=swap');
.card {
background-color: rgba(255, 255, 255, 0.3);
backdrop-filter: blur(2px);
}
.table {
color: #f2d8bb !important;
}
.navbar-collapse {
height: 100vh;
text-align: center !important;
align-content: center;
}
.navbar-collapse .nav-link {
font-size: 1.2rem;
letter-spacing: 2px;
}
.navbar-collapse .nav-item:not(:last-child) {
padding: 0.2em 1em;
}
.navbar {
background-color: #022c28;
color: #d0eae9;
}
.nav-link {
color: #d0eae9 !important;
}
.content {
top: 60px;
}
body {
background-color: #022c28;
background-attachment: fixed;
background-position: center;
background-size: cover;
color: aqua;
font-family: "Montserrat", sans-serif;
font-optical-sizing: auto;
font-weight: 300;
font-style: normal;
font-size: 1rem; /* Base size */
}
h1 {
font-weight: 700;
font-size: 2.5rem;
padding-top: 30px;
padding-bottom: 10px;
}
h2 {
font-weight: 500;
font-size: 2rem;
padding-top: 15px;
padding-bottom: 10px;
}
h3 {
font-weight: 400;
font-size: 1.6rem;
padding-top: 10px;
padding-bottom: 10px;
}
p {
color: #fff;
font-size: 1.1rem;
}
li {
color: #fff;
font-size: 1rem;
text-align: center;
}
.btn-primary {
color: #d0eae9;
background-color: #014d4e;
border: 0px;
margin: 5px;
}
.btn-primary:hover {
color: #fff;
background-color: #086262;
border: 0px;
}
.row {
padding-bottom: 30px;
margin: 0 auto !important;
}
.searchInput::placeholder {
color: #d0eae9;
}
#myVideo {
position: fixed;
right: 0;
top: -100px;
min-width: 100%;
min-height: 100%;
transform: translateX(calc((100% - 100vw) / 2));
}
.img-fluid {
max-height: 50vh;
width: auto;
border-radius: 15px;
}
.sp-img {
box-shadow: 10px 10px 30px 0px rgba(0,0,0,0.75);
-webkit-box-shadow: 10px 10px 30px 0px rgba(0,0,0,0.75);
-moz-box-shadow: 10px 10px 30px 0px rgba(0,0,0,0.75);
}
.col {
align-items: center;
display: flex;
}
.form-select {
background-color: rgba(255, 255, 255, 0.2);
border-radius: 5px;
display: unset !important;
color: #fff;
}
.list-group-item,
.bg-light {
background-color: rgb(11 24 23 / 76%) !important;
backdrop-filter: blur(8px) !important;
}
.text-primary {
--bs-text-opacity: 1;
color: aqua;
}
/* 🔽 Mobile Responsiveness */
@media (max-width: 768px) {
h1 { font-size: 2rem; }
h2 { font-size: 1.6rem; }
h3 { font-size: 1.4rem; }
p, li { font-size: 1rem; }
.navbar-collapse .nav-link { font-size: 1.1rem; }
p {text-align: justify;}
}
@media (max-width: 480px) {
h1 { font-size: 1.6rem; }
h2 { font-size: 1.3rem; }
h3 { font-size: 1.1rem; }
p, li { font-size: 0.95rem; }
.navbar-collapse .nav-link { font-size: 1rem; }
p {text-align: justify;}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 864 KiB