feat(app): improved input handling + better UI

This commit is contained in:
SethBurkart123
2024-12-11 11:44:56 +11:00
parent 6209b65afe
commit 51c265400c
3 changed files with 292 additions and 92 deletions
+109 -35
View File
@@ -3,65 +3,122 @@
<head>
<title>BetterSEQTA Settings</title>
<style>
body {
:root {
--background-primary: #ffffff;
--background-secondary: #e5e7eb;
--text-primary: black;
--theme-primary: #4F46E5;
--theme-hover: #4338CA;
}
@media (prefers-color-scheme: dark) {
:root {
--background-primary: #232323;
--background-secondary: #1a1a1a;
--text-primary: white;
--theme-primary: #6366F1;
--theme-hover: #818CF8;
}
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
padding: 20px;
max-width: 600px;
margin: 0 auto;
background: #f5f5f5;
}
body {
background: var(--background-primary);
color: var(--text-primary);
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 2rem;
}
.container {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
width: 100%;
max-width: 100%;
display: flex;
flex-direction: column;
gap: 1rem;
}
h1 {
color: #333;
margin-bottom: 20px;
font-size: 1.75rem;
font-weight: 500;
margin-bottom: 0.25rem;
}
.form-group {
margin-bottom: 20px;
.subtitle {
color: #666;
font-size: 1rem;
margin-bottom: 1.5rem;
}
label {
display: block;
margin-bottom: 8px;
color: #555;
margin-bottom: 0.25rem;
font-weight: 500;
color: var(--text-primary);
}
input[type="url"] {
width: 100%;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 16px;
margin-bottom: 10px;
padding: 0.75rem 1rem;
border: 1px solid var(--background-secondary);
border-radius: 8px;
font-size: 1rem;
background: var(--background-secondary);
color: var(--text-primary);
transition: all 0.2s;
}
input[type="url"]:focus {
outline: none;
border-color: var(--theme-primary);
box-shadow: 0 0 0 2px rgba(79, 70, 229, 0.1);
}
button {
background: #4F46E5;
color: white;
margin-top: 1rem;
width: 100%;
padding: 0.75rem;
border: none;
padding: 10px 20px;
border-radius: 4px;
border-radius: 8px;
background: var(--theme-primary);
color: white;
font-size: 1rem;
font-weight: 500;
cursor: pointer;
font-size: 16px;
transition: all 0.2s;
}
button:hover {
background: #4338CA;
background: var(--theme-hover);
}
#error-message {
color: #EF4444;
font-size: 0.875rem;
margin-top: 0.5rem;
display: none;
}
</style>
</head>
<body>
<div class="container">
<h1>BetterSEQTA Settings</h1>
<div class="form-group">
<label for="seqtaUrl">SEQTA Website URL</label>
<input type="url" id="seqtaUrl" placeholder="https://your-school.seqta.com.au" required>
<button onclick="saveSettings()">Save Settings</button>
</div>
<div class="subtitle">It's time to get started! To begin type in your school's SEQTA URL below.</div>
<label for="seqtaUrl">SEQTA Website URL</label>
<input type="url" id="seqtaUrl" placeholder="Enter your school's SEQTA URL (e.g https://seqta.school.edu.au)" required>
<div id="error-message"></div>
<button onclick="saveSettings()">Save Settings</button>
</div>
<script>
// Access electron APIs through the contextBridge
const electron = window.require('electron');
const { ipcRenderer } = electron;
const Store = window.require('electron-store');
@@ -69,20 +126,37 @@
// Load saved URL on page load
window.addEventListener('DOMContentLoaded', () => {
console.log('Loading saved URL...');
const savedUrl = store.get('seqtaUrl') || '';
console.log('Saved URL:', savedUrl);
document.getElementById('seqtaUrl').value = savedUrl;
});
// Handle error messages from main process
ipcRenderer.on('seqta-url-error', (event, message) => {
const errorElement = document.getElementById('error-message');
errorElement.textContent = message;
errorElement.style.display = 'block';
});
// Save settings
function saveSettings() {
const url = document.getElementById('seqtaUrl').value;
console.log('Saving URL:', url);
const errorElement = document.getElementById('error-message');
errorElement.style.display = 'none';
if (url) {
ipcRenderer.send('set-seqta-url', url);
} else {
errorElement.textContent = 'Please enter a URL';
errorElement.style.display = 'block';
}
}
// Handle enter key
document.getElementById('seqtaUrl').addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
saveSettings();
}
});
</script>
</body>
</html>
+181 -55
View File
@@ -1,4 +1,4 @@
import { app, BrowserWindow, ipcMain, session } from 'electron';
import { app, BrowserWindow, ipcMain, session, Menu } from 'electron';
import path from 'path';
import { fileURLToPath } from 'url';
import Store from 'electron-store';
@@ -16,8 +16,74 @@ const customCSS = `
#alertBar {
display: none !important;
}
/* Match SEQTA's styling */
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif !important;
}
`;
// Create the application menu
function createAppMenu() {
const isMac = process.platform === 'darwin';
const template = [
...(isMac ? [{
label: app.name,
submenu: [
{ role: 'about' },
{ type: 'separator' },
{ role: 'services' },
{ type: 'separator' },
{ role: 'hide' },
{ role: 'hideOthers' },
{ role: 'unhide' },
{ type: 'separator' },
{ role: 'quit' }
]
}] : []),
{
label: 'Settings',
submenu: [
{
label: 'Configure SEQTA URL',
accelerator: isMac ? 'Cmd+,' : 'Ctrl+,',
click: () => {
createSettingsWindow();
}
}
]
},
{
label: 'View',
submenu: [
{ role: 'reload' },
{ role: 'forceReload' },
{ role: 'toggleDevTools' },
{ type: 'separator' },
{ role: 'resetZoom' },
{ role: 'zoomIn' },
{ role: 'zoomOut' },
{ type: 'separator' },
{ role: 'togglefullscreen' }
]
}
];
const menu = Menu.buildFromTemplate(template);
Menu.setApplicationMenu(menu);
}
// Validate SEQTA URL
function isValidSeqtaUrl(url) {
try {
const urlObj = new URL(url);
// Only ensure it's a valid HTTPS URL
return urlObj.protocol === 'https:';
} catch {
return false;
}
}
// Get the correct path for the extension based on whether we're in development or production
function getExtensionPath() {
if (app.isPackaged) {
@@ -45,37 +111,45 @@ async function loadExtension() {
}
function createMainWindow() {
console.log('🚀 Creating main window...');
if (mainWindow) {
if (!mainWindow.isDestroyed()) {
console.log('✨ Existing window found, focusing it');
mainWindow.focus();
return mainWindow;
}
console.log('🔄 Old window was destroyed, creating new one');
}
console.log('📦 Initializing new BrowserWindow');
mainWindow = new BrowserWindow({
width: 1200,
height: 800,
webPreferences: {
nodeIntegration: true,
contextIsolation: false,
// Performance optimizations
backgroundThrottling: false,
enableWebSQL: false,
webgl: false,
offscreen: false
},
// Performance optimizations
show: false, // Don't show until ready
show: false,
backgroundColor: '#ffffff'
});
const seqtaUrl = store.get('seqtaUrl');
console.log('📍 Stored SEQTA URL:', seqtaUrl);
// Optimize page loading
mainWindow.webContents.setWindowOpenHandler(({ url }) => {
// Open external links in browser instead of new electron window
if (url.startsWith('http')) {
require('electron').shell.openExternal(url);
return { action: 'deny' };
// Register keyboard shortcut for settings
mainWindow.webContents.on('before-input-event', (event, input) => {
if ((input.meta || input.control) && input.key === ',') {
createSettingsWindow();
}
return { action: 'allow' };
});
// Inject CSS when the page loads
mainWindow.webContents.on('did-finish-load', () => {
console.log('🎨 Page loaded, injecting CSS');
mainWindow.webContents.insertCSS(customCSS).catch(err => {
console.error('Failed to inject CSS:', err);
});
@@ -83,40 +157,35 @@ function createMainWindow() {
// Only show window when it's ready
mainWindow.once('ready-to-show', () => {
console.log('🎉 Window ready to show!');
mainWindow.show();
mainWindow.focus();
});
if (seqtaUrl) {
mainWindow.loadURL(seqtaUrl, {
// Performance optimizations for page loading
httpReferrer: seqtaUrl,
userAgent: 'Chrome',
cache: 'force-cache'
}).then(() => {
// Re-inject CSS after URL change
mainWindow.webContents.insertCSS(customCSS).catch(err => {
console.error('Failed to inject CSS after URL change:', err);
if (!isValidSeqtaUrl(seqtaUrl)) {
console.error('❌ Invalid SEQTA URL stored:', seqtaUrl);
createSettingsWindow();
return;
}
console.log('🌐 Loading SEQTA URL:', seqtaUrl);
mainWindow.loadURL(seqtaUrl)
.then(() => {
console.log('✅ Successfully loaded SEQTA URL');
mainWindow.show();
mainWindow.focus();
})
.catch(err => {
console.error('❌ Failed to load SEQTA URL:', err);
createSettingsWindow();
});
});
} else {
console.log('⚙️ No SEQTA URL found, opening settings');
createSettingsWindow();
}
// Optimize memory usage
mainWindow.on('minimize', () => {
if (process.platform === 'darwin') return; // Skip for macOS
mainWindow.webContents.setBackgroundThrottling(true);
});
mainWindow.on('restore', () => {
mainWindow.webContents.setBackgroundThrottling(false);
});
// Only enable DevTools in development
if (process.env.NODE_ENV === 'development') {
mainWindow.webContents.openDevTools();
}
return mainWindow;
}
function createSettingsWindow() {
@@ -131,10 +200,6 @@ function createSettingsWindow() {
webPreferences: {
nodeIntegration: true,
contextIsolation: false,
// Performance optimizations
backgroundThrottling: false,
enableWebSQL: false,
webgl: false
},
show: false,
backgroundColor: '#ffffff'
@@ -168,6 +233,7 @@ app.commandLine.appendSwitch('disable-smooth-scrolling');
// Wait for app to be ready before creating windows
app.whenReady().then(async () => {
createAppMenu();
await loadExtension();
createMainWindow();
});
@@ -183,25 +249,85 @@ app.on('activate', () => {
}
});
// Format and validate SEQTA URL
function formatAndValidateUrl(url) {
// Remove any whitespace
url = url.trim();
// If no protocol specified, add https://
if (!url.startsWith('http://') && !url.startsWith('https://')) {
url = 'https://' + url;
}
// If it's http://, upgrade to https://
if (url.startsWith('http://')) {
url = 'https://' + url.slice(7);
}
try {
const urlObj = new URL(url);
// Ensure it's https
if (urlObj.protocol !== 'https:') {
throw new Error('URL must use HTTPS');
}
return { isValid: true, url: url };
} catch (error) {
return { isValid: false, url: url, error: error.message };
}
}
// Handle setting the SEQTA URL
ipcMain.on('set-seqta-url', (event, url) => {
console.log('Setting SEQTA URL:', url);
store.set('seqtaUrl', url);
if (mainWindow) {
mainWindow.loadURL(url, {
httpReferrer: url,
userAgent: 'Chrome',
cache: 'force-cache'
}).then(() => {
// Re-inject CSS after URL change
mainWindow.webContents.insertCSS(customCSS).catch(err => {
console.error('Failed to inject CSS after URL change:', err);
});
});
} else {
createMainWindow();
console.log('🔧 Received new SEQTA URL:', url);
const { isValid, url: formattedUrl, error } = formatAndValidateUrl(url);
if (!isValid) {
console.error('❌ Invalid URL format:', error);
event.reply('seqta-url-error', 'Please enter a valid URL');
return;
}
if (settingsWindow) {
console.log('💾 Saving URL to store:', formattedUrl);
store.set('seqtaUrl', formattedUrl);
// Create main window if it doesn't exist
if (!mainWindow || mainWindow.isDestroyed()) {
console.log('🆕 Creating new main window');
createMainWindow();
} else {
console.log('🔄 Loading new URL in existing window:', formattedUrl);
mainWindow.loadURL(formattedUrl).then(() => {
console.log('✅ URL loaded successfully');
console.log('🎨 Injecting CSS and settings button');
mainWindow.webContents.insertCSS(customCSS).catch(err => {
console.error('Failed to inject CSS:', err);
});
mainWindow.webContents.executeJavaScript(`
if (!document.getElementById('bsp-settings-button')) {
document.body.insertAdjacentHTML('beforeend', ${JSON.stringify(settingsButtonHTML)});
document.getElementById('bsp-settings-button').addEventListener('click', () => {
window.postMessage('open-settings', '*');
});
}
`).catch(err => {
console.error('Failed to inject settings button:', err);
});
console.log('👀 Showing and focusing window');
mainWindow.show();
mainWindow.focus();
}).catch(err => {
console.error('❌ Failed to load SEQTA URL:', err);
event.reply('seqta-url-error', 'Failed to load SEQTA. Please check your connection and URL.');
});
}
// Close settings window if it exists
if (settingsWindow && !settingsWindow.isDestroyed()) {
console.log('🚪 Closing settings window');
settingsWindow.close();
}
});
+1 -1
View File
@@ -16,7 +16,7 @@
"release": "gh release create $npm_package_name@$npm_package_version ./dist/*.zip --generate-notes",
"publish": "bun lib/publish.js --b",
"zip": "bedframe zip",
"electron-dev": "npm run build:chrome && electron .",
"electron-dev": "electron .",
"electron-build": "electron-builder",
"electron-pack": "npm run build:chrome && electron-builder --dir",
"electron-dist": "npm run build:chrome && electron-builder"