better titlebar and fixes

This commit is contained in:
2026-01-09 20:35:45 +01:00
parent 1e85de8db6
commit e6c3320a7c
20 changed files with 472 additions and 333 deletions

View File

@@ -1,6 +1,5 @@
;(() => { ;(() => {
const token = localStorage.getItem("token") const token = localStorage.getItem("token")
if (!token && window.location.pathname !== "/") { if (!token && window.location.pathname !== "/") {
window.location.href = "/" window.location.href = "/"
} }
@@ -12,41 +11,61 @@ async function loadMeUI() {
try { try {
const res = await fetch("/api/me", { const res = await fetch("/api/me", {
headers: { headers: { Authorization: `Bearer ${token}` },
Authorization: `Bearer ${token}`,
},
}) })
if (!res.ok) return if (!res.ok) return
const user = await res.json() const user = await res.json()
const avatarUrl = user.avatar || "/public/assets/avatar.png"
const navUser = document.getElementById("nav-user") const navUser = document.getElementById("nav-user")
const navUsername = document.getElementById("nav-username") const navUsername = document.getElementById("nav-username")
const navAvatar = document.getElementById("nav-avatar") const navAvatar = document.getElementById("nav-avatar")
const dropdownAvatar = document.getElementById("dropdown-avatar") const dropdownAvatar = document.getElementById("dropdown-avatar")
if (!navUser || !navUsername || !navAvatar) return if (navUser && navUsername && navAvatar) {
navUser.style.display = "flex"
navUsername.textContent = user.username
navAvatar.src = avatarUrl
if (dropdownAvatar) dropdownAvatar.src = avatarUrl
}
navUser.style.display = "flex" const titlebarUserBox = document.getElementById("titlebar-user-box")
navUsername.textContent = user.username const titlebarUsername = document.getElementById("titlebar-username")
const titlebarAvatar = document.getElementById("titlebar-avatar")
const titlebarDropdownAvatar = document.getElementById("titlebar-dropdown-avatar")
const titlebarDropdownUsername = document.getElementById("titlebar-dropdown-username")
const avatarUrl = user.avatar || "/public/assets/avatar.png" if (titlebarUserBox && titlebarUsername && titlebarAvatar) {
navAvatar.src = avatarUrl titlebarUserBox.style.display = "flex"
if (dropdownAvatar) {
dropdownAvatar.src = avatarUrl titlebarUsername.textContent = user.username
titlebarAvatar.src = avatarUrl
if (titlebarDropdownAvatar) titlebarDropdownAvatar.src = avatarUrl
if (titlebarDropdownUsername) titlebarDropdownUsername.textContent = user.username
} }
setupDropdown() setupDropdown()
} catch (e) { } catch (e) {
console.error("Failed to load user UI:", e) console.error("Failed to load user UI:", e)
} }
} }
// Variable para saber si el modal ya fue cargado
let settingsModalLoaded = false; let settingsModalLoaded = false;
document.getElementById('nav-settings').addEventListener('click', openSettings) const navSettingsBtn = document.getElementById('nav-settings');
const titlebarSettingsBtn = document.getElementById('titlebar-settings');
if (navSettingsBtn) navSettingsBtn.addEventListener('click', openSettings);
if (titlebarSettingsBtn) titlebarSettingsBtn.addEventListener('click', (e) => {
e.stopPropagation();
openSettings();
document.getElementById("titlebar-dropdown")?.classList.remove("active");
});
async function openSettings() { async function openSettings() {
if (!settingsModalLoaded) { if (!settingsModalLoaded) {
@@ -55,63 +74,69 @@ async function openSettings() {
const html = await res.text() const html = await res.text()
document.body.insertAdjacentHTML('beforeend', html) document.body.insertAdjacentHTML('beforeend', html)
settingsModalLoaded = true; settingsModalLoaded = true;
// Esperar un momento para que el DOM se actualice
await new Promise(resolve => setTimeout(resolve, 50)); await new Promise(resolve => setTimeout(resolve, 50));
if (window.toggleSettingsModal) await window.toggleSettingsModal(false);
// Ahora cargar los settings
if (window.toggleSettingsModal) {
await window.toggleSettingsModal(false);
}
} catch (err) { } catch (err) {
console.error('Error loading settings modal:', err); console.error('Error loading settings modal:', err);
} }
} else { } else {
if (window.toggleSettingsModal) { if (window.toggleSettingsModal) await window.toggleSettingsModal(false);
await window.toggleSettingsModal(false);
}
}
}
function closeSettings() {
const modal = document.getElementById('settings-modal');
if (modal) {
modal.classList.add('hidden');
} }
} }
function setupDropdown() { function setupDropdown() {
const userAvatarBtn = document.querySelector(".user-avatar-btn") const userAvatarBtn = document.querySelector(".user-avatar-btn")
const navDropdown = document.getElementById("nav-dropdown") const navDropdown = document.getElementById("nav-dropdown")
const navLogout = document.getElementById("nav-logout") const navLogout = document.getElementById("nav-logout")
if (!userAvatarBtn || !navDropdown || !navLogout) return if (userAvatarBtn && navDropdown) {
userAvatarBtn.addEventListener("click", (e) => {
e.stopPropagation()
navDropdown.classList.toggle("active")
userAvatarBtn.addEventListener("click", (e) => { document.getElementById("titlebar-dropdown")?.classList.remove("active")
e.stopPropagation() })
navDropdown.classList.toggle("active") if (navLogout) {
}) navLogout.addEventListener("click", () => {
localStorage.removeItem("token")
window.location.href = "/"
})
}
}
const titlebarUserBox = document.getElementById("titlebar-user-box")
const titlebarDropdown = document.getElementById("titlebar-dropdown")
const titlebarLogout = document.getElementById("titlebar-logout")
if (titlebarUserBox && titlebarDropdown) {
titlebarUserBox.addEventListener("click", (e) => {
e.stopPropagation()
titlebarDropdown.classList.toggle("active")
document.getElementById("nav-dropdown")?.classList.remove("active")
})
if (titlebarLogout) {
titlebarLogout.addEventListener("click", (e) => {
e.stopPropagation()
localStorage.removeItem("token")
window.location.href = "/"
})
}
titlebarDropdown.addEventListener("click", (e) => {
e.stopPropagation()
})
}
document.addEventListener("click", (e) => { document.addEventListener("click", (e) => {
if (!navDropdown.contains(e.target)) { if (navDropdown && !navDropdown.contains(e.target)) {
navDropdown.classList.remove("active") navDropdown.classList.remove("active")
} }
}) if (titlebarDropdown && !titlebarDropdown.contains(e.target)) {
titlebarDropdown.classList.remove("active")
navDropdown.addEventListener("click", (e) => { }
e.stopPropagation()
})
navLogout.addEventListener("click", () => {
localStorage.removeItem("token")
window.location.href = "/"
})
const dropdownLinks = navDropdown.querySelectorAll("a.dropdown-item")
dropdownLinks.forEach((link) => {
link.addEventListener("click", () => {
navDropdown.classList.remove("active")
})
}) })
} }
@@ -159,7 +184,6 @@ searchWrapper.addEventListener('click', (e) => {
} }
}); });
// Cerrar el buscador si se hace clic fuera
document.addEventListener('click', (e) => { document.addEventListener('click', (e) => {
if (!searchWrapper.contains(e.target)) { if (!searchWrapper.contains(e.target)) {
searchWrapper.classList.remove('active-mobile'); searchWrapper.classList.remove('active-mobile');

View File

@@ -1,7 +1,7 @@
const urlParams = new URLSearchParams(window.location.search); const urlParams = new URLSearchParams(window.location.search);
const reader = document.getElementById('reader'); const reader = document.getElementById('reader');
const panel = document.getElementById('settings-panel'); const panel = document.getElementById('settings-panel');
const overlay = document.getElementById('overlay'); const overlay2 = document.getElementById('overlay');
const settingsBtn = document.getElementById('settings-btn'); const settingsBtn = document.getElementById('settings-btn');
const closePanel = document.getElementById('close-panel'); const closePanel = document.getElementById('close-panel');
const chapterLabel = document.getElementById('chapter-label'); const chapterLabel = document.getElementById('chapter-label');
@@ -665,14 +665,14 @@ document.getElementById('back-btn').addEventListener('click', () => {
// Panel de configuración // Panel de configuración
settingsBtn.addEventListener('click', () => { settingsBtn.addEventListener('click', () => {
panel.classList.add('open'); panel.classList.add('open');
overlay.classList.add('active'); overlay2.classList.add('active');
}); });
closePanel.addEventListener('click', closeSettings); closePanel.addEventListener('click', closeSettings);
overlay.addEventListener('click', closeSettings); overlay2.addEventListener('click', closeSettings);
function closeSettings() { function closeSettings() {
panel.classList.remove('open'); panel.classList.remove('open');
overlay.classList.remove('active'); overlay2.classList.remove('active');
} }
document.addEventListener('keydown', (e) => { document.addEventListener('keydown', (e) => {
if (e.key === 'Escape' && panel.classList.contains('open')) { if (e.key === 'Escape' && panel.classList.contains('open')) {

View File

@@ -1,5 +1,5 @@
const providerSelector = document.getElementById('provider-selector'); const providerSelector = document.getElementById('provider-selector');
const searchInput = document.getElementById('search-input'); const searchInput2 = document.getElementById('search-input');
const resultsContainer = document.getElementById('gallery-results'); const resultsContainer = document.getElementById('gallery-results');
let currentPage = 1; let currentPage = 1;
@@ -231,7 +231,7 @@ function showSkeletons(count, append = false) {
async function searchGallery(isLoadMore = false) { async function searchGallery(isLoadMore = false) {
if (isLoading) return; if (isLoading) return;
const query = searchInput.value.trim(); const query = searchInput2.value.trim();
const provider = providerSelector.value; const provider = providerSelector.value;
const page = isLoadMore ? currentPage + 1 : 1; const page = isLoadMore ? currentPage + 1 : 1;
@@ -351,22 +351,22 @@ async function loadExtensions() {
providerSelector.addEventListener('change', () => { providerSelector.addEventListener('change', () => {
if (providerSelector.value === 'favorites') { if (providerSelector.value === 'favorites') {
searchInput.placeholder = "Search in favorites..."; searchInput2.placeholder = "Search in favorites...";
} else { } else {
searchInput.placeholder = "Search in gallery..."; searchInput2.placeholder = "Search in gallery...";
} }
searchGallery(false); searchGallery(false);
}); });
let searchTimeout; let searchTimeout;
searchInput.addEventListener('input', () => { searchInput2.addEventListener('input', () => {
clearTimeout(searchTimeout); clearTimeout(searchTimeout);
searchTimeout = setTimeout(() => { searchTimeout = setTimeout(() => {
searchGallery(false); searchGallery(false);
}, 500); }, 500);
}); });
searchInput.addEventListener('keydown', e => { searchInput2.addEventListener('keydown', e => {
if (e.key === 'Enter') { if (e.key === 'Enter') {
clearTimeout(searchTimeout); clearTimeout(searchTimeout);
searchGallery(false); searchGallery(false);

View File

@@ -1,7 +1,18 @@
import { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify'; import {FastifyInstance, FastifyReply, FastifyRequest} from 'fastify';
import * as fs from 'fs'; import * as fs from 'fs';
import * as path from 'path'; import * as path from 'path';
let cachedTitlebar: string | null = null;
function getTitlebarHTML(): string {
if (!cachedTitlebar) {
const titlebarPath = path.join(__dirname, '..', '..', 'views', 'components', 'titlebar.html');
cachedTitlebar = fs.readFileSync(titlebarPath, 'utf-8');
}
return cachedTitlebar;
}
let cachedNavbar: string | null = null; let cachedNavbar: string | null = null;
function getNavbarHTML(activePage: string, showSearch: boolean = true): string { function getNavbarHTML(activePage: string, showSearch: boolean = true): string {
@@ -14,6 +25,7 @@ function getNavbarHTML(activePage: string, showSearch: boolean = true): string {
const pages = ['anime', 'books', 'gallery', 'schedule' , 'marketplace']; const pages = ['anime', 'books', 'gallery', 'schedule' , 'marketplace'];
pages.forEach(page => { pages.forEach(page => {
const regex = new RegExp(`(<button class="nav-button[^"]*)"\\s+data-page="${page}"`, 'g'); const regex = new RegExp(`(<button class="nav-button[^"]*)"\\s+data-page="${page}"`, 'g');
if (page === activePage) { if (page === activePage) {
navbar = navbar.replace(regex, `$1 active" data-page="${page}"`); navbar = navbar.replace(regex, `$1 active" data-page="${page}"`);
@@ -30,10 +42,15 @@ function getNavbarHTML(activePage: string, showSearch: boolean = true): string {
return navbar; return navbar;
} }
function injectNavbar(htmlContent: string, activePage: string, showSearch: boolean = true): string { function injectLayout(htmlContent: string, activePage: string | null = null, showSearch: boolean = true): string {
const navbar = getNavbarHTML(activePage, showSearch); let contentToInject = getTitlebarHTML();
return htmlContent.replace(/<body[^>]*>/, `$&\n${navbar}`); if (activePage !== null) {
const navbar = getNavbarHTML(activePage, showSearch);
contentToInject += `\n${navbar}`;
}
return htmlContent.replace(/<body[^>]*>/, `$&\n${contentToInject}`);
} }
async function viewsRoutes(fastify: FastifyInstance) { async function viewsRoutes(fastify: FastifyInstance) {
@@ -41,105 +58,116 @@ async function viewsRoutes(fastify: FastifyInstance) {
fastify.get('/', (req: FastifyRequest, reply: FastifyReply) => { fastify.get('/', (req: FastifyRequest, reply: FastifyReply) => {
const htmlPath = path.join(__dirname, '..', '..', 'views', 'users.html'); const htmlPath = path.join(__dirname, '..', '..', 'views', 'users.html');
const html = fs.readFileSync(htmlPath, 'utf-8'); const html = fs.readFileSync(htmlPath, 'utf-8');
reply.type('text/html').send(html);
const finalHtml = injectLayout(html, null);
reply.type('text/html').send(finalHtml);
}); });
fastify.get('/anime', (req: FastifyRequest, reply: FastifyReply) => { fastify.get('/anime', (req: FastifyRequest, reply: FastifyReply) => {
const htmlPath = path.join(__dirname, '..', '..', 'views', 'anime', 'animes.html'); const htmlPath = path.join(__dirname, '..', '..', 'views', 'anime', 'animes.html');
const html = fs.readFileSync(htmlPath, 'utf-8'); const html = fs.readFileSync(htmlPath, 'utf-8');
const htmlWithNavbar = injectNavbar(html, 'anime', true); const finalHtml = injectLayout(html, 'anime', true);
reply.type('text/html').send(htmlWithNavbar); reply.type('text/html').send(finalHtml);
}); });
fastify.get('/profile', (req: FastifyRequest, reply: FastifyReply) => { fastify.get('/profile', (req: FastifyRequest, reply: FastifyReply) => {
const htmlPath = path.join(__dirname, '..', '..', 'views', 'profile.html'); const htmlPath = path.join(__dirname, '..', '..', 'views', 'profile.html');
const html = fs.readFileSync(htmlPath, 'utf-8'); const html = fs.readFileSync(htmlPath, 'utf-8');
const htmlWithNavbar = injectNavbar(html, '', false); const finalHtml = injectLayout(html, '', false);
reply.type('text/html').send(htmlWithNavbar); reply.type('text/html').send(finalHtml);
}); });
fastify.get('/books', (req: FastifyRequest, reply: FastifyReply) => { fastify.get('/books', (req: FastifyRequest, reply: FastifyReply) => {
const htmlPath = path.join(__dirname, '..', '..', 'views', 'books', 'books.html'); const htmlPath = path.join(__dirname, '..', '..', 'views', 'books', 'books.html');
const html = fs.readFileSync(htmlPath, 'utf-8'); const html = fs.readFileSync(htmlPath, 'utf-8');
const htmlWithNavbar = injectNavbar(html, 'books', true); const finalHtml = injectLayout(html, 'books', true);
reply.type('text/html').send(htmlWithNavbar); reply.type('text/html').send(finalHtml);
}); });
fastify.get('/schedule', (req: FastifyRequest, reply: FastifyReply) => { fastify.get('/schedule', (req: FastifyRequest, reply: FastifyReply) => {
const htmlPath = path.join(__dirname, '..', '..', 'views', 'schedule.html'); const htmlPath = path.join(__dirname, '..', '..', 'views', 'schedule.html');
const html = fs.readFileSync(htmlPath, 'utf-8'); const html = fs.readFileSync(htmlPath, 'utf-8');
const htmlWithNavbar = injectNavbar(html, 'schedule', false); const finalHtml = injectLayout(html, 'schedule', false);
reply.type('text/html').send(htmlWithNavbar); reply.type('text/html').send(finalHtml);
}); });
fastify.get('/gallery', (req: FastifyRequest, reply: FastifyReply) => { fastify.get('/gallery', (req: FastifyRequest, reply: FastifyReply) => {
const htmlPath = path.join(__dirname, '..', '..', 'views', 'gallery', 'gallery.html'); const htmlPath = path.join(__dirname, '..', '..', 'views', 'gallery', 'gallery.html');
const html = fs.readFileSync(htmlPath, 'utf-8'); const html = fs.readFileSync(htmlPath, 'utf-8');
const htmlWithNavbar = injectNavbar(html, 'gallery', true); const finalHtml = injectLayout(html, 'gallery', true);
reply.type('text/html').send(htmlWithNavbar); reply.type('text/html').send(finalHtml);
}); });
fastify.get('/marketplace', (req: FastifyRequest, reply: FastifyReply) => { fastify.get('/marketplace', (req: FastifyRequest, reply: FastifyReply) => {
const htmlPath = path.join(__dirname, '..', '..', 'views', 'marketplace.html'); const htmlPath = path.join(__dirname, '..', '..', 'views', 'marketplace.html');
const html = fs.readFileSync(htmlPath, 'utf-8'); const html = fs.readFileSync(htmlPath, 'utf-8');
const htmlWithNavbar = injectNavbar(html, 'marketplace', false); const finalHtml = injectLayout(html, 'marketplace', false);
reply.type('text/html').send(htmlWithNavbar); reply.type('text/html').send(finalHtml);
}); });
fastify.get('/gallery/:extension/*', (req: FastifyRequest, reply: FastifyReply) => { fastify.get('/gallery/:extension/*', (req: FastifyRequest, reply: FastifyReply) => {
const htmlPath = path.join(__dirname, '..', '..', 'views', 'gallery', 'image.html'); const htmlPath = path.join(__dirname, '..', '..', 'views', 'gallery', 'image.html');
const html = fs.readFileSync(htmlPath, 'utf-8'); const html = fs.readFileSync(htmlPath, 'utf-8');
const htmlWithNavbar = injectNavbar(html, 'gallery', true); const finalHtml = injectLayout(html, 'gallery', true);
reply.type('text/html').send(htmlWithNavbar); reply.type('text/html').send(finalHtml);
}); });
fastify.get('/gallery/favorites/*', (req: FastifyRequest, reply: FastifyReply) => { fastify.get('/gallery/favorites/*', (req: FastifyRequest, reply: FastifyReply) => {
const htmlPath = path.join(__dirname, '..', '..', 'views', 'gallery', 'image.html'); const htmlPath = path.join(__dirname, '..', '..', 'views', 'gallery', 'image.html');
const html = fs.readFileSync(htmlPath, 'utf-8'); const html = fs.readFileSync(htmlPath, 'utf-8');
const htmlWithNavbar = injectNavbar(html, 'gallery', true); const finalHtml = injectLayout(html, 'gallery', true);
reply.type('text/html').send(htmlWithNavbar); reply.type('text/html').send(finalHtml);
}); });
fastify.get('/anime/:id', (req: FastifyRequest, reply: FastifyReply) => { fastify.get('/anime/:id', (req: FastifyRequest, reply: FastifyReply) => {
const htmlPath = path.join(__dirname, '..', '..', 'views', 'anime', 'anime.html'); const htmlPath = path.join(__dirname, '..', '..', 'views', 'anime', 'anime.html');
const html = fs.readFileSync(htmlPath, 'utf-8'); const html = fs.readFileSync(htmlPath, 'utf-8');
reply.type('text/html').send(html);
const finalHtml = injectLayout(html, null);
reply.type('text/html').send(finalHtml);
}); });
fastify.get('/anime/:extension/*', (req: FastifyRequest, reply: FastifyReply) => { fastify.get('/anime/:extension/*', (req: FastifyRequest, reply: FastifyReply) => {
const htmlPath = path.join(__dirname, '..', '..', 'views', 'anime', 'anime.html'); const htmlPath = path.join(__dirname, '..', '..', 'views', 'anime', 'anime.html');
const html = fs.readFileSync(htmlPath, 'utf-8'); const html = fs.readFileSync(htmlPath, 'utf-8');
reply.type('text/html').send(html); const finalHtml = injectLayout(html, null);
reply.type('text/html').send(finalHtml);
}); });
fastify.get('/book/:id', (req: FastifyRequest, reply: FastifyReply) => { fastify.get('/book/:id', (req: FastifyRequest, reply: FastifyReply) => {
const htmlPath = path.join(__dirname, '..', '..', 'views', 'books', 'book.html'); const htmlPath = path.join(__dirname, '..', '..', 'views', 'books', 'book.html');
const html = fs.readFileSync(htmlPath, 'utf-8'); const html = fs.readFileSync(htmlPath, 'utf-8');
reply.type('text/html').send(html); const finalHtml = injectLayout(html, null);
reply.type('text/html').send(finalHtml);
}); });
fastify.get('/book/:extension/*', (req: FastifyRequest, reply: FastifyReply) => { fastify.get('/book/:extension/*', (req: FastifyRequest, reply: FastifyReply) => {
const htmlPath = path.join(__dirname, '..', '..', 'views', 'books', 'book.html'); const htmlPath = path.join(__dirname, '..', '..', 'views', 'books', 'book.html');
const html = fs.readFileSync(htmlPath, 'utf-8'); const html = fs.readFileSync(htmlPath, 'utf-8');
reply.type('text/html').send(html); const finalHtml = injectLayout(html, null);
reply.type('text/html').send(finalHtml);
}); });
fastify.get('/read/:provider/:chapter/*', (req: FastifyRequest, reply: FastifyReply) => { fastify.get('/read/:provider/:chapter/*', (req: FastifyRequest, reply: FastifyReply) => {
const htmlPath = path.join(__dirname, '..', '..', 'views', 'books', 'read.html'); const htmlPath = path.join(__dirname, '..', '..', 'views', 'books', 'read.html');
const html = fs.readFileSync(htmlPath, 'utf-8'); const html = fs.readFileSync(htmlPath, 'utf-8');
reply.type('text/html').send(html);
const finalHtml = injectLayout(html, null);
reply.type('text/html').send(finalHtml);
}); });
fastify.get('/room', (req: FastifyRequest, reply: FastifyReply) => { fastify.get('/room', (req: FastifyRequest, reply: FastifyReply) => {
const htmlPath = path.join(__dirname, '..', '..', 'views', 'room.html'); const htmlPath = path.join(__dirname, '..', '..', 'views', 'room.html');
const html = fs.readFileSync(htmlPath, 'utf-8'); const html = fs.readFileSync(htmlPath, 'utf-8');
reply.type('text/html').send(html); const finalHtml = injectLayout(html, null);
reply.type('text/html').send(finalHtml);
}); });
fastify.setNotFoundHandler((req, reply) => { fastify.setNotFoundHandler((req, reply) => {
const htmlPath = path.join(__dirname, '..', '..', 'views', '404.html'); const htmlPath = path.join(__dirname, '..', '..', 'views', '404.html');
const html = fs.readFileSync(htmlPath, 'utf-8'); const html = fs.readFileSync(htmlPath, 'utf-8');
reply.code(404).type('text/html').send(html); const finalHtml = injectLayout(html, null);
reply.code(404).type('text/html').send(finalHtml);
}); });
} }

View File

@@ -45,18 +45,6 @@
</head> </head>
<body> <body>
<div id="titlebar">
<div class="title-left">
<img class="app-icon" src="/public/assets/waifuboards.ico" alt=""/>
<span class="app-title">WaifuBoard</span>
</div>
<div class="title-right">
<button class="min"></button>
<button class="max">🗖</button>
<button class="close"></button>
</div>
</div>
<nav class="navbar" id="navbar"> <nav class="navbar" id="navbar">
<a href="#" class="nav-brand"> <a href="#" class="nav-brand">
<div class="brand-icon"> <div class="brand-icon">

View File

@@ -15,21 +15,11 @@
<link rel="stylesheet" href="/views/css/anime/anime.css" /> <link rel="stylesheet" href="/views/css/anime/anime.css" />
<link rel="stylesheet" href="/views/css/anime/player.css" /> <link rel="stylesheet" href="/views/css/anime/player.css" />
<link rel="stylesheet" href="/views/css/components/match-modal.css"> <link rel="stylesheet" href="/views/css/components/match-modal.css">
<link rel="stylesheet" href="/views/css/components/titlebar.css">
<script src="/src/scripts/titlebar.js"></script> <script src="/src/scripts/titlebar.js"></script>
<link rel="stylesheet" href="/views/css/components/titlebar.css">
</head> </head>
<body> <body>
<div id="titlebar">
<div class="title-left">
<img class="app-icon" src="/public/assets/waifuboards.ico" alt=""/>
<span class="app-title">WaifuBoard</span>
</div>
<div class="title-right">
<button class="min"></button>
<button class="max">🗖</button>
<button class="close"></button>
</div>
</div>
<a href="/anime" class="back-btn"> <a href="/anime" class="back-btn">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"> <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
<path d="M19 12H5"/><path d="M12 19l-7-7 7-7"/> <path d="M19 12H5"/><path d="M12 19l-7-7 7-7"/>

View File

@@ -17,18 +17,6 @@
<script src="/src/scripts/titlebar.js"></script> <script src="/src/scripts/titlebar.js"></script>
</head> </head>
<body> <body>
<div id="titlebar">
<div class="title-left">
<img class="app-icon" src="/public/assets/waifuboards.ico" alt=""/>
<span class="app-title">WaifuBoard</span>
</div>
<div class="title-right">
<button class="min"></button>
<button class="max">🗖</button>
<button class="close"></button>
</div>
</div>
<div class="hero-wrapper"> <div class="hero-wrapper">
<div class="hero-background"> <div class="hero-background">
<img id="hero-bg-media" alt=""> <img id="hero-bg-media" alt="">

View File

@@ -15,16 +15,6 @@
<script src="/src/scripts/titlebar.js"></script> <script src="/src/scripts/titlebar.js"></script>
</head> </head>
<body> <body>
<div id="titlebar"> <div class="title-left">
<img class="app-icon" src="/public/assets/waifuboards.ico" alt=""/>
<span class="app-title">WaifuBoard</span>
</div>
<div class="title-right">
<button class="min"></button>
<button class="max">🗖</button>
<button class="close"></button>
</div>
</div>
<a href="/books" class="back-btn"> <a href="/books" class="back-btn">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"> <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
<path d="M19 12H5"/><path d="M12 19l-7-7 7-7"/> <path d="M19 12H5"/><path d="M12 19l-7-7 7-7"/>

View File

@@ -16,17 +16,6 @@
<script src="/src/scripts/titlebar.js"></script> <script src="/src/scripts/titlebar.js"></script>
</head> </head>
<body> <body>
<div id="titlebar"> <div class="title-left">
<img class="app-icon" src="/public/assets/waifuboards.ico" alt=""/>
<span class="app-title">WaifuBoard</span>
</div>
<div class="title-right">
<button class="min"></button>
<button class="max">🗖</button>
<button class="close"></button>
</div>
</div>
<div class="hero-wrapper"> <div class="hero-wrapper">
<div class="hero-background"> <div class="hero-background">
<img id="hero-bg-media" src="" alt=""> <img id="hero-bg-media" src="" alt="">

View File

@@ -12,17 +12,6 @@
<script src="/src/scripts/titlebar.js"></script> <script src="/src/scripts/titlebar.js"></script>
</head> </head>
<body> <body>
<div id="titlebar"> <div class="title-left">
<img class="app-icon" src="/public/assets/waifuboards.ico" alt=""/>
<span class="app-title">WaifuBoard</span>
</div>
<div class="title-right">
<button class="min"></button>
<button class="max">🗖</button>
<button class="close"></button>
</div>
</div>
<header class="top-bar"> <header class="top-bar">
<button id="back-btn" class="glass-btn"> <button id="back-btn" class="glass-btn">
← Back ← Back

View File

@@ -0,0 +1,57 @@
<div id="titlebar">
<div class="title-left">
<div class="app-icon">
<img src="/public/assets/waifuboards.ico" alt="WB"/>
</div>
<span class="app-title">WaifuBoard</span>
</div>
<div class="title-drag-area"></div>
<div class="title-right">
<div class="user-box" id="titlebar-user-box" style="display: none;">
<img id="titlebar-avatar" src="/public/assets/waifuboards.ico" alt="User" />
<span id="titlebar-username">Guest</span>
<div class="nav-dropdown" id="titlebar-dropdown">
<div class="dropdown-header">
<img id="titlebar-dropdown-avatar" src="/public/assets/waifuboards.ico" class="dropdown-avatar">
<div class="dropdown-user-info">
<div class="dropdown-username" id="titlebar-dropdown-username">Guest</div>
<div class="dropdown-status">Online</div>
</div>
</div>
<a href="/profile" class="dropdown-item">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>
<span>Profile</span>
</a>
<button class="dropdown-item" id="titlebar-settings">
<svg width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06A1.65 1.65 0 0 0 15 19.4a1.65 1.65 0 0 0-1 .6 1.65 1.65 0 0 0-.33 1.82V22a2 2 0 1 1-4 0v-.18a1.65 1.65 0 0 0-.33-1.82 1.65 1.65 0 0 0-1-.6 1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.6 15a1.65 1.65 0 0 0-.6-1 1.65 1.65 0 0 0-1.82-.33H2a2 2 0 1 1 0-4h.18a1.65 1.65 0 0 0 1.82-.33 1.65 1.65 0 0 0 .6-1 1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.6c.37 0 .72-.14 1-.6A1.65 1.65 0 0 0 10.33 2.18V2a2 2 0 1 1 4 0v.18a1.65 1.65 0 0 0 .33 1.82c.28.46.63.6 1 .6a1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9c0 .37.14.72.6 1 .46.28.6.63.6 1z"/></svg>
<span>Settings</span>
</button>
<div class="dropdown-divider"></div>
<button class="dropdown-item logout-item" id="titlebar-logout">
<svg width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/></svg>
<span>Logout</span>
</button>
</div>
</div>
<div class="window-controls">
<button class="control-btn min" title="Minimize">
<svg width="11" height="1" viewBox="0 0 11 1"><path d="M0 0h11v1H0z" fill="currentColor"/></svg>
</button>
<button class="control-btn max" title="Maximize">
<svg width="10" height="10" viewBox="0 0 10 10"><path d="M1 1v8h8V1H1zm1 1h6v6H2V2z" fill="currentColor"/></svg>
</button>
<button class="control-btn close" title="Close">
<svg width="11" height="11" viewBox="0 0 11 11"><path d="M5.5 4.793L1.854 1.146.646 2.354 4.293 6 .646 9.646l1.208 1.208L5.5 7.207l3.646 3.647 1.208-1.208L6.707 6l3.647-3.646-1.208-1.208L5.5 4.793z" fill="currentColor"/></svg>
</button>
</div>
</div>
</div>

View File

@@ -1,5 +1,7 @@
:root { :root {
--titlebar-height: 40px; --titlebar-height: 40px;
--primary-glass: rgba(9, 9, 11, 0.95);
--border-color: rgba(139, 92, 246, 0.2);
} }
* { * {
@@ -34,7 +36,7 @@ html.electron .panel-content {
margin-top: 2rem; margin-top: 2rem;
} }
html.electron .calendar-wrapper{ html.electron .calendar-wrapper {
margin-top: 4rem; margin-top: 4rem;
} }
@@ -47,42 +49,46 @@ html.electron .back-btn {
} }
#titlebar { #titlebar {
display: none; width: calc(100vw - 12px); display: none;
height: var(--titlebar-height); height: var(--titlebar-height);
background: rgba(9, 9, 11, 0.95); background: var(--primary-glass);
color: white; color: white;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
padding: 0 12px; padding-left: 12px;
padding-right: 0;
-webkit-app-region: drag; -webkit-app-region: drag;
user-select: none; user-select: none;
font-family: "Inter", system-ui, sans-serif;
border-bottom: 1px solid rgba(139, 92, 246, 0.2);
position: fixed; position: fixed;
top: 0; top: 0;
left: 0; left: 0;
width: 100vw;
z-index: 999999; z-index: 999999;
font-family: "Inter", system-ui, sans-serif;
border-bottom: 1px solid var(--border-color);
backdrop-filter: blur(12px); backdrop-filter: blur(12px);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3); box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
} }
.title-left { .title-left {
display: flex; display: flex;
align-items: center !important; align-items: center;
gap: 10px; gap: 10px;
pointer-events: none;
} }
#titlebar .app-icon { #titlebar .app-icon {
width: 24px; width: 22px;
height: 24px; height: 22px;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
border-radius: 6px; border-radius: 5px;
background: rgba(139, 92, 246, 0.15); background: rgba(139, 92, 246, 0.1);
border: 1px solid rgba(139, 92, 246, 0.3); border: 1px solid rgba(139, 92, 246, 0.25);
padding: 3px; padding: 2px;
} }
#titlebar .app-icon img { #titlebar .app-icon img {
@@ -95,85 +101,19 @@ html.electron .back-btn {
font-size: 13px; font-size: 13px;
font-weight: 600; font-weight: 600;
color: rgba(255, 255, 255, 0.9); color: rgba(255, 255, 255, 0.9);
letter-spacing: -0.2px; letter-spacing: -0.1px;
}
.title-drag-area {
flex-grow: 1;
height: 100%;
} }
.title-right { .title-right {
display: flex; display: flex;
height: 100%; height: 100%;
gap: 1px;
}
.title-right button {
-webkit-app-region: no-drag;
border: none;
background: transparent;
color: rgba(255, 255, 255, 0.7);
width: 46px;
height: 100%;
cursor: pointer;
font-size: 16px;
display: flex;
align-items: center; align-items: center;
justify-content: center; -webkit-app-region: no-drag;
position: relative;
}
.title-right button svg {
width: 16px;
height: 16px;
transition: transform 0.2s;
}
.title-right button:hover {
color: white;
}
.title-right button:active {
transform: scale(0.95);
}
.title-right .min:hover {
background: rgba(139, 92, 246, 0.2);
}
.title-right .max:hover {
background: rgba(34, 197, 94, 0.2);
}
.title-right .close:hover {
background: #e81123;
color: white;
}
.title-right button:hover svg {
transform: scale(1.1);
}
html.electron::-webkit-scrollbar {
width: 12px;
position: absolute;
}
html.electron::-webkit-scrollbar-track {
background: #09090b;
margin-top: var(--titlebar-height);
}
html.electron::-webkit-scrollbar-thumb {
background: rgba(139, 92, 246, 0.3);
border-radius: 6px;
border: 2px solid #09090b;
}
html.electron::-webkit-scrollbar-thumb:hover {
background: rgba(139, 92, 246, 0.5);
}
body {
margin: 0;
padding: 0;
overflow-x: hidden;
} }
.user-box { .user-box {
@@ -181,18 +121,104 @@ body {
align-items: center; align-items: center;
gap: 8px; gap: 8px;
margin-right: 12px; margin-right: 12px;
padding: 4px 8px;
border-radius: 4px;
transition: background 0.2s;
cursor: default;
}
.user-box:hover {
background: rgba(255, 255, 255, 0.05);
} }
.user-box img { .user-box img {
width: 26px; width: 24px;
height: 26px; height: 24px;
border-radius: 50%; border-radius: 50%;
object-fit: cover; object-fit: cover;
border: 1px solid rgba(139, 92, 246, 0.3);
} }
.user-box span { .user-box span {
font-size: 13px; font-size: 12px;
font-weight: 500;
opacity: 0.9; opacity: 0.9;
color: #e4e4e7;
}
.window-controls {
display: flex;
height: 100%;
}
.control-btn {
border: none;
background: transparent;
color: #a1a1aa;
width: 46px;
height: 100%;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s;
outline: none;
}
.control-btn:hover {
color: white;
background: rgba(255, 255, 255, 0.05);
}
.control-btn.min:hover {
background: rgba(255, 255, 255, 0.1);
}
.control-btn.max:hover {
background: rgba(255, 255, 255, 0.1);
}
.control-btn.close:hover {
background: #e81123;
color: white;
}
.control-btn:active {
background: rgba(255, 255, 255, 0.15);
}
.control-btn.close:active {
background: #bf0f1d;
}
html.electron::-webkit-scrollbar {
width: 12px;
position: absolute;
z-index: 0;
}
html.electron::-webkit-scrollbar-track {
background: #09090b;
margin-top: var(--titlebar-height);
border-left: 1px solid rgba(255, 255, 255, 0.05);
}
html.electron::-webkit-scrollbar-thumb {
background: rgba(139, 92, 246, 0.3);
border-radius: 6px;
border: 2px solid #09090b;
background-clip: content-box;
}
html.electron::-webkit-scrollbar-thumb:hover {
background: rgba(139, 92, 246, 0.5);
border: 2px solid #09090b;
}
body {
margin: 0;
padding: 0;
overflow-x: hidden;
} }
.hidden { .hidden {
@@ -208,3 +234,151 @@ html.electron #room-view {
html.electron #room-view .room-layout { html.electron #room-view .room-layout {
height: 100%; height: 100%;
} }
#titlebar-user-box {
position: relative;
z-index: 1000000;
}
#titlebar-dropdown {
position: absolute;
top: calc(100% + 8px);
right: 0;
width: 240px;
background: rgba(18, 18, 21, 0.98);
backdrop-filter: blur(20px);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 12px;
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.6);
display: none;
flex-direction: column;
overflow: hidden;
z-index: 1000001;
transform-origin: top right;
animation: titlebarDropdownSlide 0.2s cubic-bezier(0.16, 1, 0.3, 1);
}
#titlebar-dropdown.active {
display: flex;
}
@keyframes titlebarDropdownSlide {
from { opacity: 0; transform: scale(0.95) translateY(-5px); }
to { opacity: 1; transform: scale(1) translateY(0); }
}
#titlebar-dropdown .dropdown-header {
display: flex;
align-items: center;
gap: 12px;
padding: 16px;
background: rgba(139, 92, 246, 0.05);
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
}
#titlebar-dropdown .dropdown-avatar {
width: 40px;
height: 40px;
border-radius: 50%;
border: 2px solid #8b5cf6;
}
#titlebar-dropdown .dropdown-user-info {
display: flex;
flex-direction: column;
}
#titlebar-dropdown .dropdown-username {
font-size: 14px;
font-weight: 600;
color: white;
}
#titlebar-dropdown .dropdown-status {
font-size: 11px;
color: #22c55e;
font-weight: 500;
}
#titlebar-dropdown .dropdown-item {
display: flex;
align-items: center;
gap: 10px;
padding: 10px 16px;
color: #a1a1aa;
text-decoration: none;
font-size: 13px;
transition: all 0.2s;
background: transparent;
border: none;
width: 100%;
cursor: pointer;
font-family: inherit;
text-align: left;
}
#titlebar-dropdown .dropdown-item:hover {
background: rgba(255, 255, 255, 0.05);
color: white;
padding-left: 20px;
}
#titlebar-dropdown .dropdown-item svg {
color: inherit;
transition: transform 0.2s;
}
#titlebar-dropdown .dropdown-item:hover svg {
color: #8b5cf6;
}
#titlebar-dropdown .dropdown-divider {
height: 1px;
background: rgba(255, 255, 255, 0.05);
margin: 4px 0;
}
#titlebar-dropdown .logout-item {
color: #ef4444;
}
#titlebar-dropdown .logout-item:hover {
background: rgba(239, 68, 68, 0.1);
color: #f87171;
}
#titlebar-dropdown .logout-item:hover svg {
color: #f87171;
}
html.electron::-webkit-scrollbar {
width: 12px;
}
html.electron::-webkit-scrollbar-track {
margin-top: var(--titlebar-height);
}
#titlebar .control-btn {
width: 46px !important;
height: 100% !important;
color: #a1a1aa;
transition: all 0.2s;
}
#titlebar .control-btn svg {
width: auto !important;
height: auto !important;
max-width: 14px;
max-height: 14px;
pointer-events: none;
}
#titlebar .app-icon img {
width: 100%;
height: 100%;
object-fit: contain;
max-width: none;
}

View File

@@ -10,23 +10,11 @@
<link rel="icon" href="/public/assets/waifuboards.ico" type="image/x-icon"> <link rel="icon" href="/public/assets/waifuboards.ico" type="image/x-icon">
<link rel="stylesheet" href="/views/css/components/updateNotifier.css"> <link rel="stylesheet" href="/views/css/components/updateNotifier.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/css/all.min.css"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/css/all.min.css">
<script src="/src/scripts/room-modal.js"></script>
<script src="https://unpkg.com/masonry-layout@4/dist/masonry.pkgd.min.js" async></script> <script src="https://unpkg.com/masonry-layout@4/dist/masonry.pkgd.min.js" async></script>
<link rel="stylesheet" href="/views/css/components/titlebar.css"> <link rel="stylesheet" href="/views/css/components/titlebar.css">
<script src="/src/scripts/titlebar.js"></script> <script src="/src/scripts/titlebar.js"></script>
</head> </head>
<body> <body>
<div id="titlebar"> <div class="title-left">
<img class="app-icon" src="/public/assets/waifuboards.ico" alt=""/>
<span class="app-title">WaifuBoard</span>
</div>
<div class="title-right">
<button class="min"></button>
<button class="max">🗖</button>
<button class="close"></button>
</div>
</div>
<main class="gallery-main"> <main class="gallery-main">
<div class="gallery-hero-placeholder"></div> <div class="gallery-hero-placeholder"></div>

View File

@@ -16,17 +16,6 @@
<script src="/src/scripts/titlebar.js"></script> <script src="/src/scripts/titlebar.js"></script>
</head> </head>
<body> <body>
<div id="titlebar"> <div class="title-left">
<img class="app-icon" src="/public/assets/waifuboards.ico" alt=""/>
<span class="app-title">WaifuBoard</span>
</div>
<div class="title-right">
<button class="min"></button>
<button class="max">🗖</button>
<button class="close"></button>
</div>
</div>
<a href="/gallery" class="back-btn"> <a href="/gallery" class="back-btn">
<svg width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" viewBox="0 0 24 24"><path d="M15 19l-7-7 7-7"/></svg> <svg width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" viewBox="0 0 24 24"><path d="M15 19l-7-7 7-7"/></svg>
Back to Gallery Back to Gallery

View File

@@ -14,18 +14,6 @@
<script src="/src/scripts/titlebar.js"></script> <script src="/src/scripts/titlebar.js"></script>
</head> </head>
<body> <body>
<div id="titlebar">
<div class="title-left">
<img class="app-icon" src="/public/assets/waifuboards.ico" alt=""/>
<span class="app-title">WaifuBoard</span>
</div>
<div class="title-right">
<button class="min"></button>
<button class="max">🗖</button>
<button class="close"></button>
</div>
</div>
<div class="hero-spacer"></div> <div class="hero-spacer"></div>
<main> <main>

View File

@@ -15,17 +15,6 @@
<script src="/src/scripts/titlebar.js"></script> <script src="/src/scripts/titlebar.js"></script>
</head> </head>
<body> <body>
<div id="titlebar">
<div class="title-left">
<img class="app-icon" src="/public/assets/waifuboards.ico" alt=""/>
<span class="app-title">WaifuBoard</span>
</div>
<div class="title-right">
<button class="min"></button>
<button class="max">🗖</button>
<button class="close"></button>
</div>
</div>
<div class="main-wrapper"> <div class="main-wrapper">
<section class="profile-header"> <section class="profile-header">

View File

@@ -18,17 +18,6 @@
<script src="/src/scripts/titlebar.js"></script> <script src="/src/scripts/titlebar.js"></script>
</head> </head>
<body> <body>
<div id="titlebar">
<div class="title-left">
<img class="app-icon" src="/public/assets/waifuboards.ico" alt=""/>
<span class="app-title">WaifuBoard</span>
</div>
<div class="title-right">
<button class="min"></button>
<button class="max">🗖</button>
<button class="close"></button>
</div>
</div>
<div id="room-view"> <div id="room-view">
<div class="room-layout" id="room-layout"> <div class="room-layout" id="room-layout">
<div class="video-area"> <div class="video-area">

View File

@@ -17,17 +17,6 @@
<script src="/src/scripts/titlebar.js"></script> <script src="/src/scripts/titlebar.js"></script>
</head> </head>
<body> <body>
<div id="titlebar"> <div class="title-left">
<img class="app-icon" src="/public/assets/waifuboards.ico" alt=""/>
<span class="app-title">WaifuBoard</span>
</div>
<div class="title-right">
<button class="min"></button>
<button class="max">🗖</button>
<button class="close"></button>
</div>
</div>
<div class="ambient-bg" id="ambientBg"></div> <div class="ambient-bg" id="ambientBg"></div>
<div class="calendar-wrapper"> <div class="calendar-wrapper">

View File

@@ -11,16 +11,6 @@
<script src="/src/scripts/titlebar.js"></script> <script src="/src/scripts/titlebar.js"></script>
</head> </head>
<body> <body>
<div id="titlebar"> <div class="title-left">
<img class="app-icon" src="/public/assets/waifuboards.ico" alt=""/>
<span class="app-title">WaifuBoard</span>
</div>
<div class="title-right">
<button class="min"></button>
<button class="max">🗖</button>
<button class="close"></button>
</div>
</div>
<div class="page-wrapper"> <div class="page-wrapper">
<div class="background-gradient"></div> <div class="background-gradient"></div>

View File

@@ -1,5 +1,5 @@
const providerSelector = document.getElementById('provider-selector'); const providerSelector = document.getElementById('provider-selector');
const searchInput = document.getElementById('search-input'); const searchInput2 = document.getElementById('search-input');
const resultsContainer = document.getElementById('gallery-results'); const resultsContainer = document.getElementById('gallery-results');
let currentPage = 1; let currentPage = 1;
@@ -231,7 +231,7 @@ function showSkeletons(count, append = false) {
async function searchGallery(isLoadMore = false) { async function searchGallery(isLoadMore = false) {
if (isLoading) return; if (isLoading) return;
const query = searchInput.value.trim(); const query = searchInput2.value.trim();
const provider = providerSelector.value; const provider = providerSelector.value;
const page = isLoadMore ? currentPage + 1 : 1; const page = isLoadMore ? currentPage + 1 : 1;
@@ -351,22 +351,22 @@ async function loadExtensions() {
providerSelector.addEventListener('change', () => { providerSelector.addEventListener('change', () => {
if (providerSelector.value === 'favorites') { if (providerSelector.value === 'favorites') {
searchInput.placeholder = "Search in favorites..."; searchInput2.placeholder = "Search in favorites...";
} else { } else {
searchInput.placeholder = "Search in gallery..."; searchInput2.placeholder = "Search in gallery...";
} }
searchGallery(false); searchGallery(false);
}); });
let searchTimeout; let searchTimeout;
searchInput.addEventListener('input', () => { searchInput2.addEventListener('input', () => {
clearTimeout(searchTimeout); clearTimeout(searchTimeout);
searchTimeout = setTimeout(() => { searchTimeout = setTimeout(() => {
searchGallery(false); searchGallery(false);
}, 500); }, 500);
}); });
searchInput.addEventListener('keydown', e => { searchInput2.addEventListener('keydown', e => {
if (e.key === 'Enter') { if (e.key === 'Enter') {
clearTimeout(searchTimeout); clearTimeout(searchTimeout);
searchGallery(false); searchGallery(false);