Spaces:
Running
Aanvullende eisen:
Browse files12. Automatisch afsluiten:
- Na een succesvolle conversie en validatie sluit het programma automatisch binnen 3 seconden.
- Toon kort een statusmelding ("Conversie voltooid — programma sluit nu automatisch.") voordat het venster sluit.
- Als er een fout optreedt, blijft het programma open en toont het een foutmelding met loglink.
13. Instellingen voor opslaan:
- Voeg een tab of sectie toe aan de GUI genaamd “Opslaginstellingen”.
- Gebruiker kan aanpassen:
• Standaard uitvoermap (output directory).
• Optie om elke conversie in een nieuwe submap te plaatsen (met tijdstempel).
• Of bestaande bestanden overschreven mogen worden.
• Of instellingen bij afsluiten opgeslagen moeten worden.
- Sla deze voorkeuren lokaal op in een JSON-bestand: `settings.json`.
- Laad deze instellingen automatisch bij het opstarten van de app.
- Als `settings.json` ontbreekt of corrupt is, genereer standaardwaarden.
- Toon huidige instellingen samenvatting in een collapsible panel onderaan het hoofdvenster.
14. Logging & afsluitgedrag:
- Schrijf elke conversie-log naar `logs/last_run.log`.
- Als het programma automatisch sluit, zorg dat het logbestand blijft bestaan in de outputmap of `logs/`.
- Voeg een instelling toe “Automatisch afsluiten na conversie (ja/nee)” in het instellingenmenu.
- index.html +196 -15
|
@@ -48,14 +48,88 @@
|
|
| 48 |
<button class="tab-btn px-6 py-4 font-medium text-gray-400 hover:text-white">
|
| 49 |
<i data-feather="terminal" class="mr-2"></i> CLI Mode
|
| 50 |
</button>
|
| 51 |
-
<button class="tab-btn px-6 py-4 font-medium text-gray-400 hover:text-white">
|
| 52 |
<i data-feather="settings" class="mr-2"></i> Settings
|
| 53 |
</button>
|
| 54 |
-
|
| 55 |
-
|
| 56 |
<!-- Content -->
|
| 57 |
-
<div class="p-8">
|
| 58 |
-
<!--
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 59 |
<div id="dropzone" class="dropzone rounded-lg p-12 text-center mb-8 cursor-pointer">
|
| 60 |
<div class="flex flex-col items-center justify-center space-y-4">
|
| 61 |
<i data-feather="upload-cloud" class="w-12 h-12 text-gray-400"></i>
|
|
@@ -192,17 +266,71 @@
|
|
| 192 |
<button id="cancelBtn" class="px-4 py-2 bg-gray-700 hover:bg-gray-600 rounded-lg mr-3">
|
| 193 |
Cancel
|
| 194 |
</button>
|
| 195 |
-
|
| 196 |
-
|
| 197 |
-
|
| 198 |
-
|
| 199 |
</div>
|
| 200 |
</div>
|
| 201 |
</div>
|
| 202 |
|
| 203 |
<script>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 204 |
// Initialize Vanta.js background
|
| 205 |
-
|
| 206 |
el: "#vanta-bg",
|
| 207 |
mouseControls: true,
|
| 208 |
touchControls: true,
|
|
@@ -221,19 +349,49 @@
|
|
| 221 |
// Initialize Feather Icons
|
| 222 |
document.addEventListener('DOMContentLoaded', function() {
|
| 223 |
feather.replace();
|
| 224 |
-
|
| 225 |
// Tab switching
|
| 226 |
const tabBtns = document.querySelectorAll('.tab-btn');
|
|
|
|
|
|
|
|
|
|
|
|
|
| 227 |
tabBtns.forEach(btn => {
|
| 228 |
btn.addEventListener('click', () => {
|
| 229 |
tabBtns.forEach(b => b.classList.remove('active', 'text-blue-400', 'border-blue-400'));
|
| 230 |
tabBtns.forEach(b => b.classList.add('text-gray-400'));
|
| 231 |
btn.classList.add('active', 'text-blue-400', 'border-blue-400');
|
| 232 |
btn.classList.remove('text-gray-400');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 233 |
});
|
| 234 |
});
|
| 235 |
-
|
| 236 |
-
//
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 237 |
const advancedToggle = document.getElementById('advancedToggle');
|
| 238 |
const advancedOptions = document.getElementById('advancedOptions');
|
| 239 |
advancedToggle.addEventListener('click', () => {
|
|
@@ -379,7 +537,6 @@
|
|
| 379 |
hideModalBtn.disabled = false;
|
| 380 |
return;
|
| 381 |
}
|
| 382 |
-
|
| 383 |
const step = steps[progress];
|
| 384 |
progressBar.style.width = `${step.percent}%`;
|
| 385 |
progressPercent.textContent = `${step.percent}%`;
|
|
@@ -389,8 +546,32 @@
|
|
| 389 |
progressLog.appendChild(logEntry);
|
| 390 |
progressLog.scrollTop = progressLog.scrollHeight;
|
| 391 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 392 |
progress++;
|
| 393 |
-
|
| 394 |
}
|
| 395 |
});
|
| 396 |
</script>
|
|
|
|
| 48 |
<button class="tab-btn px-6 py-4 font-medium text-gray-400 hover:text-white">
|
| 49 |
<i data-feather="terminal" class="mr-2"></i> CLI Mode
|
| 50 |
</button>
|
| 51 |
+
<button class="tab-btn px-6 py-4 font-medium text-gray-400 hover:text-white" data-tab="settings">
|
| 52 |
<i data-feather="settings" class="mr-2"></i> Settings
|
| 53 |
</button>
|
| 54 |
+
</div>
|
|
|
|
| 55 |
<!-- Content -->
|
| 56 |
+
<div class="p-8" id="tabContent">
|
| 57 |
+
<!-- Settings Tab Content -->
|
| 58 |
+
<div id="settingsTab" class="hidden">
|
| 59 |
+
<div class="space-y-6">
|
| 60 |
+
<h2 class="text-2xl font-bold mb-6">Application Settings</h2>
|
| 61 |
+
|
| 62 |
+
<div class="bg-gray-700/50 rounded-lg p-6">
|
| 63 |
+
<h3 class="text-lg font-semibold mb-4 flex items-center">
|
| 64 |
+
<i data-feather="save" class="mr-2"></i> Storage Settings
|
| 65 |
+
</h3>
|
| 66 |
+
|
| 67 |
+
<div class="space-y-4">
|
| 68 |
+
<div>
|
| 69 |
+
<label class="block text-sm font-medium mb-2">Default Output Directory</label>
|
| 70 |
+
<div class="flex">
|
| 71 |
+
<input type="text" id="outputDir" class="flex-1 bg-gray-700 border border-gray-600 rounded-l-lg px-4 py-2 focus:ring-2 focus:ring-blue-500 focus:border-blue-500" placeholder="Path to output directory">
|
| 72 |
+
<button id="browseDirBtn" class="px-4 py-2 bg-gray-600 hover:bg-gray-500 rounded-r-lg">
|
| 73 |
+
<i data-feather="folder" class="w-4 h-4"></i>
|
| 74 |
+
</button>
|
| 75 |
+
</div>
|
| 76 |
+
</div>
|
| 77 |
+
|
| 78 |
+
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
| 79 |
+
<div>
|
| 80 |
+
<label class="flex items-center">
|
| 81 |
+
<input type="checkbox" id="createSubdir" class="rounded bg-gray-700 border-gray-600 text-blue-500 focus:ring-blue-500 mr-2" checked>
|
| 82 |
+
<span>Create timestamped subdirectory for each conversion</span>
|
| 83 |
+
</label>
|
| 84 |
+
</div>
|
| 85 |
+
<div>
|
| 86 |
+
<label class="flex items-center">
|
| 87 |
+
<input type="checkbox" id="overwriteFiles" class="rounded bg-gray-700 border-gray-600 text-blue-500 focus:ring-blue-500 mr-2">
|
| 88 |
+
<span>Overwrite existing files</span>
|
| 89 |
+
</label>
|
| 90 |
+
</div>
|
| 91 |
+
<div>
|
| 92 |
+
<label class="flex items-center">
|
| 93 |
+
<input type="checkbox" id="saveSettings" class="rounded bg-gray-700 border-gray-600 text-blue-500 focus:ring-blue-500 mr-2" checked>
|
| 94 |
+
<span>Save settings on exit</span>
|
| 95 |
+
</label>
|
| 96 |
+
</div>
|
| 97 |
+
<div>
|
| 98 |
+
<label class="flex items-center">
|
| 99 |
+
<input type="checkbox" id="autoClose" class="rounded bg-gray-700 border-gray-600 text-blue-500 focus:ring-blue-500 mr-2" checked>
|
| 100 |
+
<span>Auto-close after successful conversion</span>
|
| 101 |
+
</label>
|
| 102 |
+
</div>
|
| 103 |
+
</div>
|
| 104 |
+
</div>
|
| 105 |
+
</div>
|
| 106 |
+
|
| 107 |
+
<div class="flex justify-end">
|
| 108 |
+
<button id="saveSettingsBtn" class="px-6 py-2.5 bg-gradient-to-r from-blue-600 to-purple-600 hover:from-blue-700 hover:to-purple-700 rounded-lg font-medium flex items-center justify-center">
|
| 109 |
+
<i data-feather="save" class="mr-2"></i> Save Settings
|
| 110 |
+
</button>
|
| 111 |
+
</div>
|
| 112 |
+
|
| 113 |
+
<!-- Current Settings Summary -->
|
| 114 |
+
<div class="bg-gray-700/50 rounded-lg p-4">
|
| 115 |
+
<button id="settingsSummaryToggle" class="flex items-center text-blue-400 hover:text-blue-300 w-full justify-between">
|
| 116 |
+
<span class="font-medium">Current Settings Summary</span>
|
| 117 |
+
<i data-feather="chevron-down" class="w-4 h-4"></i>
|
| 118 |
+
</button>
|
| 119 |
+
<div id="settingsSummary" class="hidden mt-4 text-sm space-y-2">
|
| 120 |
+
<div><span class="text-gray-400">Output Directory:</span> <span id="currentOutputDir"></span></div>
|
| 121 |
+
<div><span class="text-gray-400">Create Subdirectories:</span> <span id="currentSubdir"></span></div>
|
| 122 |
+
<div><span class="text-gray-400">Overwrite Files:</span> <span id="currentOverwrite"></span></div>
|
| 123 |
+
<div><span class="text-gray-400">Save Settings:</span> <span id="currentSaveSettings"></span></div>
|
| 124 |
+
<div><span class="text-gray-400">Auto-Close:</span> <span id="currentAutoClose"></span></div>
|
| 125 |
+
</div>
|
| 126 |
+
</div>
|
| 127 |
+
</div>
|
| 128 |
+
</div>
|
| 129 |
+
|
| 130 |
+
<!-- Convert Model Tab Content -->
|
| 131 |
+
<div id="convertTab">
|
| 132 |
+
<!-- Drop Zone -->
|
| 133 |
<div id="dropzone" class="dropzone rounded-lg p-12 text-center mb-8 cursor-pointer">
|
| 134 |
<div class="flex flex-col items-center justify-center space-y-4">
|
| 135 |
<i data-feather="upload-cloud" class="w-12 h-12 text-gray-400"></i>
|
|
|
|
| 266 |
<button id="cancelBtn" class="px-4 py-2 bg-gray-700 hover:bg-gray-600 rounded-lg mr-3">
|
| 267 |
Cancel
|
| 268 |
</button>
|
| 269 |
+
<button id="hideModalBtn" class="px-4 py-2 bg-blue-600 hover:bg-blue-700 rounded-lg disabled:opacity-50" disabled>
|
| 270 |
+
Close
|
| 271 |
+
</button>
|
| 272 |
+
</div>
|
| 273 |
</div>
|
| 274 |
</div>
|
| 275 |
</div>
|
| 276 |
|
| 277 |
<script>
|
| 278 |
+
// Settings management
|
| 279 |
+
function loadSettings() {
|
| 280 |
+
try {
|
| 281 |
+
const settings = JSON.parse(localStorage.getItem('ama_settings')) || {
|
| 282 |
+
outputDir: '',
|
| 283 |
+
createSubdir: true,
|
| 284 |
+
overwriteFiles: false,
|
| 285 |
+
saveSettings: true,
|
| 286 |
+
autoClose: true
|
| 287 |
+
};
|
| 288 |
+
|
| 289 |
+
document.getElementById('outputDir').value = settings.outputDir;
|
| 290 |
+
document.getElementById('createSubdir').checked = settings.createSubdir;
|
| 291 |
+
document.getElementById('overwriteFiles').checked = settings.overwriteFiles;
|
| 292 |
+
document.getElementById('saveSettings').checked = settings.saveSettings;
|
| 293 |
+
document.getElementById('autoClose').checked = settings.autoClose;
|
| 294 |
+
|
| 295 |
+
updateSettingsSummary();
|
| 296 |
+
return settings;
|
| 297 |
+
} catch (e) {
|
| 298 |
+
console.error('Error loading settings:', e);
|
| 299 |
+
return {
|
| 300 |
+
outputDir: '',
|
| 301 |
+
createSubdir: true,
|
| 302 |
+
overwriteFiles: false,
|
| 303 |
+
saveSettings: true,
|
| 304 |
+
autoClose: true
|
| 305 |
+
};
|
| 306 |
+
}
|
| 307 |
+
}
|
| 308 |
+
|
| 309 |
+
function saveSettings() {
|
| 310 |
+
const settings = {
|
| 311 |
+
outputDir: document.getElementById('outputDir').value,
|
| 312 |
+
createSubdir: document.getElementById('createSubdir').checked,
|
| 313 |
+
overwriteFiles: document.getElementById('overwriteFiles').checked,
|
| 314 |
+
saveSettings: document.getElementById('saveSettings').checked,
|
| 315 |
+
autoClose: document.getElementById('autoClose').checked
|
| 316 |
+
};
|
| 317 |
+
|
| 318 |
+
localStorage.setItem('ama_settings', JSON.stringify(settings));
|
| 319 |
+
updateSettingsSummary();
|
| 320 |
+
return settings;
|
| 321 |
+
}
|
| 322 |
+
|
| 323 |
+
function updateSettingsSummary() {
|
| 324 |
+
const settings = JSON.parse(localStorage.getItem('ama_settings')) || {};
|
| 325 |
+
document.getElementById('currentOutputDir').textContent = settings.outputDir || 'Not set';
|
| 326 |
+
document.getElementById('currentSubdir').textContent = settings.createSubdir ? 'Yes' : 'No';
|
| 327 |
+
document.getElementById('currentOverwrite').textContent = settings.overwriteFiles ? 'Yes' : 'No';
|
| 328 |
+
document.getElementById('currentSaveSettings').textContent = settings.saveSettings ? 'Yes' : 'No';
|
| 329 |
+
document.getElementById('currentAutoClose').textContent = settings.autoClose ? 'Yes' : 'No';
|
| 330 |
+
}
|
| 331 |
+
|
| 332 |
// Initialize Vanta.js background
|
| 333 |
+
VANTA.WAVES({
|
| 334 |
el: "#vanta-bg",
|
| 335 |
mouseControls: true,
|
| 336 |
touchControls: true,
|
|
|
|
| 349 |
// Initialize Feather Icons
|
| 350 |
document.addEventListener('DOMContentLoaded', function() {
|
| 351 |
feather.replace();
|
|
|
|
| 352 |
// Tab switching
|
| 353 |
const tabBtns = document.querySelectorAll('.tab-btn');
|
| 354 |
+
const tabContent = document.getElementById('tabContent');
|
| 355 |
+
const convertTab = document.getElementById('convertTab');
|
| 356 |
+
const settingsTab = document.getElementById('settingsTab');
|
| 357 |
+
|
| 358 |
tabBtns.forEach(btn => {
|
| 359 |
btn.addEventListener('click', () => {
|
| 360 |
tabBtns.forEach(b => b.classList.remove('active', 'text-blue-400', 'border-blue-400'));
|
| 361 |
tabBtns.forEach(b => b.classList.add('text-gray-400'));
|
| 362 |
btn.classList.add('active', 'text-blue-400', 'border-blue-400');
|
| 363 |
btn.classList.remove('text-gray-400');
|
| 364 |
+
|
| 365 |
+
if (btn.dataset.tab === 'settings') {
|
| 366 |
+
convertTab.style.display = 'none';
|
| 367 |
+
settingsTab.style.display = 'block';
|
| 368 |
+
} else {
|
| 369 |
+
settingsTab.style.display = 'none';
|
| 370 |
+
convertTab.style.display = 'block';
|
| 371 |
+
}
|
| 372 |
});
|
| 373 |
});
|
| 374 |
+
|
| 375 |
+
// Settings summary toggle
|
| 376 |
+
document.getElementById('settingsSummaryToggle').addEventListener('click', () => {
|
| 377 |
+
const summary = document.getElementById('settingsSummary');
|
| 378 |
+
const icon = document.getElementById('settingsSummaryToggle').querySelector('i');
|
| 379 |
+
if (summary.classList.contains('hidden')) {
|
| 380 |
+
summary.classList.remove('hidden');
|
| 381 |
+
icon.setAttribute('data-feather', 'chevron-up');
|
| 382 |
+
} else {
|
| 383 |
+
summary.classList.add('hidden');
|
| 384 |
+
icon.setAttribute('data-feather', 'chevron-down');
|
| 385 |
+
}
|
| 386 |
+
feather.replace();
|
| 387 |
+
});
|
| 388 |
+
|
| 389 |
+
// Load settings on startup
|
| 390 |
+
loadSettings();
|
| 391 |
+
|
| 392 |
+
// Save settings button
|
| 393 |
+
document.getElementById('saveSettingsBtn').addEventListener('click', saveSettings);
|
| 394 |
+
// Advanced options toggle
|
| 395 |
const advancedToggle = document.getElementById('advancedToggle');
|
| 396 |
const advancedOptions = document.getElementById('advancedOptions');
|
| 397 |
advancedToggle.addEventListener('click', () => {
|
|
|
|
| 537 |
hideModalBtn.disabled = false;
|
| 538 |
return;
|
| 539 |
}
|
|
|
|
| 540 |
const step = steps[progress];
|
| 541 |
progressBar.style.width = `${step.percent}%`;
|
| 542 |
progressPercent.textContent = `${step.percent}%`;
|
|
|
|
| 546 |
progressLog.appendChild(logEntry);
|
| 547 |
progressLog.scrollTop = progressLog.scrollHeight;
|
| 548 |
|
| 549 |
+
// If this is the last step and auto-close is enabled
|
| 550 |
+
if (progress === steps.length - 1) {
|
| 551 |
+
const settings = JSON.parse(localStorage.getItem('ama_settings')) || {};
|
| 552 |
+
if (settings.autoClose) {
|
| 553 |
+
const completeMsg = document.createElement('div');
|
| 554 |
+
completeMsg.className = 'text-green-400';
|
| 555 |
+
completeMsg.textContent = '> Conversion complete — program will close automatically.';
|
| 556 |
+
progressLog.appendChild(completeMsg);
|
| 557 |
+
|
| 558 |
+
setTimeout(() => {
|
| 559 |
+
// In a real app, this would trigger the window close
|
| 560 |
+
// For demo purposes, we'll just hide the modal
|
| 561 |
+
progressModal.classList.add('hidden');
|
| 562 |
+
resetProgress();
|
| 563 |
+
|
| 564 |
+
// Save log to "last_run.log"
|
| 565 |
+
const logContent = Array.from(progressLog.children).map(el => el.textContent).join('\n');
|
| 566 |
+
console.log('Log content saved to last_run.log:\n', logContent);
|
| 567 |
+
}, 3000);
|
| 568 |
+
} else {
|
| 569 |
+
hideModalBtn.disabled = false;
|
| 570 |
+
}
|
| 571 |
+
}
|
| 572 |
+
|
| 573 |
progress++;
|
| 574 |
+
}, 1500);
|
| 575 |
}
|
| 576 |
});
|
| 577 |
</script>
|