Updated headless browser to support dynamic sites

Removed tabs and moved over to pages
Updated the rendering system
Fixed multiple pages not loading on scroll and re-rending or not rendering anything or just page 1.
Fixed the search bar not taking in spaces for each query
Updated how extensions are made
Updated how extensions are loaded
This commit is contained in:
2025-11-21 11:48:07 -05:00
parent c3de5af1f2
commit 04f37218de
11 changed files with 567 additions and 295 deletions

94
views/favorites.html Normal file
View File

@@ -0,0 +1,94 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' https: data:; connect-src 'self' https:;" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Waifu Board - Favorites</title>
<link rel="stylesheet" href="styles/home.css">
</head>
<body>
<aside class="sidebar">
<br>
<nav style="display: flex; flex-direction: column; gap: 0.5rem;">
<a href="index.html" class="nav-button" title="Image Boards">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="3" y="3" width="7" height="7"></rect>
<rect x="14" y="3" width="7" height="7"></rect>
<rect x="14" y="14" width="7" height="7"></rect>
<rect x="3" y="14" width="7" height="7"></rect>
</svg>
<span>Image Boards</span>
</a>
<a href="favorites.html" class="nav-button active" title="Favorites">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"></path>
</svg>
<span>Favorites</span>
</a>
</nav>
<nav style="display: flex; flex-direction: column; gap: 0.5rem; margin-top: auto;">
<a href="settings.html" class="nav-button" title="Settings">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="12" cy="12" r="3"></circle>
<path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path>
</svg>
<span>Settings</span>
</a>
</nav>
</aside>
<div class="main-wrapper">
<header class="top-header">
<div style="position: relative;">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="position: absolute; left: 15px; top: 50%; transform: translateY(-50%); pointer-events: none; color: var(--text-tertiary);">
<circle cx="11" cy="11" r="8"></circle>
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
</svg>
<input type="text" id="search-input" placeholder="Search across sources..." style="padding-left: 2.5rem;" />
</div>
<button id="search-button" class="hidden"></button>
<button id="search-icon-button" class="hidden"></button>
<button id="search-close-button" class="hidden"></button>
<div id="header-context" class="hidden"></div>
<h1 id="page-title" class="hidden">Favorites</h1>
</header>
<div class="content-view">
<div id="favorites-page" class="page">
<div style="margin-bottom: 2rem;">
<h2 style="margin-bottom: 0.5rem;">Your Favorites</h2>
<p style="color: var(--text-secondary);">Your personally curated collection.</p>
</div>
<main id="favorites-gallery" class="gallery-masonry"></main>
</div>
</div>
</div>
<div id="tag-info-modal" class="hidden">
<div>
<button id="tag-info-close-button">&times;</button>
<h3 style="margin-top:0; margin-bottom: 1rem;">Tags</h3>
<div id="tag-info-content" class="tag-cloud"></div>
</div>
</div>
<div id="message-bar" class="toast hidden">Message</div>
<script type="module" src="../src/renderer.js"></script>
<script type="module" src="../scripts/main.js"></script>
<script src="../src/updateNotification.js"></script>
<script>
const searchInput = document.getElementById('search-input');
const searchBtn = document.getElementById('search-button');
if(searchInput && searchBtn) {
searchInput.addEventListener('keydown', (e) => {
if (e.key === 'Enter') searchBtn.click();
});
}
</script>
</body>
</html>

View File

@@ -1,86 +1,68 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' https: data:; connect-src 'self' https:;" />
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' https: data:; connect-src 'self' https:;" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Waifu Board</title>
<link rel="stylesheet" href="./styles/home.css">
<link rel="stylesheet" href="styles/home.css">
</head>
<body>
<aside class="sidebar">
<br>
<nav style="display: flex; flex-direction: column; gap: 0.5rem;">
<button id="browse-button" class="nav-button active" title="Browse">
<a href="index.html" class="nav-button active" title="Image Boards">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="3" y="3" width="7" height="7"></rect>
<rect x="14" y="3" width="7" height="7"></rect>
<rect x="14" y="14" width="7" height="7"></rect>
<rect x="3" y="14" width="7" height="7"></rect>
</svg>
<span>Browse</span>
</button>
<span>Image Boards</span>
</a>
<button id="favorites-button" class="nav-button" title="Favorites">
<a href="favorites.html" class="nav-button" title="Favorites">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path
d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z">
</path>
<path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"></path>
</svg>
<span>Favorites</span>
</button>
</a>
</nav>
<nav style="display: flex; flex-direction: column; gap: 0.5rem; margin-top: auto;">
<button id="settings-button" class="nav-button" title="Settings">
<a href="settings.html" class="nav-button" title="Settings">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="12" cy="12" r="3"></circle>
<path
d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z">
</path>
<path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path>
</svg>
<span>Settings</span>
</button>
</a>
</nav>
</aside>
<div class="main-wrapper">
<header class="top-header">
<div style="position: relative;">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
style="position: absolute; left: 15px; top: 50%; transform: translateY(-50%); pointer-events: none; color: var(--text-tertiary);">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="position: absolute; left: 15px; top: 50%; transform: translateY(-50%); pointer-events: none; color: var(--text-tertiary);">
<circle cx="11" cy="11" r="8"></circle>
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
</svg>
<input type="text" id="search-input" placeholder="Search across sources..."
style="padding-left: 2.5rem;" />
<input type="text" id="search-input" placeholder="Search across sources..." style="padding-left: 2.5rem;" />
</div>
<button id="search-button" class="hidden"></button>
<button id="search-icon-button" class="hidden"></button>
<button id="search-close-button" class="hidden"></button>
<div id="header-context" class="hidden"></div>
<h1 id="page-title" class="hidden">Home</h1>
<h1 id="page-title" class="hidden">Dashboard</h1>
</header>
<div class="content-view">
<div id="browse-page" class="page">
<h3 style="color: var(--text-tertiary); font-size: 0.8rem; text-transform: uppercase; letter-spacing: 1px; margin-bottom: 1rem;">Sources</h3>
<div id="source-list"></div>
<h3
style="color: var(--text-tertiary); font-size: 0.8rem; text-transform: uppercase; letter-spacing: 1px; margin-bottom: 1rem;">
Sources</h3>
<div id="source-list">
</div>
<h3
style="color: var(--text-tertiary); font-size: 0.8rem; text-transform: uppercase; letter-spacing: 1px; margin: 2rem 0 1rem 0;">
Library</h3>
<h3 style="color: var(--text-tertiary); font-size: 0.8rem; text-transform: uppercase; letter-spacing: 1px; margin: 2rem 0 1rem 0;">Library</h3>
<main id="content-gallery" class="gallery-masonry">
<div id="gallery-placeholder" class="loading-state" style="width: 100%; grid-column: 1 / -1;">
<svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="#52525b" stroke-width="1">
@@ -90,70 +72,17 @@
</svg>
<p>Select a source above to load content</p>
</div>
<div id="loading-spinner" class="hidden loading-state" style="width: 100%; grid-column: 1 / -1;">
<svg style="width:32px; height:32px; color: var(--accent); animation: spin 1s linear infinite;"
viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path
d="M12 2v4m0 12v4M4.93 4.93l2.83 2.83m8.48 8.48l2.83 2.83M2 12h4m12 0h4M4.93 19.07l2.83-2.83m8.48-8.48l2.83-2.83">
</path>
<svg style="width:32px; height:32px; color: var(--accent); animation: spin 1s linear infinite;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M12 2v4m0 12v4M4.93 4.93l2.83 2.83m8.48 8.48l2.83 2.83M2 12h4m12 0h4M4.93 19.07l2.83-2.83m8.48-8.48l2.83-2.83"></path>
</svg>
<p>Fetching images...</p>
</div>
<div id="infinite-loading-spinner" class="hidden loading-state"
style="width: 100%; grid-column: 1 / -1;">
<div id="infinite-loading-spinner" class="hidden loading-state" style="width: 100%; grid-column: 1 / -1;">
<p>Loading more...</p>
</div>
</main>
</div>
<div id="favorites-page" class="page hidden">
<div style="margin-bottom: 2rem;">
<h2 style="margin-bottom: 0.5rem;">Your Favorites</h2>
<p style="color: var(--text-secondary);">Your personally curated collection.</p>
</div>
<main id="favorites-gallery" class="gallery-masonry"></main>
</div>
<div id="settings-page" class="page hidden">
<div style="margin-bottom: 2rem;">
<h2>Settings</h2>
<p style="color: var(--text-secondary);">Customize your viewing experience.</p>
</div>
<div class="settings-grid">
<div class="settings-card">
<h3>Layout Style</h3>
<fieldset>
<label>
<input type="radio" name="layout" value="scroll" id="layout-scroll">
<div>
<strong>Scroll View</strong>
<div style="font-size: 0.8rem; color: var(--text-tertiary);">Single column feed
</div>
</div>
</label>
<label>
<input type="radio" name="layout" value="grid" id="layout-grid">
<div>
<strong>Masonry Grid</strong>
<div style="font-size: 0.8rem; color: var(--text-tertiary);">Staggered dynamic
heights</div>
</div>
</label>
<label>
<input type="radio" name="layout" value="compact" id="layout-compact">
<div>
<strong>Compact Grid</strong>
<div style="font-size: 0.8rem; color: var(--text-tertiary);">Uniform square tiles
</div>
</div>
</label>
</fieldset>
</div>
</div>
</div>
</div>
</div>
@@ -166,31 +95,22 @@
</div>
<div id="search-modal" class="hidden"></div>
<div id="message-bar" class="toast hidden">Message</div>
<div id="updateToast" class="toast hidden" style="border-left-color: #eab308;">
<p>Update Available: <span id="latestVersionDisplay">v1.x</span></p>
</div>
<style>
@keyframes spin {
to {
transform: rotate(360deg);
}
}
</style>
<script type="module" src="../src/renderer.js"></script>
<script type="module" src="../scripts/main.js"></script>
<script src="../src/updateNotification.js"></script>
<script>
// Simple Enter key handler for search
const searchInput = document.getElementById('search-input');
const searchBtn = document.getElementById('search-button');
searchInput.addEventListener('keydown', (e) => {
if (e.key === 'Enter') searchBtn.click();
});
if(searchInput && searchBtn) {
searchInput.addEventListener('keydown', (e) => {
if (e.key === 'Enter') searchBtn.click();
});
}
</script>
</body>
</html>

91
views/settings.html Normal file
View File

@@ -0,0 +1,91 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' https: data:; connect-src 'self' https:;" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Waifu Board - Settings</title>
<link rel="stylesheet" href="styles/home.css">
</head>
<body>
<aside class="sidebar">
<br>
<nav style="display: flex; flex-direction: column; gap: 0.5rem;">
<a href="index.html" class="nav-button" title="Image Boards">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="3" y="3" width="7" height="7"></rect>
<rect x="14" y="3" width="7" height="7"></rect>
<rect x="14" y="14" width="7" height="7"></rect>
<rect x="3" y="14" width="7" height="7"></rect>
</svg>
<span>Image Boards</span>
</a>
<a href="favorites.html" class="nav-button" title="Favorites">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"></path>
</svg>
<span>Favorites</span>
</a>
</nav>
<nav style="display: flex; flex-direction: column; gap: 0.5rem; margin-top: auto;">
<a href="settings.html" class="nav-button active" title="Settings">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="12" cy="12" r="3"></circle>
<path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path>
</svg>
<span>Settings</span>
</a>
</nav>
</aside>
<div class="main-wrapper">
<header class="top-header">
<div style="position: relative;">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="position: absolute; left: 15px; top: 50%; transform: translateY(-50%); pointer-events: none; color: var(--text-tertiary);">
<circle cx="11" cy="11" r="8"></circle>
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
</svg>
<input type="text" id="search-input" placeholder="Search across sources..." style="padding-left: 2.5rem;" />
</div>
<button id="search-button" class="hidden"></button>
<button id="search-icon-button" class="hidden"></button>
<button id="search-close-button" class="hidden"></button>
<div id="header-context" class="hidden"></div>
<h1 id="page-title" class="hidden">Settings</h1>
</header>
<div class="content-view">
<div id="settings-page" class="page">
<div style="margin-bottom: 2rem;">
<h2>Settings</h2>
<p style="color: var(--text-secondary);">App configuration.</p>
</div>
<div class="settings-grid">
<div class="settings-card">
<p style="color: var(--text-tertiary); font-style: italic;">No settings available currently.</p>
</div>
</div>
</div>
</div>
</div>
<div id="message-bar" class="toast hidden">Message</div>
<script type="module" src="../src/renderer.js"></script>
<script type="module" src="../scripts/main.js"></script>
<script src="../src/updateNotification.js"></script>
<script>
const searchInput = document.getElementById('search-input');
const searchBtn = document.getElementById('search-button');
if(searchInput && searchBtn) {
searchInput.addEventListener('keydown', (e) => {
if (e.key === 'Enter') searchBtn.click();
});
}
</script>
</body>
</html>

View File

@@ -136,6 +136,9 @@ body {
margin-right: 1rem;
}
a, a:visited, a:hover, a:active {
text-decoration: none;
}
.nav-button span {
opacity: 0;