refactor: Consolidate settings and dependencies into single modal

Merged the two gear icons in the header bar into one unified Settings modal.
Added a "Tools" tab to display dependency status, removing the separate
dependencies modal and button.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Smittix
2026-01-28 20:30:08 +00:00
parent 003b44c62e
commit 52cb47e5c9
3 changed files with 124 additions and 140 deletions

View File

@@ -391,6 +391,99 @@ function switchSettingsTab(tabName) {
document.querySelectorAll('.settings-section').forEach(section => {
section.classList.toggle('active', section.id === `settings-${tabName}`);
});
// Load tools/dependencies when that tab is selected
if (tabName === 'tools') {
loadSettingsTools();
}
}
/**
* Load tool dependencies into settings modal
*/
function loadSettingsTools() {
const content = document.getElementById('settingsToolsContent');
if (!content) return;
content.innerHTML = '<div style="text-align: center; padding: 30px; color: var(--text-dim);">Loading dependencies...</div>';
fetch('/dependencies')
.then(r => r.json())
.then(data => {
if (data.status !== 'success') {
content.innerHTML = '<div style="color: var(--accent-red);">Error loading dependencies</div>';
return;
}
let html = '';
let totalMissing = 0;
for (const [modeKey, mode] of Object.entries(data.modes)) {
const statusColor = mode.ready ? 'var(--accent-green)' : 'var(--accent-red)';
const statusIcon = mode.ready ? '✓' : '✗';
html += `
<div style="background: var(--bg-tertiary); border-radius: 6px; padding: 12px; margin-bottom: 10px; border-left: 3px solid ${statusColor};">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px;">
<span style="font-weight: 600; color: var(--accent-cyan); font-size: 13px;">${mode.name}</span>
<span style="color: ${statusColor}; font-size: 11px; font-weight: bold;">${statusIcon} ${mode.ready ? 'Ready' : 'Missing'}</span>
</div>
<div style="display: grid; gap: 6px;">
`;
for (const [toolName, tool] of Object.entries(mode.tools)) {
const installed = tool.installed;
const dotColor = installed ? 'var(--accent-green)' : 'var(--accent-red)';
const requiredBadge = tool.required ? '<span style="background: var(--accent-orange); color: #000; padding: 1px 4px; border-radius: 3px; font-size: 9px; margin-left: 4px;">REQ</span>' : '';
if (!installed) totalMissing++;
let installCmd = '';
if (tool.install) {
if (tool.install.pip) {
installCmd = tool.install.pip;
} else if (data.pkg_manager && tool.install[data.pkg_manager]) {
installCmd = tool.install[data.pkg_manager];
} else if (tool.install.manual) {
installCmd = tool.install.manual;
}
}
html += `
<div style="display: flex; align-items: center; gap: 8px; padding: 6px 8px; background: var(--bg-secondary); border-radius: 4px; font-size: 11px;">
<span style="color: ${dotColor}; font-size: 12px;">●</span>
<div style="flex: 1; min-width: 0;">
<span style="font-weight: 500;">${toolName}${requiredBadge}</span>
<div style="font-size: 10px; color: var(--text-dim); white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">${tool.description}</div>
</div>
${!installed && installCmd ? `
<code style="font-size: 9px; background: var(--bg-tertiary); padding: 2px 6px; border-radius: 3px; max-width: 150px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;" title="${installCmd}">${installCmd}</code>
` : ''}
<span style="font-size: 10px; color: ${dotColor}; font-weight: bold; min-width: 45px; text-align: right;">${installed ? 'OK' : 'MISSING'}</span>
</div>
`;
}
html += '</div></div>';
}
// Summary at top
const summaryHtml = `
<div style="background: ${totalMissing > 0 ? 'rgba(255, 100, 0, 0.1)' : 'rgba(0, 255, 100, 0.1)'}; border: 1px solid ${totalMissing > 0 ? 'var(--accent-orange)' : 'var(--accent-green)'}; border-radius: 6px; padding: 10px 12px; margin-bottom: 12px;">
<div style="font-size: 13px; font-weight: bold; color: ${totalMissing > 0 ? 'var(--accent-orange)' : 'var(--accent-green)'};">
${totalMissing > 0 ? '⚠️ ' + totalMissing + ' tool(s) not found' : '✓ All tools installed'}
</div>
<div style="font-size: 11px; color: var(--text-dim); margin-top: 3px;">
OS: ${data.os} | Package Manager: ${data.pkg_manager}
</div>
</div>
`;
content.innerHTML = summaryHtml + html;
})
.catch(err => {
content.innerHTML = '<div style="color: var(--accent-red);">Error loading dependencies: ' + err.message + '</div>';
});
}
// Initialize settings on page load

View File

@@ -385,8 +385,6 @@
<span class="icon-moon icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></svg></span>
<span class="icon-sun icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/></svg></span>
</button>
<button class="nav-tool-btn" onclick="showDependencies()" title="Check Tool Dependencies"
id="depsBtn"><span class="icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg></span></button>
<a href="/controller/monitor" class="nav-tool-btn" title="Network Monitor - Multi-Agent View" style="text-decoration: none;"><span class="icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg></span></a>
<a href="/controller/manage" class="nav-tool-btn" title="Manage Remote Agents" style="text-decoration: none;"><span class="icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="2" width="20" height="8" rx="2" ry="2"/><rect x="2" y="14" width="20" height="8" rx="2" ry="2"/><line x1="6" y1="6" x2="6.01" y2="6"/><line x1="6" y1="18" x2="6.01" y2="18"/></svg></span></a>
<button class="nav-tool-btn" onclick="showSettings()" title="Settings"><span class="icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg></span></button>
@@ -11686,38 +11684,6 @@
</div>
</div>
<!-- Dependencies Modal -->
<div id="depsModal" class="help-modal" onclick="if(event.target === this) hideDependencies()">
<div class="help-content" style="max-width: 800px;">
<button class="help-close" onclick="hideDependencies()">×</button>
<h2>Tool Dependencies</h2>
<p style="color: var(--text-dim); margin-bottom: 15px;">Check which tools are installed for each mode. <span
style="color: var(--accent-green);"></span> = Installed, <span
style="color: var(--accent-red);"></span> = Missing</p>
<div id="depsContent" style="max-height: 60vh; overflow-y: auto;">
<div style="text-align: center; padding: 40px; color: var(--text-dim);">
Loading dependencies...
</div>
</div>
<div style="margin-top: 20px; padding-top: 15px; border-top: 1px solid var(--border-color);">
<h3 style="margin-bottom: 10px;">Quick Install (Debian/Ubuntu)</h3>
<div
style="background: var(--bg-tertiary); padding: 10px; border-radius: 4px; font-family: monospace; font-size: 11px; overflow-x: auto;">
<div>sudo apt install rtl-sdr multimon-ng rtl-433 aircrack-ng bluez dump1090-mutability hcxdumptool
hcxtools</div>
<div style="margin-top: 5px;">pip install skyfield flask</div>
</div>
<div style="margin-top: 10px; font-size: 11px; color: var(--text-dim);">
<strong>Note:</strong> ACARS decoding requires <code>acarsdec</code> which must be built from
source.
See <a href="https://github.com/TLeconte/acarsdec" target="_blank"
style="color: var(--accent-cyan);">github.com/TLeconte/acarsdec</a> or run
<code>./setup.sh</code> for automated installation.
</div>
</div>
</div>
</div>
<!-- Satellite Add Modal -->
<div id="satModal" class="help-modal" onclick="if(event.target === this) closeSatModal()">
<div class="help-content" style="max-width: 600px;">
@@ -11779,105 +11745,6 @@
</div>
<script>
function showDependencies() {
document.getElementById('depsModal').classList.add('active');
loadDependencies();
}
function hideDependencies() {
document.getElementById('depsModal').classList.remove('active');
}
function loadDependencies() {
const content = document.getElementById('depsContent');
content.innerHTML = '<div style="text-align: center; padding: 40px; color: var(--text-dim);">Loading dependencies...</div>';
fetch('/dependencies')
.then(r => r.json())
.then(data => {
if (data.status !== 'success') {
content.innerHTML = '<div style="color: var(--accent-red);">Error loading dependencies</div>';
return;
}
let html = '';
let totalMissing = 0;
for (const [modeKey, mode] of Object.entries(data.modes)) {
const statusColor = mode.ready ? 'var(--accent-green)' : 'var(--accent-red)';
const statusIcon = mode.ready ? '✓' : '✗';
html += `
<div style="background: var(--bg-tertiary); border-radius: 8px; padding: 15px; margin-bottom: 15px; border-left: 3px solid ${statusColor};">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px;">
<h3 style="margin: 0; color: var(--accent-cyan);">${mode.name}</h3>
<span style="color: ${statusColor}; font-weight: bold;">${statusIcon} ${mode.ready ? 'Ready' : 'Missing Required'}</span>
</div>
<div style="display: grid; gap: 8px;">
`;
for (const [toolName, tool] of Object.entries(mode.tools)) {
const installed = tool.installed;
const dotColor = installed ? 'var(--accent-green)' : 'var(--accent-red)';
const requiredBadge = tool.required ? '<span style="background: var(--accent-orange); color: #000; padding: 1px 5px; border-radius: 3px; font-size: 9px; margin-left: 5px;">REQUIRED</span>' : '';
if (!installed) totalMissing++;
// Get install command for current OS
let installCmd = '';
if (tool.install) {
if (tool.install.pip) {
installCmd = tool.install.pip;
} else if (data.pkg_manager && tool.install[data.pkg_manager]) {
installCmd = tool.install[data.pkg_manager];
} else if (tool.install.manual) {
installCmd = tool.install.manual;
}
}
html += `
<div style="display: flex; align-items: center; gap: 10px; padding: 8px; background: var(--bg-secondary); border-radius: 4px;">
<span style="color: ${dotColor}; font-size: 16px;">●</span>
<div style="flex: 1;">
<div style="font-weight: bold;">${toolName}${requiredBadge}</div>
<div style="font-size: 11px; color: var(--text-dim);">${tool.description}</div>
</div>
${!installed && installCmd ? `
<code style="font-size: 10px; background: var(--bg-tertiary); padding: 4px 8px; border-radius: 3px; max-width: 250px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;" title="${installCmd}">${installCmd}</code>
` : ''}
<span style="font-size: 11px; color: ${dotColor}; font-weight: bold;">${installed ? 'OK' : 'MISSING'}</span>
</div>
`;
}
html += '</div></div>';
}
// Summary at top
const summaryHtml = `
<div style="background: ${totalMissing > 0 ? 'rgba(255, 100, 0, 0.1)' : 'rgba(0, 255, 100, 0.1)'}; border: 1px solid ${totalMissing > 0 ? 'var(--accent-orange)' : 'var(--accent-green)'}; border-radius: 8px; padding: 15px; margin-bottom: 20px;">
<div style="font-size: 16px; font-weight: bold; color: ${totalMissing > 0 ? 'var(--accent-orange)' : 'var(--accent-green)'};">
${totalMissing > 0 ? '⚠️ ' + totalMissing + ' tool(s) not found' : '✓ All tools installed'}
</div>
<div style="font-size: 12px; color: var(--text-dim); margin-top: 5px;">
OS: ${data.os} | Package Manager: ${data.pkg_manager}
</div>
</div>
`;
content.innerHTML = summaryHtml + html;
// Update button indicator
const btn = document.getElementById('depsBtn');
if (btn) {
btn.style.color = totalMissing > 0 ? 'var(--accent-orange)' : 'var(--accent-green)';
}
})
.catch(err => {
content.innerHTML = '<div style="color: var(--accent-red);">Error loading dependencies: ' + err.message + '</div>';
});
}
// Check dependencies on page load
document.addEventListener('DOMContentLoaded', function () {
// Check if user dismissed the startup check
@@ -11902,12 +11769,6 @@
}
}
const btn = document.getElementById('depsBtn');
if (btn && missingModes > 0) {
btn.style.color = 'var(--accent-orange)';
btn.title = missingModes + ' mode(s) have missing tools - click to see details';
}
// Show startup prompt if tools are missing and not dismissed
// Only show if disclaimer has been accepted
const disclaimerAccepted = localStorage.getItem('disclaimerAccepted') === 'true';
@@ -11951,7 +11812,7 @@
<strong style="color: var(--accent-orange);">${modeCount} mode(s)</strong> require tools that aren't installed.
</p>
<div style="display: flex; flex-direction: column; gap: 8px;">
<button class="action-btn" onclick="closeStartupDeps(); showDependencies();" style="padding: 10px 16px; font-size: 12px;">
<button class="action-btn" onclick="closeStartupDeps(); showSettings(); switchSettingsTab('tools');" style="padding: 10px 16px; font-size: 12px;">
View Details & Install
</button>
<label style="display: flex; align-items: center; gap: 8px; font-size: 11px; color: var(--text-dim); cursor: pointer;">

View File

@@ -12,6 +12,7 @@
<div class="settings-tabs">
<button class="settings-tab active" data-tab="offline" onclick="switchSettingsTab('offline')">Offline</button>
<button class="settings-tab" data-tab="display" onclick="switchSettingsTab('display')">Display</button>
<button class="settings-tab" data-tab="tools" onclick="switchSettingsTab('tools')">Tools</button>
<button class="settings-tab" data-tab="about" onclick="switchSettingsTab('about')">About</button>
</div>
@@ -146,6 +147,35 @@
</div>
</div>
<!-- Tools Section -->
<div id="settings-tools" class="settings-section">
<div class="settings-group">
<div class="settings-group-title">Tool Dependencies</div>
<p style="color: var(--text-dim); margin-bottom: 15px; font-size: 12px;">
Check which external tools are installed for each mode.
<span style="color: var(--accent-green);"></span> = Installed,
<span style="color: var(--accent-red);"></span> = Missing
</p>
<div id="settingsToolsContent" style="max-height: 45vh; overflow-y: auto;">
<div style="text-align: center; padding: 30px; color: var(--text-dim);">
Loading dependencies...
</div>
</div>
</div>
<div class="settings-group" style="margin-top: 15px;">
<div class="settings-group-title">Quick Install (Debian/Ubuntu)</div>
<div style="background: var(--bg-tertiary); padding: 10px; border-radius: 4px; font-family: var(--font-mono); font-size: 10px; overflow-x: auto;">
<div>sudo apt install rtl-sdr multimon-ng rtl-433 aircrack-ng bluez dump1090-mutability hcxdumptool hcxtools</div>
<div style="margin-top: 5px;">pip install skyfield flask</div>
</div>
<div style="margin-top: 10px; font-size: 11px; color: var(--text-dim);">
<strong>Note:</strong> ACARS decoding requires <code>acarsdec</code> which must be built from source.
See <a href="https://github.com/TLeconte/acarsdec" target="_blank" style="color: var(--accent-cyan);">github.com/TLeconte/acarsdec</a> or run <code>./setup.sh</code> for automated installation.
</div>
</div>
</div>
<!-- About Section -->
<div id="settings-about" class="settings-section">
<div class="settings-group">