project created

This commit is contained in:
2025-07-25 22:03:20 +12:00
parent b80f759df2
commit 1b6c58a348
27 changed files with 211 additions and 5271 deletions
-21
View File
@@ -1,21 +0,0 @@
MIT License
Copyright (c) 2025 Andrew Zambazos
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

Before

Width:  |  Height:  |  Size: 352 KiB

After

Width:  |  Height:  |  Size: 352 KiB

Before

Width:  |  Height:  |  Size: 890 KiB

After

Width:  |  Height:  |  Size: 890 KiB

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.
-3
View File
@@ -1,3 +0,0 @@
{
"bookmarks": []
}
-3
View File
@@ -1,3 +0,0 @@
{
"home_bookmarks": []
}
-64
View File
@@ -1,64 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>SteamOS Browser</title>
<!-- Google Material Icons -->
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<link rel="stylesheet" href="style.css">
<!-- This is where we will link to our SteamOS specific styles -->
<link rel="stylesheet" id="steam-os-style" href="">
</head>
<body class="bg-gray-800 text-white">
<div id="browser-bar" class="flex items-center p-2 bg-gray-900">
<div id="nav-buttons" class="flex">
<button id="back-btn" class="px-4 py-2 mr-2 bg-gray-700 rounded hover:bg-gray-600 flex items-center justify-center">
<span class="material-icons">arrow_back</span>
</button>
<button id="forward-btn" class="px-4 py-2 mr-2 bg-gray-700 rounded hover:bg-gray-600 flex items-center justify-center">
<span class="material-icons">arrow_forward</span>
</button>
<button id="reload-btn" class="px-4 py-2 mr-2 bg-gray-700 rounded hover:bg-gray-600 flex items-center justify-center">
<span class="material-icons">refresh</span>
</button>
<button id="home-btn" class="px-4 py-2 mr-2 bg-blue-600 rounded hover:bg-blue-500 flex items-center justify-center">
<span class="material-icons">home</span>
</button>
</div>
<input type="text" id="url-bar" class="flex-grow p-2 bg-gray-700 border border-gray-600 rounded" placeholder="https://...">
<div id="bookmark-controls" class="flex ml-2">
<button id="bookmark-btn" class="px-4 py-2 mr-2 bg-blue-600 rounded hover:bg-blue-500 flex items-center justify-center">
<span class="material-icons">star_border</span>
</button>
<button id="bookmarks-menu-btn" class="px-4 py-2 mr-2 bg-gray-700 rounded hover:bg-gray-600 flex items-center justify-center">
<span class="material-icons">bookmarks</span>
</button>
<button id="menu-btn" class="px-4 py-2 bg-gray-700 rounded hover:bg-gray-600 flex items-center justify-center">
<span class="material-icons">menu</span>
</button>
</div>
</div>
<!-- Burger menu dropdown -->
<div id="menu-dropdown" class="hidden absolute right-2 top-16 bg-gray-800 border border-gray-600 rounded shadow-lg z-10 w-48">
<a href="#" id="settings-link" class="block px-4 py-2 text-white hover:bg-gray-700">Settings</a>
<a href="#" id="history-link" class="block px-4 py-2 text-white hover:bg-gray-700">History</a>
<a href="#" id="downloads-link" class="block px-4 py-2 text-white hover:bg-gray-700">Downloads</a>
</div>
<!-- Bookmarks dropdown menu -->
<div id="bookmarks-menu" class="hidden absolute right-2 top-16 bg-gray-800 border border-gray-600 rounded shadow-lg z-10 w-64 max-h-96 overflow-y-auto">
<div class="p-2 border-b border-gray-600">
<h3 class="text-sm font-semibold">Bookmarks</h3>
</div>
<div id="bookmarks-list" class="p-2">
<!-- Bookmarks will be populated here -->
</div>
</div>
<webview id="webview" class="w-full h-screen" preload="pages/home-preload.js"></webview>
<script src="../renderer.js"></script>
</body>
</html>
-18
View File
@@ -1,18 +0,0 @@
// home-preload.js
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('homeAPI', {
navigateTo: (url) => ipcRenderer.sendToHost('navigate', url),
// Bookmarks
getBookmarks: () => ipcRenderer.invoke('get-home-bookmarks'),
addBookmark: (bookmark) => ipcRenderer.invoke('add-home-bookmark', bookmark),
removeBookmark: (url) => ipcRenderer.invoke('remove-home-bookmark', url),
});
// Expose settings API for settings page
contextBridge.exposeInMainWorld('settingsAPI', {
clearCookies: () => ipcRenderer.invoke('clear-cookies'),
goHome: () => ipcRenderer.invoke('go-home'),
clearBookmarks: () => ipcRenderer.invoke('clear-bookmarks'),
clearHomeBookmarks: () => ipcRenderer.invoke('clear-home-bookmarks')
});
-774
View File
@@ -1,774 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Nebula Browser - Home</title>
<!-- Google Material Icons -->
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<style>
@font-face {
font-family: 'Inter';
src: url('../../assets/fonts/InterVariable.ttf') format('truetype');
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
background: #1a1724;
min-height: 100vh;
color: #333;
overflow-x: hidden;
display: flex;
flex-direction: column;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 40px 20px;
display: flex;
flex-direction: column;
flex-grow: 1;
}
.main-content {
flex-grow: 1;
display: flex;
flex-direction: column;
justify-content: center;
}
/* Header Section */
.header {
text-align: center;
margin-bottom: 10px;
}
.logo {
height: 100px;
margin-bottom: 10px;
filter: drop-shadow(0 2px 10px rgba(0,0,0,0.3));
}
.title {
color: white;
font-size: 3.5rem;
font-weight: 600;
margin-top: 10px;
margin-bottom: 40px;
text-shadow: 0 2px 10px rgba(0,0,0,0.3);
}
/* Search Section */
.search-section {
text-align: center;
margin-bottom: 40px;
}
.search-container {
position: relative;
max-width: 1000px;
margin: 0 auto;
}
.search-input {
width: 100%;
padding: 18px 60px 18px 20px;
font-size: 16px;
border: none;
border-radius: 25px;
background: rgba(255,255,255,0.95);
backdrop-filter: blur(10px);
box-shadow: 0 8px 32px rgba(0,0,0,0.1);
outline: none;
transition: all 0.3s ease;
}
.search-input:focus {
background: white;
box-shadow: 0 12px 40px rgba(0,0,0,0.15);
transform: translateY(-2px);
}
.search-button {
position: absolute;
right: 10px;
top: 50%;
transform: translateY(-50%);
background: #792fff;
border: none;
border-radius: 50%;
width: 40px;
height: 40px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s ease;
}
.search-button:hover {
background: #0384ff;
transform: translateY(-50%) scale(1.1);
}
.search-button svg {
width: 18px;
height: 18px;
fill: white;
}
/* Quick Links Section */
.quick-links {
margin-bottom: 60px;
}
.section-title {
color: white;
font-size: 1.5rem;
font-weight: 600;
margin-bottom: 30px;
text-align: center;
text-shadow: 0 2px 10px rgba(0,0,0,0.3);
}
.links-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
gap: 20px;
max-width: 1000px;
margin: 0 auto;
}
.link-item {
background: rgba(255,255,255,0.1);
backdrop-filter: blur(10px);
border-radius: 15px;
padding: 20px;
text-align: center;
text-decoration: none;
color: white;
transition: all 0.3s ease;
border: 1px solid rgba(255,255,255,0.2);
}
.link-item:hover {
background: rgba(255,255,255,0.2);
transform: translateY(-5px);
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
}
.link-icon {
width: 48px;
height: 48px;
margin: 0 auto 12px;
background: rgba(255,255,255,0.2);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 24px;
}
.link-title {
font-weight: 500;
font-size: 14px;
}
.link-item {
position: relative;
}
.delete-bookmark-btn {
position: absolute;
top: 5px;
right: 5px;
width: 20px;
height: 20px;
background: rgba(0,0,0,0.5);
color: white;
border: none;
border-radius: 50%;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
font-weight: bold;
opacity: 0;
transition: opacity 0.2s ease;
}
.link-item:hover .delete-bookmark-btn {
opacity: 1;
}
/* Add Link Button */
.add-link-item {
background: rgba(255,255,255,0.1);
border: 2px dashed rgba(255,255,255,0.3);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
font-size: 48px;
color: rgba(255,255,255,0.5);
min-height: 125px; /* Ensure consistent height */
}
.add-link-item:hover {
background: rgba(255,255,255,0.2);
color: white;
border-color: white;
}
/* Modal Styles */
.modal-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.6);
backdrop-filter: blur(5px);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.modal-content {
background: white;
padding: 30px;
border-radius: 15px;
width: 90%;
max-width: 400px;
box-shadow: 0 10px 40px rgba(0,0,0,0.2);
animation: fadeInUp 0.3s ease forwards;
}
.modal-content h2 {
margin-top: 0;
margin-bottom: 25px;
color: #333;
text-align: center;
}
.form-group {
margin-bottom: 20px;
}
.form-group label {
display: block;
margin-bottom: 8px;
font-weight: 500;
color: #555;
}
.form-group input {
width: 100%;
padding: 12px;
border: 1px solid #ddd;
border-radius: 8px;
font-size: 16px;
}
.form-actions {
display: flex;
justify-content: flex-end;
gap: 10px;
margin-top: 30px;
}
.form-actions button {
padding: 10px 20px;
border: none;
border-radius: 8px;
font-size: 16px;
cursor: pointer;
transition: background-color 0.2s ease;
}
.form-actions button[type="submit"] {
background-color: #667eea;
color: white;
}
.form-actions button[type="submit"]:hover {
background-color: #5a67d8;
}
.form-actions button[type="button"] {
background-color: #e0e0e0;
color: #333;
}
.form-actions button[type="button"]:hover {
background-color: #ccc;
}
/* Icon Selector Styles */
.icon-selector {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(40px, 1fr));
gap: 8px;
max-height: 200px;
overflow-y: auto;
border: 1px solid #ddd;
border-radius: 8px;
padding: 10px;
margin-top: 8px;
}
.icon-option {
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
border: 2px solid transparent;
border-radius: 6px;
cursor: pointer;
transition: all 0.2s ease;
background: #f5f5f5;
}
.icon-option:hover {
background: #e0e0e0;
border-color: #667eea;
}
.icon-option.selected {
background: #667eea;
color: white;
border-color: #5a67d8;
}
.icon-option .material-icons {
font-size: 20px;
}
.icon-input-group {
display: flex;
gap: 10px;
margin-bottom: 10px;
}
.icon-input-group input {
flex: 1;
}
.icon-preview {
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
background: #f5f5f5;
border: 1px solid #ddd;
border-radius: 6px;
font-size: 20px;
}
/* Footer */
.footer {
text-align: center;
color: rgba(255,255,255,0.7);
font-size: 14px;
margin-top: 40px;
}
/* Responsive Design */
@media (max-width: 768px) {
.container {
padding: 20px 15px;
}
.logo {
font-size: 2.5rem;
}
.search-input {
padding: 15px 50px 15px 18px;
}
.links-grid {
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
gap: 15px;
}
.recent-grid {
grid-template-columns: 1fr;
}
}
/* Animations */
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.container > * {
animation: fadeInUp 0.6s ease forwards;
}
.search-section {
animation-delay: 0.1s;
}
.quick-links {
animation-delay: 0.2s;
}
</style>
</head>
<body>
<div class="container">
<div class="main-content">
<!-- Header -->
<div class="header">
<img src="../../assets/images/Nebula-Logo.svg" alt="Nebula Browser Logo" class="logo">
<h1 class="title">Nebula Browser</h1>
</div>
<!-- Search Section -->
<div class="search-section">
<div class="search-container">
<input type="text" class="search-input" placeholder="Search" id="searchInput">
<button class="search-button" id="searchButton">
<svg viewBox="0 0 24 24">
<path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/>
</svg>
</button>
</div>
</div>
</div>
<!-- Quick Links -->
<div class="quick-links">
<h2 class="section-title">Quick Access</h2>
<div class="links-grid" id="quickLinksGrid">
<!-- Bookmarks will be populated by JavaScript -->
</div>
</div>
<!-- Add Bookmark Modal -->
<div id="addBookmarkModal" class="modal-overlay" style="display: none;">
<div class="modal-content">
<h2>Add New Bookmark</h2>
<form id="addBookmarkForm">
<div class="form-group">
<label for="bookmarkTitle">Title</label>
<input type="text" id="bookmarkTitle" required>
</div>
<div class="form-group">
<label for="bookmarkUrl">URL</label>
<input type="url" id="bookmarkUrl" required>
</div>
<div class="form-group">
<label for="bookmarkIcon">Icon</label>
<div class="icon-input-group">
<input type="text" id="bookmarkIcon" placeholder="Search for icon or enter emoji">
<div class="icon-preview" id="iconPreview"></div>
</div>
<div class="icon-selector" id="iconSelector">
<!-- Icons will be generated dynamically -->
</div>
</div>
<div class="form-actions">
<button type="button" id="cancelAddBookmark">Cancel</button>
<button type="submit">Add</button>
</div>
</form>
</div>
</div>
<!-- Footer -->
<div class="footer">
<p>&copy; 2025 Nebula Browser.</p>
</div>
</div>
<script type="module">
import { materialIcons } from './iconData.js';
// Search functionality
const searchInput = document.getElementById('searchInput');
const searchButton = document.getElementById('searchButton');
function performSearch() {
const query = searchInput.value.trim();
if (query) {
// Check if it's a URL
if (query.includes('.') && !query.includes(' ')) {
const url = query.startsWith('http') ? query : `https://${query}`;
navigateToUrl(url);
} else {
// Perform search
const searchUrl = `https://www.google.com/search?q=${encodeURIComponent(query)}`;
navigateToUrl(searchUrl);
}
}
}
function navigateToUrl(url) {
if (window.homeAPI) {
window.homeAPI.navigateTo(url);
} else {
console.error('Home API not available');
// Fallback for when preload script fails
window.parent.postMessage({ type: 'navigate', url: url }, '*');
}
}
searchButton.addEventListener('click', performSearch);
searchInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
performSearch();
}
});
// Quick links and recent items navigation
document.addEventListener('click', (e) => {
// Handle delete button click
if (e.target.classList.contains('delete-bookmark-btn')) {
e.preventDefault();
e.stopPropagation();
const urlToDelete = e.target.dataset.url;
deleteBookmark(urlToDelete);
return; // Stop further processing
}
// Handle add bookmark button click
if (e.target.id === 'addBookmarkBtn' || e.target.closest('#addBookmarkBtn')) {
modal.style.display = 'flex';
resetModal();
return; // Stop further processing
}
// Handle navigation for links
const linkItem = e.target.closest('.link-item');
if (linkItem) {
e.preventDefault();
const url = linkItem.dataset.url;
if (url) {
navigateToUrl(url);
}
}
});
// Focus search input on load
window.addEventListener('load', () => {
searchInput.focus();
loadQuickLinks();
});
// Handle keyboard shortcuts
document.addEventListener('keydown', (e) => {
// Ctrl+L or F6 to focus address bar (search input)
if ((e.ctrlKey && e.key === 'l') || e.key === 'F6') {
e.preventDefault();
searchInput.focus();
searchInput.select();
}
});
// Load quick links from bookmarks
async function loadQuickLinks() {
const quickLinksGrid = document.getElementById('quickLinksGrid');
if (!quickLinksGrid) return;
if (window.homeAPI && window.homeAPI.getBookmarks) {
try {
const { home_bookmarks } = await window.homeAPI.getBookmarks();
quickLinksGrid.innerHTML = ''; // Clear existing links
if (home_bookmarks && home_bookmarks.length > 0) {
home_bookmarks.forEach(bookmark => {
const linkItem = document.createElement('a');
linkItem.href = '#';
linkItem.className = 'link-item';
linkItem.dataset.url = bookmark.url;
const linkIcon = document.createElement('div');
linkIcon.className = 'link-icon';
// Check if it's a Material Icon or emoji
if (bookmark.icon && bookmark.icon.startsWith('material:')) {
const materialIcon = document.createElement('span');
materialIcon.className = 'material-icons';
materialIcon.textContent = bookmark.icon.replace('material:', '');
linkIcon.appendChild(materialIcon);
} else {
linkIcon.textContent = bookmark.icon || '⭐';
}
const linkTitle = document.createElement('div');
linkTitle.className = 'link-title';
linkTitle.textContent = bookmark.title;
const deleteBtn = document.createElement('button');
deleteBtn.className = 'delete-bookmark-btn';
deleteBtn.textContent = 'x';
deleteBtn.dataset.url = bookmark.url;
linkItem.appendChild(linkIcon);
linkItem.appendChild(linkTitle);
linkItem.appendChild(deleteBtn);
quickLinksGrid.appendChild(linkItem);
});
}
// Add the "Add" button
const addButton = document.createElement('div');
addButton.className = 'link-item add-link-item';
addButton.id = 'addBookmarkBtn';
addButton.textContent = '+';
quickLinksGrid.appendChild(addButton);
} catch (error) {
console.error('Error loading home bookmarks:', error);
quickLinksGrid.innerHTML = '<p style="color: white; text-align: center; grid-column: 1 / -1;">Could not load bookmarks.</p>';
}
} else {
console.error('Home API for bookmarks not available');
quickLinksGrid.innerHTML = '<p style="color: white; text-align: center; grid-column: 1 / -1;">Could not load bookmarks.</p>';
}
}
// Modal handling
const modal = document.getElementById('addBookmarkModal');
const cancelBtn = document.getElementById('cancelAddBookmark');
const form = document.getElementById('addBookmarkForm');
// Icon selector functionality
const iconInput = document.getElementById('bookmarkIcon');
const iconPreview = document.getElementById('iconPreview');
const iconSelector = document.getElementById('iconSelector');
// Dynamically generate icon options
materialIcons.forEach(name => {
const option = document.createElement('div');
option.className = 'icon-option';
option.dataset.icon = name;
const span = document.createElement('span');
span.className = 'material-icons';
span.textContent = name;
option.appendChild(span);
iconSelector.appendChild(option);
});
let selectedIconType = 'emoji'; // 'emoji' or 'material'
let selectedIcon = '⭐';
// Handle icon input changes
iconInput.addEventListener('input', (e) => {
const value = e.target.value.trim();
if (value) {
selectedIconType = 'emoji';
selectedIcon = value;
updateIconPreview(value);
clearIconSelection();
}
});
// Handle icon selector clicks
iconSelector.addEventListener('click', (e) => {
const iconOption = e.target.closest('.icon-option');
if (iconOption) {
const iconName = iconOption.dataset.icon;
selectedIconType = 'material';
selectedIcon = iconName;
// Update input and preview
iconInput.value = iconName;
updateIconPreview(`material:${iconName}`);
// Update selection visual
clearIconSelection();
iconOption.classList.add('selected');
}
});
function updateIconPreview(icon) {
iconPreview.innerHTML = '';
if (icon.startsWith('material:')) {
const materialIcon = document.createElement('span');
materialIcon.className = 'material-icons';
materialIcon.textContent = icon.replace('material:', '');
iconPreview.appendChild(materialIcon);
} else {
iconPreview.textContent = icon || '⭐';
}
}
function clearIconSelection() {
iconSelector.querySelectorAll('.icon-option').forEach(option => {
option.classList.remove('selected');
});
}
// Reset modal when opened
function resetModal() {
form.reset();
selectedIconType = 'emoji';
selectedIcon = '⭐';
updateIconPreview('⭐');
clearIconSelection();
}
cancelBtn.addEventListener('click', () => {
modal.style.display = 'none';
});
modal.addEventListener('click', (e) => {
if (e.target === modal) {
modal.style.display = 'none';
}
});
form.addEventListener('submit', async (e) => {
e.preventDefault();
const title = document.getElementById('bookmarkTitle').value;
const url = document.getElementById('bookmarkUrl').value;
// Determine the final icon format
let icon;
if (selectedIconType === 'material') {
icon = `material:${selectedIcon}`;
} else {
icon = selectedIcon || iconInput.value || '⭐';
}
if (window.homeAPI && window.homeAPI.addBookmark) {
const result = await window.homeAPI.addBookmark({ title, url, icon });
if (result.success) {
loadQuickLinks();
modal.style.display = 'none';
resetModal();
} else {
alert(result.message);
}
}
});
async function deleteBookmark(url) {
if (window.homeAPI && window.homeAPI.removeBookmark) {
const result = await window.homeAPI.removeBookmark(url);
if (result.success) {
loadQuickLinks();
} else {
}
}
}
</script>
</body>
</html>
-377
View File
@@ -1,377 +0,0 @@
// List of popular Material Icons
export const materialIcons = [
'home',
'favorite',
'star',
'work',
'school',
'shopping_cart',
'music_note',
'movie',
'games',
'sports_esports',
'library_books',
'camera_alt',
'flight',
'restaurant',
'sports_soccer',
'code',
'build',
'palette',
'analytics',
'security',
'account_circle',
'alarm',
'alarm_on',
'android',
'apartment',
'arrow_back',
'arrow_forward',
'attach_file',
'autorenew',
'book',
'bookmark',
'build_circle',
'calendar_today',
'chevron_left',
'chevron_right',
'cloud',
'delete',
'directions',
'edit',
'email',
'add',
'add_alert',
'add_box',
'add_circle',
'add_circle_outline',
'adjust',
'airline_seat_flat',
'airline_seat_individual_suite',
'airplanemode_active',
'announcement',
'aspect_ratio',
'assessment',
'assignment',
'assistant',
'attach_money',
'attachment',
'backspace',
'backup',
'battery_alert',
'beenhere',
'block',
'bluetooth',
'bluetooth_audio',
'book_online',
'bookmark_border',
'border_all',
'brightness_1',
'brightness_2',
'brightness_7',
'bug_report',
'cached',
'cake',
'call',
'camera',
'cancel_presentation',
'card_giftcard',
'casino',
'cast',
'change_history',
'check',
'check_box',
'check_circle',
'child_care',
'chrome_reader_mode',
'class',
'clean_hands',
'clear',
'close',
'cloud_download',
'cloud_upload',
'code_off',
'collections',
'color_lens',
'comment',
'compare_arrows',
'computer',
'confirmation_number',
'contact_mail',
'content_copy',
'content_paste',
'control_point',
'copyright',
'create',
'credit_card',
'crop',
'dashboard',
'date_range',
'delete_forever',
'description',
'desktop_mac',
'desktop_windows',
'directions_bike',
'directions_boat',
'directions_bus',
'directions_car',
'directions_run',
'directions_transit',
'dns',
'done',
'done_all',
'donut_large',
'double_arrow',
'drafts',
'drag_handle',
'drive_eta',
'dvr',
'edit_attributes',
'eject',
'emoji_emotions',
'event',
'exit_to_app',
'expand_less',
'expand_more',
'explore',
'extension',
'face',
'favorite_border',
'file_copy',
'filter_list',
'flag',
'flaky',
'flight_takeoff',
'folder',
'format_bold',
'format_clear',
'format_color_fill',
'format_indent_decrease',
'format_indent_increase',
'format_italic',
'format_list_bulleted',
'format_list_numbered',
'format_quote',
'format_underlined',
'forum',
'forward',
'fullscreen',
'functions',
'g_translate',
'gesture',
'get_app',
'gif',
'grade',
'group',
'help',
'history',
'home_filled',
'hourglass_empty',
'http',
'image',
'input',
'insert_chart',
'invert_colors',
'iso',
'keyboard',
'label',
'language',
'laptop_mac',
'launch',
'lightbulb',
'line_style',
'link',
'list',
'list_alt',
'lock',
'lock_open',
'loop',
'loupe',
'mail_outline',
'map',
'markunread_mailbox',
'memory',
'menu',
'merge_type',
'mic',
'mic_off',
'mood',
'more_horiz',
'more_vert',
'mouse',
'movie_creation',
'multiline_chart',
'music_video',
'nature',
'navigate_before',
'navigate_next',
'network_cell',
'new_releases',
'nfc',
'no_encryption',
'not_interested',
'open_in_browser',
'open_with',
'pageview',
'pan_tool',
'pause',
'payment',
'people',
'perm_camera_mic',
'perm_contact_calendar',
'perm_identity',
'perm_media',
'perm_phone_msg',
'perm_scan_wifi',
'person',
'person_add',
'photo',
'pie_chart',
'pin_drop',
'place',
'play_arrow',
'play_circle_filled',
'playlist_add',
'poll',
'polymer',
'power_settings_new',
'print',
'priority_high',
'public',
'publish',
'query_builder',
'question_answer',
'radio',
'receipt',
'redo',
'refresh',
'remove',
'remove_circle',
'reorder',
'report',
'report_problem',
'restaurant_menu',
'restore',
'ring_volume',
'room',
'rotate_left',
'rotate_right',
'rounded_corner',
'rowing',
'rss_feed',
'rv_hookup',
'satellite',
'save',
'scanner',
'schedule',
'screen_lock_landscape',
'screen_lock_portrait',
'screen_rotation',
'sd_storage',
'search',
'send',
'settings',
'settings_cell',
'settings_backup_restore',
'settings_input_component',
'share',
'shield',
'shop',
'shopping_bag',
'shuffle',
'signal_cellular_4_bar',
'sim_card',
'skip_next',
'skip_previous',
'slideshow',
'smartphone',
'smoke_free',
'sms',
'snooze',
'sort',
'spa',
'spellcheck',
'star_border',
'star_half',
'stars',
'stay_primary_landscape',
'stay_current_portrait',
'stop',
'storage',
'store',
'straighten',
'streetview',
'style',
'swap_calls',
'sync',
'system_update_alt',
'tab',
'table_chart',
'tag',
'text_fields',
'text_format',
'thumb_down',
'thumb_up',
'thumbs_up_down',
'time_to_leave',
'timer',
'timer_off',
'toc',
'today',
'toll',
'tonality',
'touch_app',
'traffic',
'train',
'tram',
'transfer_within_a_station',
'translate',
'trending_down',
'trending_flat',
'trending_up',
'tune',
'turned_in',
'turned_in_not',
'unarchive',
'undo',
'unfold_less',
'unfold_more',
'update',
'usb',
'verified_user',
'vertical_align_bottom',
'vertical_align_center',
'vertical_align_top',
'vibration',
'video_call',
'video_label',
'video_library',
'visibility',
'visibility_off',
'voice_chat',
'voicemail',
'volume_down',
'volume_mute',
'volume_off',
'volume_up',
'vpn_key',
'wallpaper',
'warning',
'watch',
'wb_auto',
'wb_cloudy',
'wb_incandescent',
'wb_iridescent',
'wb_sunny',
'web',
'whatshot',
'widgets',
'wifi',
'wifi_off',
'work_outline',
'youtube_searched_for',
'zoom_in',
'alternate_email',
'fax',
'zoom_out'
];
-93
View File
@@ -1,93 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Nebula Browser - Settings</title>
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<link rel="stylesheet" href="../style.css">
<style>
body {
background: #1a1724;
color: white;
}
.container {
padding: 20px;
}
.settings-section {
background: rgba(255, 255, 255, 0.1);
border-radius: 8px;
padding: 20px;
margin-bottom: 20px;
}
.settings-section h2 {
margin-top: 0;
border-bottom: 1px solid rgba(255, 255, 255, 0.2);
padding-bottom: 10px;
margin-bottom: 20px;
}
.setting-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 15px 0;
}
.setting-item p {
margin: 0;
}
.setting-item .description {
font-size: 0.9em;
color: rgba(255, 255, 255, 0.7);
}
button {
background-color: #792fff;
color: white;
border: none;
padding: 10px 15px;
border-radius: 5px;
cursor: pointer;
transition: background-color 0.3s ease;
}
button:hover {
background-color: #0384ff;
}
#backToHome {
margin-top: 20px;
}
</style>
</head>
<body>
<div class="container">
<h1>Settings</h1>
<div class="settings-section">
<h2>Privacy and Security</h2>
<div class="setting-item">
<div>
<p>Clear browsing data</p>
<span class="description">Clear cookies, and other site data.</span>
</div>
<button id="clearCookiesBtn">Clear data</button>
</div>
<div class="setting-item">
<div>
<p>Clear bookmarks</p>
<span class="description">Remove all bookmarks from bookmarks.json.</span>
</div>
<button id="clearBookmarksBtn">Clear bookmarks</button>
</div>
<div class="setting-item">
<div>
<p>Clear home bookmarks</p>
<span class="description">Remove all bookmarks from home-bookmarks.json.</span>
</div>
<button id="clearHomeBookmarksBtn">Clear home bookmarks</button>
</div>
</div>
<button id="backToHome">Back to Home</button>
</div>
<script src="./settings.js"></script>
</body>
</html>
-65
View File
@@ -1,65 +0,0 @@
document.addEventListener('DOMContentLoaded', () => {
const clearCookiesBtn = document.getElementById('clearCookiesBtn');
const backToHomeBtn = document.getElementById('backToHome');
if (clearCookiesBtn) {
clearCookiesBtn.addEventListener('click', () => {
if (window.settingsAPI && window.settingsAPI.clearCookies) {
window.settingsAPI.clearCookies();
alert('Cookies have been cleared.');
} else {
console.error('Settings API not available.');
alert('Could not clear cookies. API not found.');
}
});
}
if (backToHomeBtn) {
backToHomeBtn.addEventListener('click', () => {
if (window.settingsAPI && window.settingsAPI.goHome) {
window.settingsAPI.goHome();
} else {
// Fallback if the API isn't available
window.history.back();
}
});
}
// Clear bookmarks button
const clearBookmarksBtn = document.getElementById('clearBookmarksBtn');
if (clearBookmarksBtn) {
clearBookmarksBtn.addEventListener('click', () => {
if (window.settingsAPI && window.settingsAPI.clearBookmarks) {
window.settingsAPI.clearBookmarks().then(result => {
if (result && result.success) {
alert('All bookmarks have been cleared.');
} else {
alert('Could not clear bookmarks.');
console.error(result.error);
}
});
} else {
console.error('Settings API for clearing bookmarks not available.');
alert('Could not clear bookmarks. API not found.');
}
});
}
// Clear home bookmarks button
const clearHomeBookmarksBtn = document.getElementById('clearHomeBookmarksBtn');
if (clearHomeBookmarksBtn) {
clearHomeBookmarksBtn.addEventListener('click', () => {
if (window.settingsAPI && window.settingsAPI.clearHomeBookmarks) {
window.settingsAPI.clearHomeBookmarks().then(result => {
if (result && result.success) {
alert('All home bookmarks have been cleared.');
} else {
alert('Could not clear home bookmarks.');
console.error(result.error);
}
});
} else {
console.error('Settings API for clearing home bookmarks not available.');
alert('Could not clear home bookmarks. API not found.');
}
});
}
});
-39
View File
@@ -1,39 +0,0 @@
/* steamos.css */
/* Big Picture Mode inspired UI for SteamOS */
body {
background-color: #1b2838 !important;
color: #c7d5e0 !important;
}
#browser-bar {
background-color: #171a21 !important;
padding: 10px !important;
}
#nav-buttons button {
background-color: #4c6b22 !important;
color: white !important;
border: none !important;
border-radius: 5px !important;
font-size: 1.2em !important;
padding: 10px 20px !important;
margin-right: 10px !important;
transition: background-color 0.2s;
}
#nav-buttons button:hover {
background-color: #5f862a !important;
}
#url-bar {
background-color: #2a475e !important;
border: 1px solid #66c0f4 !important;
color: white !important;
font-size: 1.2em !important;
padding: 10px !important;
}
#url-bar::placeholder {
color: #c7d5e0;
}
-33
View File
@@ -1,33 +0,0 @@
/* style.css */
@import 'https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css';
@font-face {
font-family: 'Inter';
src: url('../assets/fonts/InterVariable.ttf') format('truetype');
}
/* General styles for the browser UI */
body {
margin: 0;
font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
overflow: hidden; /* Hide scrollbars on the main window */
}
#browser-bar {
-webkit-app-region: drag; /* Allows dragging the window from the browser bar */
}
#nav-buttons button, #url-bar {
-webkit-app-region: no-drag; /* Prevents dragging from interactive elements */
}
#home-btn {
font-size: 16px;
display: flex;
align-items: center;
justify-content: center;
}
#webview {
height: calc(100vh - 56px); /* Adjust height to fill space below the browser bar */
}
+19
View File
@@ -0,0 +1,19 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Sleek Electron Browser</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="navbar">
<button id="back-button" title="Go Back"></button>
<button id="forward-button" title="Go Forward"></button>
<button id="refresh-button" title="Refresh Page"></button>
<input type="text" id="address-bar" placeholder="Enter URL" value="https://www.google.com/">
<button id="go-button" title="Go">Go</button>
</div>
<script src="renderer.js"></script>
</body>
</html>
+77 -230
View File
@@ -1,245 +1,92 @@
// main.js const { app, BrowserWindow, BrowserView, ipcMain } = require('electron'); // Add ipcMain here
const path = require('node:path');
// Modules to control application life and create native browser window let mainWindow;
const { app, BrowserWindow, ipcMain, session } = require('electron'); let browserView;
const { pathToFileURL } = require('url');
const path = require('path');
const os = require('os');
const fs = require('fs');
const createWindow = () => {
mainWindow = new BrowserWindow({
width: 1000,
height: 700,
minWidth: 800,
minHeight: 600,
titleBarStyle: 'hiddenInset',
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
nodeIntegration: false,
contextIsolation: true,
webviewTag: false
}
});
app.commandLine.appendSwitch('ignore-gpu-blacklist'); mainWindow.loadFile('index.html');
// Open the DevTools.
// mainWindow.webContents.openDevTools();
if (process.platform === 'win32') { browserView = new BrowserView({
app.commandLine.appendSwitch('disable-gpu-sandbox'); webPreferences: {
nodeIntegration: false,
app.commandLine.appendSwitch('no-sandbox'); contextIsolation: true,
} }
});
mainWindow.setBrowserView(browserView);
const navBarHeight = 80; // Make sure this matches your CSS navbar height
const updateBrowserViewBounds = () => {
const { width, height } = mainWindow.getBounds();
browserView.setBounds({ x: 0, y: navBarHeight, width: width, height: height - navBarHeight });
};
// Function to check if the application is running on SteamOS updateBrowserViewBounds(); // Set initial bounds
const isSteamOS = () => { browserView.webContents.loadURL('https://www.google.com');
return os.platform() === 'linux' && os.release().includes('steam'); mainWindow.on('resize', updateBrowserViewBounds);
// --- IPC Handlers ---
ipcMain.on('load-url', (event, url) => {
let formattedUrl = url;
// Basic URL formatting: if no protocol, assume https://
if (!formattedUrl.match(/^[a-zA-Z]+:\/\//)) {
formattedUrl = 'https://' + formattedUrl;
}
browserView.webContents.loadURL(formattedUrl).catch(err => {
console.error('Failed to load URL:', err);
// Optionally send an error back to the renderer
});
});
ipcMain.on('go-back', () => {
if (browserView.webContents.canGoBack()) {
browserView.webContents.goBack();
}
});
ipcMain.on('go-forward', () => {
if (browserView.webContents.canGoForward()) {
browserView.webContents.goForward();
}
});
ipcMain.on('refresh-page', () => {
browserView.webContents.reload();
});
// --- End IPC Handlers ---
}; };
// Bookmark management functions
const bookmarksPath = path.join(__dirname, 'data', 'bookmarks.json');
const homeBookmarksPath = path.join(__dirname, 'data', 'home-bookmarks.json');
const loadBookmarks = () => {
try {
if (fs.existsSync(bookmarksPath)) {
const data = fs.readFileSync(bookmarksPath, 'utf8');
return JSON.parse(data);
}
} catch (error) {
console.error('Error loading bookmarks:', error);
}
return { bookmarks: [] };
};
const saveBookmarks = (bookmarksData) => {
try {
fs.writeFileSync(bookmarksPath, JSON.stringify(bookmarksData, null, 2));
return true;
} catch (error) {
console.error('Error saving bookmarks:', error);
return false;
}
};
const loadHomeBookmarks = () => {
try {
if (fs.existsSync(homeBookmarksPath)) {
const data = fs.readFileSync(homeBookmarksPath, 'utf8');
return JSON.parse(data);
}
} catch (error) {
console.error('Error loading home bookmarks:', error);
}
return { home_bookmarks: [] };
};
const saveHomeBookmarks = (bookmarksData) => {
try {
fs.writeFileSync(homeBookmarksPath, JSON.stringify(bookmarksData, null, 2));
return true;
} catch (error) {
console.error('Error saving home bookmarks:', error);
return false;
}
};
// IPC handlers for bookmark functionality
ipcMain.handle('get-bookmarks', () => {
return loadBookmarks();
});
ipcMain.handle('get-home-bookmarks', () => {
return loadHomeBookmarks();
});
ipcMain.handle('add-bookmark', (event, bookmark) => {
const bookmarksData = loadBookmarks();
// Check if bookmark already exists
const exists = bookmarksData.bookmarks.some(b => b.url === bookmark.url);
if (exists) {
return { success: false, message: 'Bookmark already exists' };
}
// Add timestamp
bookmark.dateAdded = new Date().toISOString();
bookmarksData.bookmarks.push(bookmark);
const saved = saveBookmarks(bookmarksData);
return { success: saved, message: saved ? 'Bookmark added' : 'Failed to save bookmark' };
});
ipcMain.handle('add-home-bookmark', (event, bookmark) => {
const bookmarksData = loadHomeBookmarks();
// Check if bookmark already exists
const exists = bookmarksData.home_bookmarks.some(b => b.url === bookmark.url);
if (exists) {
return { success: false, message: 'Bookmark already exists' };
}
// Add timestamp
bookmark.dateAdded = new Date().toISOString();
bookmarksData.home_bookmarks.push(bookmark);
const saved = saveHomeBookmarks(bookmarksData);
return { success: saved, message: saved ? 'Bookmark added' : 'Failed to save bookmark' };
});
ipcMain.handle('remove-bookmark', (event, url) => {
const bookmarksData = loadBookmarks();
const initialLength = bookmarksData.bookmarks.length;
bookmarksData.bookmarks = bookmarksData.bookmarks.filter(b => b.url !== url);
if (bookmarksData.bookmarks.length < initialLength) {
const saved = saveBookmarks(bookmarksData);
return { success: saved, message: saved ? 'Bookmark removed' : 'Failed to save bookmarks' };
}
return { success: false, message: 'Bookmark not found' };
});
ipcMain.handle('remove-home-bookmark', (event, url) => {
const bookmarksData = loadHomeBookmarks();
const initialLength = bookmarksData.home_bookmarks.length;
bookmarksData.home_bookmarks = bookmarksData.home_bookmarks.filter(b => b.url !== url);
if (bookmarksData.home_bookmarks.length < initialLength) {
const saved = saveHomeBookmarks(bookmarksData);
return { success: saved, message: saved ? 'Bookmark removed' : 'Failed to save bookmarks' };
}
return { success: false, message: 'Bookmark not found' };
});
// Handle clear cookies request from settings page
ipcMain.handle('clear-cookies', async (event) => {
try {
const ses = event.sender.session;
await ses.clearStorageData({ storages: ['cookies'] });
return { success: true };
} catch (error) {
console.error('Error clearing cookies:', error);
return { success: false, error: error.message };
}
});
// Handle go-home request from settings page to navigate back to home
ipcMain.handle('go-home', (event) => {
// event.sender is the webContents of the webview
const wc = event.sender;
const homePath = path.join(__dirname, 'frontend', 'pages', 'home.html');
// Convert file path to file:// URL and load in the webview
const homeUrl = pathToFileURL(homePath).href;
wc.loadURL(homeUrl);
return { success: true };
});
// Handle clear bookmarks request from settings page
ipcMain.handle('clear-bookmarks', (event) => {
try {
fs.writeFileSync(bookmarksPath, JSON.stringify({ bookmarks: [] }, null, 2));
return { success: true };
} catch (error) {
console.error('Error clearing bookmarks:', error);
return { success: false, error: error.message };
}
});
// Handle clear home bookmarks request from settings page
ipcMain.handle('clear-home-bookmarks', (event) => {
try {
fs.writeFileSync(homeBookmarksPath, JSON.stringify({ home_bookmarks: [] }, null, 2));
return { success: true };
} catch (error) {
console.error('Error clearing home bookmarks:', error);
return { success: false, error: error.message };
}
});
function createWindow() {
// Create the browser window.
const mainWindow = new BrowserWindow({
width: 1280,
height: 720,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
// The webview tag is essential for our browser's core functionality.
webviewTag: true,
// For security reasons, it's good practice to disable nodeIntegration
// and use a preload script to expose specific functionalities.
nodeIntegration: false,
contextIsolation: true,
},
});
// Remove default application menu (File, Edit, View)
mainWindow.removeMenu();
// Load the index.html of the app.
mainWindow.loadFile('frontend/index.html');
// Pass the SteamOS status to the renderer process
mainWindow.webContents.on('did-finish-load', () => {
mainWindow.webContents.send('is-steam-os', isSteamOS());
});
// Open the DevTools.
// mainWindow.webContents.openDevTools()
}
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.whenReady().then(() => { app.whenReady().then(() => {
createWindow(); createWindow();
app.on('activate', function () { app.on('activate', () => {
// On macOS it's common to re-create a window in the app when the if (BrowserWindow.getAllWindows().length === 0) {
// dock icon is clicked and there are no other windows open. createWindow();
if (BrowserWindow.getAllWindows().length === 0) createWindow(); }
}); });
}); });
// Quit when all windows are closed, except on macOS. There, it's common app.on('window-all-closed', () => {
// for applications and their menu bar to stay active until the user quits if (process.platform !== 'darwin') {
// explicitly with Cmd + Q. app.quit();
app.on('window-all-closed', function () { }
if (process.platform !== 'darwin') app.quit(); });
});
// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.
+7 -3251
View File
File diff suppressed because it is too large Load Diff
+16 -30
View File
@@ -1,38 +1,24 @@
{ {
"name": "steamos-browser", "name": "nebulabrowser",
"version": "1.0.0", "version": "1.0.0",
"description": "A simple Electron browser optimized for SteamOS", "description": "",
"main": "main.js", "main": "main.js",
"scripts": { "scripts": {
"start": "electron .", "test": "echo \"Error: no test specified\" && exit 1",
"pack": "electron-builder --dir", "start" : "electron . --disable-gpu-sandbox"
"dist": "electron-builder"
}, },
"author": "Zambazos Media Group", "repository": {
"license": "MIT", "type": "git",
"url": "git+https://github.com/Bobbybear007/NebulaBrowser.git"
},
"keywords": [],
"author": "",
"license": "ISC",
"bugs": {
"url": "https://github.com/Bobbybear007/NebulaBrowser/issues"
},
"homepage": "https://github.com/Bobbybear007/NebulaBrowser#readme",
"devDependencies": { "devDependencies": {
"electron": "^37.2.4", "electron": "^37.2.4"
"electron-builder": "^24.9.1"
},
"build": {
"appId": "group.zambazosmedia.NebulaBrowser",
"productName": "NebulaBrowser",
"files": [
"main.js",
"preload.js",
"index.html",
"renderer.js",
"style.css",
"steamos.css"
],
"win": {
"target": "nsis"
},
"mac": {
"target": "dmg"
},
"linux": {
"target": "AppImage"
}
} }
} }
+7 -23
View File
@@ -1,26 +1,10 @@
// preload.js
const { contextBridge, ipcRenderer } = require('electron'); const { contextBridge, ipcRenderer } = require('electron');
// Expose protected methods that allow the renderer process to use // Expose a limited set of IPC methods to the renderer process
// the ipcRenderer without exposing the entire object
contextBridge.exposeInMainWorld('electronAPI', { contextBridge.exposeInMainWorld('electronAPI', {
onIsSteamOS: (callback) => ipcRenderer.on('is-steam-os', (_event, value) => callback(value)), loadURL: (url) => ipcRenderer.send('load-url', url),
goBack: () => ipcRenderer.send('go-back'),
// Bookmark API goForward: () => ipcRenderer.send('go-forward'),
getBookmarks: () => ipcRenderer.invoke('get-bookmarks'), refreshPage: () => ipcRenderer.send('refresh-page'),
addBookmark: (bookmark) => ipcRenderer.invoke('add-bookmark', bookmark), // You can add more APIs here as your browser grows
removeBookmark: (url) => ipcRenderer.invoke('remove-bookmark', url), });
// Home Bookmark API
getHomeBookmarks: () => ipcRenderer.invoke('get-home-bookmarks'),
addHomeBookmark: (bookmark) => ipcRenderer.invoke('add-home-bookmark', bookmark),
removeHomeBookmark: (url) => ipcRenderer.invoke('remove-home-bookmark', url),
});
contextBridge.exposeInMainWorld('settingsAPI', {
clearCookies: () => ipcRenderer.invoke('clear-cookies'),
goHome: () => ipcRenderer.invoke('go-home'),
clearBookmarks: () => ipcRenderer.invoke('clear-bookmarks'),
clearHomeBookmarks: () => ipcRenderer.invoke('clear-home-bookmarks'),
});
+30 -247
View File
@@ -1,263 +1,46 @@
// renderer.js document.addEventListener('DOMContentLoaded', () => {
const addressBar = document.getElementById('address-bar');
const goButton = document.getElementById('go-button');
const backButton = document.getElementById('back-button');
const forwardButton = document.getElementById('forward-button');
const refreshButton = document.getElementById('refresh-button');
window.electronAPI.onIsSteamOS((isSteamOS) => { // Load initial URL if present in address bar
console.log('Is SteamOS:', isSteamOS); const initialUrl = addressBar.value;
if (isSteamOS) { if (initialUrl) {
// Apply the SteamOS stylesheet window.electronAPI.loadURL(initialUrl);
document.getElementById('steam-os-style').href = 'steamos.css';
// You could also set a specific user agent for the webview if needed
const webview = document.getElementById('webview');
webview.useragent = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36 SteamOS/3.0";
} }
});
// Bookmark management goButton.addEventListener('click', () => {
let bookmarks = []; const url = addressBar.value;
if (url) {
// Load bookmarks from storage window.electronAPI.loadURL(url);
async function loadBookmarks() {
try {
const result = await window.electronAPI.getBookmarks();
bookmarks = result.bookmarks || [];
updateBookmarksUI();
updateBookmarkButton();
} catch (error) {
console.error('Error loading bookmarks:', error);
}
}
// Update the bookmarks dropdown menu
function updateBookmarksUI() {
const bookmarksList = document.getElementById('bookmarks-list');
if (bookmarks.length === 0) {
bookmarksList.innerHTML = '<p class="text-gray-400 text-sm">No bookmarks yet</p>';
return;
}
bookmarksList.innerHTML = bookmarks.map(bookmark => `
<div class="bookmark-item flex items-center justify-between p-2 hover:bg-gray-700 rounded mb-1">
<div class="flex-grow cursor-pointer" onclick="loadBookmark('${bookmark.url}')">
<div class="font-medium text-sm truncate">${bookmark.title}</div>
<div class="text-xs text-gray-400 truncate">${bookmark.url}</div>
</div>
<button class="ml-2 px-2 py-1 text-red-400 hover:text-red-300 text-xs" onclick="removeBookmark('${bookmark.url}')">✕</button>
</div>
`).join('');
}
// Update bookmark button state
function updateBookmarkButton() {
const bookmarkBtn = document.getElementById('bookmark-btn');
const currentUrl = document.getElementById('url-bar').value;
const isBookmarked = bookmarks.some(b => b.url === currentUrl);
bookmarkBtn.textContent = isBookmarked ? '★' : '☆';
bookmarkBtn.style.backgroundColor = isBookmarked ? '#dc2626' : '#2563eb';
}
// Add/remove bookmark
async function toggleBookmark() {
const webview = document.getElementById('webview');
const urlBar = document.getElementById('url-bar');
const currentUrl = urlBar.value;
if (!currentUrl) return;
const isBookmarked = bookmarks.some(b => b.url === currentUrl);
try {
if (isBookmarked) {
const result = await window.electronAPI.removeBookmark(currentUrl);
if (result.success) {
console.log('Bookmark removed');
await loadBookmarks(); // Reload bookmarks
} else {
console.error('Failed to remove bookmark:', result.message);
}
} else {
// Get page title from webview
let pageTitle = currentUrl;
try {
pageTitle = await webview.executeJavaScript('document.title') || currentUrl;
} catch (e) {
console.log('Could not get page title, using URL');
}
const bookmark = {
title: pageTitle,
url: currentUrl
};
const result = await window.electronAPI.addBookmark(bookmark);
if (result.success) {
console.log('Bookmark added');
await loadBookmarks(); // Reload bookmarks
} else {
console.error('Failed to add bookmark:', result.message);
}
}
} catch (error) {
console.error('Error toggling bookmark:', error);
}
}
// Load a bookmark
function loadBookmark(url) {
const webview = document.getElementById('webview');
const urlBar = document.getElementById('url-bar');
webview.loadURL(url);
urlBar.value = url;
// Hide bookmarks menu
document.getElementById('bookmarks-menu').classList.add('hidden');
}
// Remove a bookmark
async function removeBookmark(url) {
try {
const result = await window.electronAPI.removeBookmark(url);
if (result.success) {
console.log('Bookmark removed');
await loadBookmarks(); // Reload bookmarks
} else {
console.error('Failed to remove bookmark:', result.message);
}
} catch (error) {
console.error('Error removing bookmark:', error);
}
}
window.onload = () => {
const webview = document.getElementById('webview');
const urlBar = document.getElementById('url-bar');
const backBtn = document.getElementById('back-btn');
const forwardBtn = document.getElementById('forward-btn');
const reloadBtn = document.getElementById('reload-btn');
const homeBtn = document.getElementById('home-btn');
const bookmarkBtn = document.getElementById('bookmark-btn');
const bookmarksMenuBtn = document.getElementById('bookmarks-menu-btn');
const bookmarksMenu = document.getElementById('bookmarks-menu');
const menuBtn = document.getElementById('menu-btn');
const menuDropdown = document.getElementById('menu-dropdown');
// Load bookmarks on startup
loadBookmarks();
// Set home page as default
webview.src = 'pages/home.html';
// Navigation controls
backBtn.addEventListener('click', () => webview.goBack());
forwardBtn.addEventListener('click', () => webview.goForward());
reloadBtn.addEventListener('click', () => webview.reload());
// Home button functionality
homeBtn.addEventListener('click', () => {
webview.src = 'pages/home.html';
urlBar.value = 'Type URL Here';
});
// Home button functionality (reload button can serve as home when on external sites)
reloadBtn.addEventListener('dblclick', () => {
webview.src = 'pages/home.html';
urlBar.value = 'Type URL Here';
});
// Listen for navigation messages from home page
webview.addEventListener('ipc-message', (event) => {
if (event.channel === 'navigate') {
const url = event.args[0];
webview.loadURL(url);
urlBar.value = url;
} }
}); });
// Bookmark controls addressBar.addEventListener('keydown', (event) => {
bookmarkBtn.addEventListener('click', toggleBookmark); if (event.key === 'Enter') {
const url = addressBar.value;
// Bookmarks menu toggle if (url) {
bookmarksMenuBtn.addEventListener('click', (e) => { window.electronAPI.loadURL(url);
e.stopPropagation();
bookmarksMenu.classList.toggle('hidden');
});
// Burger menu toggle
menuBtn.addEventListener('click', (e) => {
e.stopPropagation();
menuDropdown.classList.toggle('hidden');
// Hide bookmarks menu if open
bookmarksMenu.classList.add('hidden');
});
// Close menus when clicking outside
document.addEventListener('click', (e) => {
if (!bookmarksMenu.contains(e.target) && !bookmarksMenuBtn.contains(e.target)) {
bookmarksMenu.classList.add('hidden');
}
if (!menuDropdown.contains(e.target) && !menuBtn.contains(e.target)) {
menuDropdown.classList.add('hidden');
}
});
// URL bar functionality
urlBar.addEventListener('focus', () => {
if (urlBar.value === 'Type URL Here') {
urlBar.value = '';
}
});
urlBar.addEventListener('blur', () => {
if (urlBar.value.trim() === '') {
const currentUrl = webview.getURL();
if (currentUrl.endsWith('pages/home.html')) {
urlBar.value = 'Type URL Here';
} }
} }
}); });
urlBar.addEventListener('keydown', (e) => { backButton.addEventListener('click', () => {
if (e.key === 'Enter') { window.electronAPI.goBack();
let url = urlBar.value.trim();
if (url === '' || url === 'Type URL Here') {
return;
}
// Simple check to add https:// if it's missing
if (!url.startsWith('http://') && !url.startsWith('https://')) {
url = 'https://' + url;
}
webview.loadURL(url);
}
}); });
function updateUrlBar(url) { forwardButton.addEventListener('click', () => {
if (url && url.endsWith('pages/home.html')) { window.electronAPI.goForward();
urlBar.value = 'Type URL Here';
} else {
urlBar.value = url;
}
updateBookmarkButton();
}
// Update URL bar and bookmark button when webview navigates
webview.addEventListener('did-navigate', (e) => {
updateUrlBar(e.url);
}); });
webview.addEventListener('did-navigate-in-page', (e) => { refreshButton.addEventListener('click', () => {
updateUrlBar(e.url); window.electronAPI.refreshPage();
}); });
// Update bookmark button when URL bar changes
urlBar.addEventListener('input', updateBookmarkButton);
// Burger menu links // You can add more logic here, e.g., to update the address bar
document.getElementById('settings-link').addEventListener('click', (e) => { // when the BrowserView navigates to a new URL. This requires
e.preventDefault(); // IPC communication from the main process to the renderer.
webview.src = 'pages/settings.html'; // We'll leave that as a potential enhancement for later.
urlBar.value = ''; });
menuDropdown.classList.add('hidden');
});
};
+55
View File
@@ -0,0 +1,55 @@
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
display: flex;
flex-direction: column;
height: 100vh;
overflow: hidden; /* Prevent body scroll, BrowserView will handle its own scroll */
background-color: #f0f0f0;
}
.navbar {
display: flex;
align-items: center;
padding: 10px;
background-color: #333;
color: white;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
height: 60px; /* Adjust based on navBarHeight in main.js */
flex-shrink: 0; /* Prevent navbar from shrinking */
}
.navbar button {
background-color: #555;
color: white;
border: none;
padding: 8px 15px;
margin: 0 5px;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
transition: background-color 0.2s ease;
}
.navbar button:hover {
background-color: #777;
}
.navbar button:active {
background-color: #222;
}
.navbar #address-bar {
flex-grow: 1;
padding: 8px 10px;
border: 1px solid #777;
border-radius: 4px;
font-size: 16px;
background-color: #444;
color: white;
outline: none;
}
.navbar #address-bar:focus {
border-color: #007bff;
}