Use HTMX for app-guides, fix warnings
- Rewrote local-files.html to use HTMX instead of vanilla JS - Rewrote native-settings.html to use HTMX with CSS-only toggles - Added allow(dead_code) for tray.rs (prepared for future use) - Minimal JS approach: all interactions via hx-* attributes
This commit is contained in:
parent
64e11506a2
commit
3e4616dce1
3 changed files with 346 additions and 384 deletions
|
|
@ -2,6 +2,8 @@
|
||||||
//!
|
//!
|
||||||
//! Provides system tray icon and menu for desktop platforms.
|
//! Provides system tray icon and menu for desktop platforms.
|
||||||
|
|
||||||
|
#![allow(dead_code)] // Prepared for future use
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tokio::sync::RwLock;
|
use tokio::sync::RwLock;
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,80 @@
|
||||||
<!DOCTYPE html>
|
<div class="app-guide local-files">
|
||||||
<html lang="en">
|
<header class="guide-header">
|
||||||
<head>
|
<h1>📁 Local Files</h1>
|
||||||
<meta charset="UTF-8">
|
<p>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
Access and manage files on your device using native file system
|
||||||
<title>Local Files - General Bots</title>
|
access.
|
||||||
|
</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<section class="file-browser">
|
||||||
|
<div class="path-bar">
|
||||||
|
<button
|
||||||
|
hx-get="/app/api/files/parent"
|
||||||
|
hx-target="#file-list"
|
||||||
|
hx-include="#currentPath"
|
||||||
|
title="Go up one level"
|
||||||
|
>
|
||||||
|
⬆️
|
||||||
|
</button>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="currentPath"
|
||||||
|
name="path"
|
||||||
|
placeholder="Enter path..."
|
||||||
|
hx-get="/app/api/files/list"
|
||||||
|
hx-target="#file-list"
|
||||||
|
hx-trigger="keyup[key=='Enter']"
|
||||||
|
hx-indicator="#loading"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
hx-get="/app/api/files/list"
|
||||||
|
hx-target="#file-list"
|
||||||
|
hx-include="#currentPath"
|
||||||
|
hx-indicator="#loading"
|
||||||
|
>
|
||||||
|
Browse
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
hx-get="/app/api/files/home"
|
||||||
|
hx-target="#currentPath"
|
||||||
|
hx-swap="outerHTML"
|
||||||
|
hx-on::after-request="htmx.trigger('#currentPath', 'keyup', {key: 'Enter'})"
|
||||||
|
>
|
||||||
|
🏠 Home
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="loading" class="htmx-indicator loading">Loading...</div>
|
||||||
|
|
||||||
|
<div class="file-list" id="file-list">
|
||||||
|
<div class="empty-state">
|
||||||
|
<span class="icon">📂</span>
|
||||||
|
<p>Click "Home" or enter a path to browse files</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="file-actions">
|
||||||
|
<button
|
||||||
|
hx-get="/app/api/files/new-folder-dialog"
|
||||||
|
hx-target="#modal-container"
|
||||||
|
hx-swap="innerHTML"
|
||||||
|
>
|
||||||
|
📁 New Folder
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
hx-get="/app/api/files/list"
|
||||||
|
hx-target="#file-list"
|
||||||
|
hx-include="#currentPath"
|
||||||
|
hx-indicator="#loading"
|
||||||
|
>
|
||||||
|
🔄 Refresh
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<div id="modal-container"></div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.local-files {
|
.local-files {
|
||||||
padding: 1.5rem;
|
padding: 1.5rem;
|
||||||
|
|
@ -145,182 +216,17 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
padding: 2rem;
|
padding: 1rem;
|
||||||
color: var(--fg-muted, #888);
|
color: var(--fg-muted, #888);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.htmx-indicator {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.htmx-request .htmx-indicator,
|
||||||
|
.htmx-request.htmx-indicator {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</div>
|
||||||
<body>
|
|
||||||
<div class="app-guide local-files">
|
|
||||||
<header class="guide-header">
|
|
||||||
<h1>📁 Local Files</h1>
|
|
||||||
<p>Access and manage files on your device using native file system access.</p>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<section class="file-browser">
|
|
||||||
<div class="path-bar">
|
|
||||||
<button onclick="goUp()" title="Go up one level">⬆️</button>
|
|
||||||
<input type="text" id="currentPath" placeholder="Enter path..." onkeydown="if(event.key==='Enter')loadFiles()">
|
|
||||||
<button onclick="loadFiles()">Browse</button>
|
|
||||||
<button onclick="goHome()">🏠 Home</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="file-list" id="fileList">
|
|
||||||
<div class="empty-state">
|
|
||||||
<span class="icon">📂</span>
|
|
||||||
<p>Click "Home" or enter a path to browse files</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="file-actions">
|
|
||||||
<button onclick="createFolder()">📁 New Folder</button>
|
|
||||||
<button onclick="refreshFiles()">🔄 Refresh</button>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
// Check if running in Tauri
|
|
||||||
const isTauri = window.__TAURI__ !== undefined;
|
|
||||||
|
|
||||||
async function invoke(cmd, args) {
|
|
||||||
if (isTauri && window.__TAURI__.core) {
|
|
||||||
return await window.__TAURI__.core.invoke(cmd, args);
|
|
||||||
} else {
|
|
||||||
console.warn('Not running in Tauri - file operations unavailable');
|
|
||||||
throw new Error('Native file access requires the desktop app');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function goHome() {
|
|
||||||
try {
|
|
||||||
const homePath = await invoke('get_home_dir', {});
|
|
||||||
document.getElementById('currentPath').value = homePath;
|
|
||||||
await loadFiles();
|
|
||||||
} catch (e) {
|
|
||||||
showError(e.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function goUp() {
|
|
||||||
const pathInput = document.getElementById('currentPath');
|
|
||||||
const currentPath = pathInput.value;
|
|
||||||
if (!currentPath) return;
|
|
||||||
|
|
||||||
const parts = currentPath.split(/[/\\]/);
|
|
||||||
parts.pop();
|
|
||||||
if (parts.length > 0) {
|
|
||||||
pathInput.value = parts.join('/') || '/';
|
|
||||||
await loadFiles();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function loadFiles() {
|
|
||||||
const pathInput = document.getElementById('currentPath');
|
|
||||||
const path = pathInput.value || '/';
|
|
||||||
const listEl = document.getElementById('fileList');
|
|
||||||
|
|
||||||
listEl.innerHTML = '<div class="loading">Loading...</div>';
|
|
||||||
|
|
||||||
try {
|
|
||||||
const files = await invoke('list_files', { path });
|
|
||||||
|
|
||||||
if (files.length === 0) {
|
|
||||||
listEl.innerHTML = `
|
|
||||||
<div class="empty-state">
|
|
||||||
<span class="icon">📭</span>
|
|
||||||
<p>This folder is empty</p>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
listEl.innerHTML = files.map(f => `
|
|
||||||
<div class="file-item ${f.is_dir ? 'folder' : 'file'}"
|
|
||||||
onclick="${f.is_dir ? `navigateTo('${f.path.replace(/\\/g, '\\\\').replace(/'/g, "\\'")}')` : ''}">
|
|
||||||
<span class="icon">${f.is_dir ? '📁' : getFileIcon(f.name)}</span>
|
|
||||||
<span class="name">${escapeHtml(f.name)}</span>
|
|
||||||
<span class="size">${f.is_dir ? '' : formatSize(f.size)}</span>
|
|
||||||
</div>
|
|
||||||
`).join('');
|
|
||||||
} catch (e) {
|
|
||||||
listEl.innerHTML = `
|
|
||||||
<div class="empty-state">
|
|
||||||
<span class="icon">⚠️</span>
|
|
||||||
<p>${escapeHtml(e.message || 'Failed to load files')}</p>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function navigateTo(path) {
|
|
||||||
document.getElementById('currentPath').value = path;
|
|
||||||
loadFiles();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function createFolder() {
|
|
||||||
const path = document.getElementById('currentPath').value;
|
|
||||||
if (!path) {
|
|
||||||
showError('Please navigate to a directory first');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const name = prompt('Enter folder name:');
|
|
||||||
if (name) {
|
|
||||||
try {
|
|
||||||
await invoke('create_folder', { path, name });
|
|
||||||
await loadFiles();
|
|
||||||
} catch (e) {
|
|
||||||
showError(e.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function refreshFiles() {
|
|
||||||
loadFiles();
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatSize(bytes) {
|
|
||||||
if (bytes === null || bytes === undefined) return '';
|
|
||||||
if (bytes < 1024) return bytes + ' B';
|
|
||||||
if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB';
|
|
||||||
if (bytes < 1024 * 1024 * 1024) return (bytes / 1024 / 1024).toFixed(1) + ' MB';
|
|
||||||
return (bytes / 1024 / 1024 / 1024).toFixed(1) + ' GB';
|
|
||||||
}
|
|
||||||
|
|
||||||
function getFileIcon(filename) {
|
|
||||||
const ext = filename.split('.').pop().toLowerCase();
|
|
||||||
const icons = {
|
|
||||||
'pdf': '📕',
|
|
||||||
'doc': '📘', 'docx': '📘',
|
|
||||||
'xls': '📗', 'xlsx': '📗',
|
|
||||||
'ppt': '📙', 'pptx': '📙',
|
|
||||||
'jpg': '🖼️', 'jpeg': '🖼️', 'png': '🖼️', 'gif': '🖼️', 'svg': '🖼️',
|
|
||||||
'mp3': '🎵', 'wav': '🎵', 'flac': '🎵',
|
|
||||||
'mp4': '🎬', 'mkv': '🎬', 'avi': '🎬',
|
|
||||||
'zip': '📦', 'rar': '📦', 'tar': '📦', 'gz': '📦',
|
|
||||||
'js': '📜', 'ts': '📜', 'py': '📜', 'rs': '📜',
|
|
||||||
'html': '🌐', 'css': '🎨',
|
|
||||||
'json': '📋', 'xml': '📋', 'yaml': '📋', 'yml': '📋',
|
|
||||||
'txt': '📄', 'md': '📄',
|
|
||||||
};
|
|
||||||
return icons[ext] || '📄';
|
|
||||||
}
|
|
||||||
|
|
||||||
function escapeHtml(text) {
|
|
||||||
const div = document.createElement('div');
|
|
||||||
div.textContent = text;
|
|
||||||
return div.innerHTML;
|
|
||||||
}
|
|
||||||
|
|
||||||
function showError(message) {
|
|
||||||
alert('Error: ' + message);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Auto-load home directory on init if in Tauri
|
|
||||||
if (isTauri) {
|
|
||||||
goHome();
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,212 @@
|
||||||
<!DOCTYPE html>
|
<div class="app-guide native-settings">
|
||||||
<html lang="en">
|
<header class="guide-header">
|
||||||
<head>
|
<h1></h1>⚙️ App Settings</h1>
|
||||||
<meta charset="UTF-8">
|
<p>Configure native desktop application preferences</p>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
</header>
|
||||||
<title>App Settings - General Bots</title>
|
|
||||||
|
<form
|
||||||
|
id="settings-form"
|
||||||
|
hx-post="/app/api/settings/save"
|
||||||
|
hx-target="#save-status"
|
||||||
|
hx-swap="innerHTML"
|
||||||
|
hx-indicator="#saving"
|
||||||
|
>
|
||||||
|
<section class="settings-section">
|
||||||
|
<h2>General</h2>
|
||||||
|
|
||||||
|
<div class="setting-item">
|
||||||
|
<div class="setting-info">
|
||||||
|
<label for="startOnLogin" class="setting-label"
|
||||||
|
>Start on login</label
|
||||||
|
>
|
||||||
|
<div class="setting-description">
|
||||||
|
Automatically start General Bots when you log in
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="setting-control">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
id="startOnLogin"
|
||||||
|
name="startOnLogin"
|
||||||
|
class="toggle-input"
|
||||||
|
/>
|
||||||
|
<label for="startOnLogin" class="toggle-switch"></label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="setting-item">
|
||||||
|
<div class="setting-info">
|
||||||
|
<label for="minimizeToTray" class="setting-label"
|
||||||
|
>Minimize to tray</label
|
||||||
|
>
|
||||||
|
<div class="setting-description">
|
||||||
|
Keep running in system tray when window is closed
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="setting-control">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
id="minimizeToTray"
|
||||||
|
name="minimizeToTray"
|
||||||
|
class="toggle-input"
|
||||||
|
checked
|
||||||
|
/>
|
||||||
|
<label for="minimizeToTray" class="toggle-switch"></label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="setting-item">
|
||||||
|
<div class="setting-info">
|
||||||
|
<label class="setting-label">Default download location</label>
|
||||||
|
<div class="setting-description">
|
||||||
|
Where to save downloaded files
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="setting-control">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="downloadPath"
|
||||||
|
name="downloadPath"
|
||||||
|
readonly
|
||||||
|
placeholder="Select folder..."
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn btn-secondary"
|
||||||
|
hx-get="/app/api/settings/select-download-path"
|
||||||
|
hx-target="#downloadPath"
|
||||||
|
hx-swap="outerHTML"
|
||||||
|
>
|
||||||
|
Browse...
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="settings-section">
|
||||||
|
<h2>Server Connection</h2>
|
||||||
|
|
||||||
|
<div class="setting-item">
|
||||||
|
<div class="setting-info">
|
||||||
|
<label for="serverUrl" class="setting-label">Server URL</label>
|
||||||
|
<div class="setting-description">BotServer API endpoint</div>
|
||||||
|
</div>
|
||||||
|
<div class="setting-control">
|
||||||
|
<input
|
||||||
|
type="url"
|
||||||
|
id="serverUrl"
|
||||||
|
name="serverUrl"
|
||||||
|
value="http://localhost:8080"
|
||||||
|
hx-post="/app/api/settings/test-connection"
|
||||||
|
hx-target="#connection-status"
|
||||||
|
hx-trigger="blur changed"
|
||||||
|
hx-indicator="#testing-connection"
|
||||||
|
/>
|
||||||
|
<span id="testing-connection" class="htmx-indicator">⏳</span>
|
||||||
|
<span id="connection-status"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="setting-item">
|
||||||
|
<div class="setting-info">
|
||||||
|
<label for="autoReconnect" class="setting-label"
|
||||||
|
>Auto-reconnect</label
|
||||||
|
>
|
||||||
|
<div class="setting-description">
|
||||||
|
Automatically reconnect when connection is lost
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="setting-control">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
id="autoReconnect"
|
||||||
|
name="autoReconnect"
|
||||||
|
class="toggle-input"
|
||||||
|
checked
|
||||||
|
/>
|
||||||
|
<label for="autoReconnect" class="toggle-switch"></label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="settings-section">
|
||||||
|
<h2>Notifications</h2>
|
||||||
|
|
||||||
|
<div class="setting-item">
|
||||||
|
<div class="setting-info">
|
||||||
|
<label for="desktopNotifications" class="setting-label"
|
||||||
|
>Desktop notifications</label
|
||||||
|
>
|
||||||
|
<div class="setting-description">
|
||||||
|
Show native desktop notifications
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="setting-control">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
id="desktopNotifications"
|
||||||
|
name="desktopNotifications"
|
||||||
|
class="toggle-input"
|
||||||
|
checked
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
for="desktopNotifications"
|
||||||
|
class="toggle-switch"
|
||||||
|
></label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="setting-item">
|
||||||
|
<div class="setting-info">
|
||||||
|
<label for="soundAlerts" class="setting-label"
|
||||||
|
>Sound alerts</label
|
||||||
|
>
|
||||||
|
<div class="setting-description">
|
||||||
|
Play sound when receiving messages
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="setting-control">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
id="soundAlerts"
|
||||||
|
name="soundAlerts"
|
||||||
|
class="toggle-input"
|
||||||
|
/>
|
||||||
|
<label for="soundAlerts" class="toggle-switch"></label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<div class="actions">
|
||||||
|
<button type="submit" class="btn btn-primary">
|
||||||
|
<span class="btn-text">Save Settings</span>
|
||||||
|
<span id="saving" class="htmx-indicator">Saving...</span>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn btn-secondary"
|
||||||
|
hx-get="/app/api/settings/defaults"
|
||||||
|
hx-target="#settings-form"
|
||||||
|
hx-swap="outerHTML"
|
||||||
|
>
|
||||||
|
Reset to Defaults
|
||||||
|
</button>
|
||||||
|
<span id="save-status"></span>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="version-info"
|
||||||
|
hx-get="/app/api/settings/version"
|
||||||
|
hx-trigger="load"
|
||||||
|
hx-swap="innerHTML"
|
||||||
|
>
|
||||||
|
<strong>General Bots App</strong><br />
|
||||||
|
Loading version info...
|
||||||
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.app-guide {
|
.native-settings {
|
||||||
padding: 2rem;
|
padding: 2rem;
|
||||||
max-width: 800px;
|
max-width: 800px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
|
|
@ -55,6 +256,8 @@
|
||||||
.setting-label {
|
.setting-label {
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
margin-bottom: 0.25rem;
|
margin-bottom: 0.25rem;
|
||||||
|
display: block;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.setting-description {
|
.setting-description {
|
||||||
|
|
@ -64,6 +267,14 @@
|
||||||
|
|
||||||
.setting-control {
|
.setting-control {
|
||||||
margin-left: 1rem;
|
margin-left: 1rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Toggle switch using CSS only */
|
||||||
|
.toggle-input {
|
||||||
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.toggle-switch {
|
.toggle-switch {
|
||||||
|
|
@ -76,12 +287,8 @@
|
||||||
transition: background 0.2s;
|
transition: background 0.2s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.toggle-switch.active {
|
|
||||||
background: var(--accent-color, #4CAF50);
|
|
||||||
}
|
|
||||||
|
|
||||||
.toggle-switch::after {
|
.toggle-switch::after {
|
||||||
content: '';
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 2px;
|
top: 2px;
|
||||||
left: 2px;
|
left: 2px;
|
||||||
|
|
@ -92,16 +299,21 @@
|
||||||
transition: transform 0.2s;
|
transition: transform 0.2s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.toggle-switch.active::after {
|
.toggle-input:checked + .toggle-switch {
|
||||||
|
background: var(--accent-color, #4caf50);
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-input:checked + .toggle-switch::after {
|
||||||
transform: translateX(24px);
|
transform: translateX(24px);
|
||||||
}
|
}
|
||||||
|
|
||||||
select, input[type="text"], input[type="number"] {
|
input[type="text"],
|
||||||
|
input[type="url"] {
|
||||||
padding: 0.5rem;
|
padding: 0.5rem;
|
||||||
border: 1px solid var(--border-color, #ddd);
|
border: 1px solid var(--border-color, #ddd);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
font-size: 0.875rem;
|
font-size: 0.875rem;
|
||||||
min-width: 150px;
|
min-width: 200px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn {
|
.btn {
|
||||||
|
|
@ -118,7 +330,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-primary {
|
.btn-primary {
|
||||||
background: var(--accent-color, #2196F3);
|
background: var(--accent-color, #2196f3);
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -130,11 +342,17 @@
|
||||||
.actions {
|
.actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 0.5rem;
|
gap: 0.5rem;
|
||||||
|
align-items: center;
|
||||||
margin-top: 2rem;
|
margin-top: 2rem;
|
||||||
padding-top: 1rem;
|
padding-top: 1rem;
|
||||||
border-top: 1px solid var(--border-color, #e0e0e0);
|
border-top: 1px solid var(--border-color, #e0e0e0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#save-status {
|
||||||
|
margin-left: 1rem;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
}
|
||||||
|
|
||||||
.version-info {
|
.version-info {
|
||||||
margin-top: 2rem;
|
margin-top: 2rem;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
|
|
@ -143,190 +361,26 @@
|
||||||
font-size: 0.875rem;
|
font-size: 0.875rem;
|
||||||
color: var(--text-secondary, #666);
|
color: var(--text-secondary, #666);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.htmx-indicator {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.htmx-request .htmx-indicator,
|
||||||
|
.htmx-request.htmx-indicator {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.htmx-request .btn-text {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#connection-status.success {
|
||||||
|
color: #4caf50;
|
||||||
|
}
|
||||||
|
|
||||||
|
#connection-status.error {
|
||||||
|
color: #f44336;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</div>
|
||||||
<body>
|
|
||||||
<div class="app-guide native-settings">
|
|
||||||
<header class="guide-header">
|
|
||||||
<h1>App Settings</h1>
|
|
||||||
<p>Configure native desktop application preferences</p>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<section class="settings-section">
|
|
||||||
<h2>General</h2>
|
|
||||||
|
|
||||||
<div class="setting-item">
|
|
||||||
<div class="setting-info">
|
|
||||||
<div class="setting-label">Start on login</div>
|
|
||||||
<div class="setting-description">Automatically start General Bots when you log in</div>
|
|
||||||
</div>
|
|
||||||
<div class="setting-control">
|
|
||||||
<div class="toggle-switch" id="startOnLogin" onclick="toggleSetting(this)"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="setting-item">
|
|
||||||
<div class="setting-info">
|
|
||||||
<div class="setting-label">Minimize to tray</div>
|
|
||||||
<div class="setting-description">Keep running in system tray when window is closed</div>
|
|
||||||
</div>
|
|
||||||
<div class="setting-control">
|
|
||||||
<div class="toggle-switch active" id="minimizeToTray" onclick="toggleSetting(this)"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="setting-item">
|
|
||||||
<div class="setting-info">
|
|
||||||
<div class="setting-label">Default download location</div>
|
|
||||||
<div class="setting-description">Where to save downloaded files</div>
|
|
||||||
</div>
|
|
||||||
<div class="setting-control">
|
|
||||||
<button class="btn btn-secondary" onclick="selectDownloadPath()">Browse...</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="settings-section">
|
|
||||||
<h2>Server Connection</h2>
|
|
||||||
|
|
||||||
<div class="setting-item">
|
|
||||||
<div class="setting-info">
|
|
||||||
<div class="setting-label">Server URL</div>
|
|
||||||
<div class="setting-description">BotServer API endpoint</div>
|
|
||||||
</div>
|
|
||||||
<div class="setting-control">
|
|
||||||
<input type="text" id="serverUrl" value="http://localhost:8080" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="setting-item">
|
|
||||||
<div class="setting-info">
|
|
||||||
<div class="setting-label">Auto-reconnect</div>
|
|
||||||
<div class="setting-description">Automatically reconnect when connection is lost</div>
|
|
||||||
</div>
|
|
||||||
<div class="setting-control">
|
|
||||||
<div class="toggle-switch active" id="autoReconnect" onclick="toggleSetting(this)"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="settings-section">
|
|
||||||
<h2>Notifications</h2>
|
|
||||||
|
|
||||||
<div class="setting-item">
|
|
||||||
<div class="setting-info">
|
|
||||||
<div class="setting-label">Desktop notifications</div>
|
|
||||||
<div class="setting-description">Show native desktop notifications</div>
|
|
||||||
</div>
|
|
||||||
<div class="setting-control">
|
|
||||||
<div class="toggle-switch active" id="desktopNotifications" onclick="toggleSetting(this)"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="setting-item">
|
|
||||||
<div class="setting-info">
|
|
||||||
<div class="setting-label">Sound alerts</div>
|
|
||||||
<div class="setting-description">Play sound when receiving messages</div>
|
|
||||||
</div>
|
|
||||||
<div class="setting-control">
|
|
||||||
<div class="toggle-switch" id="soundAlerts" onclick="toggleSetting(this)"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<div class="actions">
|
|
||||||
<button class="btn btn-primary" onclick="saveSettings()">Save Settings</button>
|
|
||||||
<button class="btn btn-secondary" onclick="resetSettings()">Reset to Defaults</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="version-info">
|
|
||||||
<strong>General Bots App</strong><br>
|
|
||||||
Version: <span id="appVersion">6.1.0</span><br>
|
|
||||||
Platform: <span id="appPlatform">-</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
// Toggle switch functionality
|
|
||||||
function toggleSetting(element) {
|
|
||||||
element.classList.toggle('active');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Select download path via Tauri dialog
|
|
||||||
async function selectDownloadPath() {
|
|
||||||
if (window.__TAURI__) {
|
|
||||||
try {
|
|
||||||
const { open } = window.__TAURI__.dialog;
|
|
||||||
const selected = await open({
|
|
||||||
directory: true,
|
|
||||||
multiple: false,
|
|
||||||
title: 'Select Download Location'
|
|
||||||
});
|
|
||||||
if (selected) {
|
|
||||||
console.log('Selected path:', selected);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error('Failed to open dialog:', e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save settings
|
|
||||||
async function saveSettings() {
|
|
||||||
const settings = {
|
|
||||||
startOnLogin: document.getElementById('startOnLogin').classList.contains('active'),
|
|
||||||
minimizeToTray: document.getElementById('minimizeToTray').classList.contains('active'),
|
|
||||||
serverUrl: document.getElementById('serverUrl').value,
|
|
||||||
autoReconnect: document.getElementById('autoReconnect').classList.contains('active'),
|
|
||||||
desktopNotifications: document.getElementById('desktopNotifications').classList.contains('active'),
|
|
||||||
soundAlerts: document.getElementById('soundAlerts').classList.contains('active')
|
|
||||||
};
|
|
||||||
|
|
||||||
// Save to localStorage for now
|
|
||||||
localStorage.setItem('botapp_settings', JSON.stringify(settings));
|
|
||||||
|
|
||||||
// Show confirmation
|
|
||||||
if (window.BotServerApp) {
|
|
||||||
window.BotServerApp.showNotification('Settings saved successfully', 'success');
|
|
||||||
} else {
|
|
||||||
alert('Settings saved!');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset settings to defaults
|
|
||||||
function resetSettings() {
|
|
||||||
document.getElementById('startOnLogin').classList.remove('active');
|
|
||||||
document.getElementById('minimizeToTray').classList.add('active');
|
|
||||||
document.getElementById('serverUrl').value = 'http://localhost:8080';
|
|
||||||
document.getElementById('autoReconnect').classList.add('active');
|
|
||||||
document.getElementById('desktopNotifications').classList.add('active');
|
|
||||||
document.getElementById('soundAlerts').classList.remove('active');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load settings on page load
|
|
||||||
function loadSettings() {
|
|
||||||
const saved = localStorage.getItem('botapp_settings');
|
|
||||||
if (saved) {
|
|
||||||
const settings = JSON.parse(saved);
|
|
||||||
|
|
||||||
if (settings.startOnLogin) document.getElementById('startOnLogin').classList.add('active');
|
|
||||||
if (!settings.minimizeToTray) document.getElementById('minimizeToTray').classList.remove('active');
|
|
||||||
if (settings.serverUrl) document.getElementById('serverUrl').value = settings.serverUrl;
|
|
||||||
if (!settings.autoReconnect) document.getElementById('autoReconnect').classList.remove('active');
|
|
||||||
if (!settings.desktopNotifications) document.getElementById('desktopNotifications').classList.remove('active');
|
|
||||||
if (settings.soundAlerts) document.getElementById('soundAlerts').classList.add('active');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set platform info
|
|
||||||
if (window.__TAURI__) {
|
|
||||||
document.getElementById('appPlatform').textContent = navigator.platform;
|
|
||||||
} else {
|
|
||||||
document.getElementById('appPlatform').textContent = 'Web (not in app)';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize
|
|
||||||
loadSettings();
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue