mirror of
https://github.com/BetterSEQTA/BetterSEQTA-Plus.git
synced 2026-06-05 19:24:39 +00:00
feat(app): improved input handling + better UI
This commit is contained in:
+109
-35
@@ -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>
|
||||
+182
-56
@@ -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');
|
||||
|
||||
// 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' };
|
||||
console.log('📍 Stored SEQTA URL:', seqtaUrl);
|
||||
|
||||
// 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();
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user