CofifoAIWO / static /browser.html
ogerly
Initial upload of CofifoAIWO-huggingface
87d538c
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>File Browser - CofifoAIWO</title>
<style>
:root {
--primary-color: #3498db;
--secondary-color: #2980b9;
--accent-color: #e74c3c;
--text-color: #333;
--light-bg: #f5f5f5;
--border-color: #ddd;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
line-height: 1.6;
color: var(--text-color);
background-color: var(--light-bg);
}
#app {
display: flex;
flex-direction: column;
min-height: 100vh;
}
header {
background-color: #fff;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.navbar {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem 2rem;
max-width: 1200px;
margin: 0 auto;
width: 100%;
}
.navbar-brand {
display: flex;
flex-direction: column;
}
.navbar-brand h1 {
color: var(--primary-color);
font-size: 1.8rem;
margin-bottom: 0.2rem;
}
.subtitle {
font-size: 0.9rem;
color: #666;
}
.navbar-menu {
display: flex;
gap: 1.5rem;
}
.nav-link {
color: var(--text-color);
text-decoration: none;
font-weight: 500;
padding: 0.5rem 0;
border-bottom: 2px solid transparent;
transition: all 0.3s ease;
}
.nav-link:hover, .active {
color: var(--primary-color);
border-bottom: 2px solid var(--primary-color);
}
main {
flex: 1;
padding: 2rem;
max-width: 1200px;
margin: 0 auto;
width: 100%;
}
footer {
background-color: #fff;
text-align: center;
padding: 1rem;
border-top: 1px solid var(--border-color);
margin-top: auto;
}
footer a {
color: var(--primary-color);
text-decoration: none;
}
/* Allgemeine Komponenten-Stile */
.btn {
display: inline-block;
background-color: var(--primary-color);
color: white;
padding: 0.6rem 1.2rem;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 1rem;
transition: background-color 0.3s ease;
text-decoration: none;
}
.btn:hover {
background-color: var(--secondary-color);
}
.btn-secondary {
background-color: #95a5a6;
}
.btn-secondary:hover {
background-color: #7f8c8d;
}
.btn-danger {
background-color: var(--accent-color);
}
.btn-danger:hover {
background-color: #c0392b;
}
.btn-sm {
padding: 0.3rem 0.6rem;
font-size: 0.9rem;
}
.card {
background-color: #fff;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
padding: 1.5rem;
margin-bottom: 1.5rem;
}
.form-group {
margin-bottom: 1rem;
}
.form-group label {
display: block;
margin-bottom: 0.5rem;
font-weight: 500;
}
.form-control {
width: 100%;
padding: 0.6rem;
font-size: 1rem;
border: 1px solid var(--border-color);
border-radius: 4px;
}
.alert {
padding: 1rem;
border-radius: 4px;
margin-bottom: 1rem;
}
.alert-success {
background-color: #d4edda;
color: #155724;
}
.alert-danger {
background-color: #f8d7da;
color: #721c24;
}
/* Browser Styles */
.path-navigation {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1rem;
padding-bottom: 1rem;
border-bottom: 1px solid var(--border-color);
}
.current-path {
font-family: monospace;
background-color: var(--light-bg);
padding: 0.5rem;
border-radius: 4px;
flex: 1;
margin-right: 1rem;
word-break: break-all;
}
.directory-content {
margin-bottom: 1rem;
}
.entries-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 0.8rem;
margin-bottom: 1rem;
}
.entry {
display: flex;
align-items: center;
padding: 0.6rem;
border-radius: 4px;
border: 1px solid var(--border-color);
background-color: white;
cursor: pointer;
transition: background-color 0.2s;
}
.entry:hover {
background-color: var(--light-bg);
}
.entry.selected {
background-color: #e3f2fd;
border-color: var(--primary-color);
}
.entry-icon {
margin-right: 0.5rem;
font-size: 1.2rem;
}
.entry-name {
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.entry-actions {
margin-left: auto;
}
.selected-files-list {
max-height: 300px;
overflow-y: auto;
margin-bottom: 1rem;
border: 1px solid var(--border-color);
border-radius: 4px;
}
.selected-file {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0.6rem 1rem;
border-bottom: 1px solid var(--border-color);
}
.selected-file:last-child {
border-bottom: none;
}
.file-path {
font-family: monospace;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.selection-actions {
display: flex;
gap: 1rem;
}
.loading, .error, .empty-directory, .empty-selection {
padding: 1rem;
text-align: center;
color: #666;
background-color: var(--light-bg);
border-radius: 4px;
}
.error {
color: var(--accent-color);
}
/* Modal Styles */
.modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
}
.modal-content {
background-color: white;
border-radius: 8px;
width: 90%;
max-width: 500px;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
padding: 1rem;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
padding-bottom: 1rem;
border-bottom: 1px solid var(--border-color);
}
.close-btn {
background: none;
border: none;
font-size: 1.5rem;
cursor: pointer;
color: #666;
}
@media (max-width: 768px) {
.navbar {
flex-direction: column;
padding: 1rem;
}
.navbar-menu {
margin-top: 1rem;
}
main {
padding: 1rem;
}
.path-navigation {
flex-direction: column;
align-items: flex-start;
gap: 0.5rem;
}
.entries-grid {
grid-template-columns: 1fr;
}
.selection-actions {
flex-direction: column;
}
}
</style>
</head>
<body>
<div id="app">
<header>
<nav class="navbar">
<div class="navbar-brand">
<h1>CofifoAIWO</h1>
<p class="subtitle">Combine Files for AI Workflows</p>
</div>
<div class="navbar-menu">
<a href="/" class="nav-link">Home</a>
<a href="/shortcuts" class="nav-link">Shortcuts</a>
<a href="/browser" class="nav-link active">File Browser</a>
</div>
</nav>
</header>
<main>
<div class="file-browser">
<h2>Datei-Browser</h2>
<div id="notification" class="alert" style="display: none;"></div>
<div class="card">
<div class="path-navigation">
<div class="current-path">
<strong>Aktueller Pfad:</strong> <span id="current-path-display"></span>
</div>
<button id="navigate-up" class="btn btn-sm">⬆️ Ein Level nach oben</button>
</div>
<div class="directory-content">
<div id="loading" class="loading">
Lade Verzeichnisinhalt...
</div>
<div id="error" class="error" style="display: none;"></div>
<div id="entries-container" style="display: none;">
<div id="entries-grid" class="entries-grid"></div>
<div id="empty-directory" class="empty-directory" style="display: none;">
Dieses Verzeichnis ist leer.
</div>
</div>
</div>
</div>
<div class="card">
<h3>Ausgewählte Dateien</h3>
<div id="empty-selection" class="empty-selection">
<p>Keine Dateien ausgewählt. Klicke auf Dateien im Browser, um sie auszuwählen.</p>
</div>
<div id="selected-files-container" style="display: none;">
<ul id="selected-files-list" class="selected-files-list"></ul>
<div class="selection-actions">
<button id="save-as-shortcut" class="btn">Als Shortcut speichern</button>
<button id="generate-output" class="btn">Textdatei generieren</button>
</div>
</div>
</div>
</div>
<!-- "Als Shortcut speichern" Modal -->
<div id="save-shortcut-modal" class="modal">
<div class="modal-content">
<div class="modal-header">
<h3>Als Shortcut speichern</h3>
<button class="close-btn">&times;</button>
</div>
<div class="modal-body">
<form id="create-shortcut-form">
<div class="form-group">
<label for="shortcutName">Shortcut-Name:</label>
<input
type="text"
id="shortcutName"
class="form-control"
required
placeholder="z.B. my_project"
>
</div>
<button type="submit" class="btn">Speichern</button>
<button type="button" class="btn btn-secondary modal-cancel">Abbrechen</button>
</form>
</div>
</div>
</div>
<!-- "Ausgabedatei generieren" Modal -->
<div id="generate-modal" class="modal">
<div class="modal-content">
<div class="modal-header">
<h3>Textdatei generieren</h3>
<button class="close-btn">&times;</button>
</div>
<div class="modal-body">
<form id="generate-form">
<div class="form-group">
<label for="outputFile">Ausgabedatei:</label>
<input
type="text"
id="outputFile"
class="form-control"
required
placeholder="output.txt"
>
</div>
<div class="form-group">
<input type="checkbox" id="includeTree">
<label for="includeTree">Verzeichnisstruktur einbeziehen (tree)</label>
</div>
<button type="submit" class="btn">Generieren</button>
<button type="button" class="btn btn-secondary modal-cancel">Abbrechen</button>
</form>
</div>
</div>
</div>
<!-- Download-Link (Modal) -->
<div id="download-modal" class="modal">
<div class="modal-content">
<div class="modal-header">
<h3>Datei erfolgreich generiert</h3>
<button class="close-btn">&times;</button>
</div>
<div class="modal-body">
<p>Deine Datei wurde erfolgreich erstellt!</p>
<p>
<a id="download-link" href="#" class="btn" download>Datei herunterladen</a>
</p>
</div>
</div>
</div>
</main>
<footer>
<p>&copy; 2023 CofifoAIWO - Entwickelt von <a href="https://github.com/ogerly" target="_blank">ogerly</a></p>
</footer>
</div>
<script>
// DOM-Elemente
const notificationEl = document.getElementById('notification');
const currentPathDisplay = document.getElementById('current-path-display');
const navigateUpBtn = document.getElementById('navigate-up');
const loadingEl = document.getElementById('loading');
const errorEl = document.getElementById('error');
const entriesContainer = document.getElementById('entries-container');
const entriesGrid = document.getElementById('entries-grid');
const emptyDirectoryEl = document.getElementById('empty-directory');
const emptySelectionEl = document.getElementById('empty-selection');
const selectedFilesContainer = document.getElementById('selected-files-container');
const selectedFilesList = document.getElementById('selected-files-list');
const saveAsShortcutBtn = document.getElementById('save-as-shortcut');
const generateOutputBtn = document.getElementById('generate-output');
// Modals
const saveShortcutModal = document.getElementById('save-shortcut-modal');
const generateModal = document.getElementById('generate-modal');
const downloadModal = document.getElementById('download-modal');
// Forms
const createShortcutForm = document.getElementById('create-shortcut-form');
const generateForm = document.getElementById('generate-form');
// Sonstige Elemente
const downloadLink = document.getElementById('download-link');
const shortcutNameInput = document.getElementById('shortcutName');
const outputFileInput = document.getElementById('outputFile');
const includeTreeCheckbox = document.getElementById('includeTree');
// State
let currentPath = '';
let selectedFiles = [];
// Close-Buttons für Modals
const closeButtons = document.querySelectorAll('.close-btn');
closeButtons.forEach(button => {
button.addEventListener('click', () => {
saveShortcutModal.style.display = 'none';
generateModal.style.display = 'none';
downloadModal.style.display = 'none';
});
});
const modalCancelButtons = document.querySelectorAll('.modal-cancel');
modalCancelButtons.forEach(button => {
button.addEventListener('click', () => {
saveShortcutModal.style.display = 'none';
generateModal.style.display = 'none';
});
});
// Funktion zum Anzeigen von Benachrichtigungen
function showNotification(type, message) {
notificationEl.textContent = message;
notificationEl.className = 'alert alert-' + type;
notificationEl.style.display = 'block';
// Benachrichtigung nach 5 Sekunden ausblenden
setTimeout(() => {
notificationEl.style.display = 'none';
}, 5000);
}
// Funktion zum Laden eines Verzeichnisses
async function loadDirectory(path) {
// Setze UI-Status auf "Laden"
loadingEl.style.display = 'block';
errorEl.style.display = 'none';
entriesContainer.style.display = 'none';
try {
const response = await fetch(`/api/browse?path=${encodeURIComponent(path)}`);
if (!response.ok) {
const data = await response.json();
throw new Error(data.error || 'Fehler beim Laden des Verzeichnisses');
}
const data = await response.json();
// Aktuellen Pfad aktualisieren
currentPath = data.path;
currentPathDisplay.textContent = currentPath;
// Einträge anzeigen
displayEntries(data.entries || []);
// UI-Status aktualisieren
loadingEl.style.display = 'none';
entriesContainer.style.display = 'block';
// Prüfen, ob das Verzeichnis leer ist
if (!data.entries || data.entries.length === 0) {
emptyDirectoryEl.style.display = 'block';
} else {
emptyDirectoryEl.style.display = 'none';
}
} catch (error) {
// Fehler anzeigen
loadingEl.style.display = 'none';
errorEl.textContent = error.message;
errorEl.style.display = 'block';
showNotification('danger', error.message);
}
}
// Funktion zum Anzeigen der Verzeichniseinträge
function displayEntries(entries) {
entriesGrid.innerHTML = '';
entries.forEach(entry => {
const entryEl = document.createElement('div');
entryEl.className = `entry ${entry.type}`;
if (isSelected(entry.path)) {
entryEl.classList.add('selected');
}
const iconEl = document.createElement('div');
iconEl.className = 'entry-icon';
iconEl.innerHTML = entry.type === 'directory' ? '📁' : '📄';
const nameEl = document.createElement('div');
nameEl.className = 'entry-name';
nameEl.textContent = entry.name;
entryEl.appendChild(iconEl);
entryEl.appendChild(nameEl);
if (entry.type === 'file') {
const actionsEl = document.createElement('div');
actionsEl.className = 'entry-actions';
const toggleBtn = document.createElement('button');
toggleBtn.className = 'btn btn-sm';
toggleBtn.textContent = isSelected(entry.path) ? '✓' : '+';
toggleBtn.addEventListener('click', (e) => {
e.stopPropagation();
toggleSelection(entry.path);
});
actionsEl.appendChild(toggleBtn);
entryEl.appendChild(actionsEl);
}
entryEl.addEventListener('click', () => {
if (entry.type === 'directory') {
loadDirectory(entry.path);
} else {
toggleSelection(entry.path);
}
});
entriesGrid.appendChild(entryEl);
});
}
// Funktion zum Ein Level nach oben Navigieren
function navigateUp() {
const parentPath = currentPath.split('/').slice(0, -1).join('/') || '/';
loadDirectory(parentPath);
}
// Funktion zum Überprüfen, ob ein Pfad ausgewählt ist
function isSelected(path) {
return selectedFiles.includes(path);
}
// Funktion zum Hinzufügen oder Entfernen eines Pfads aus der Auswahl
function toggleSelection(path) {
if (isSelected(path)) {
// Aus Auswahl entfernen
selectedFiles = selectedFiles.filter(file => file !== path);
} else {
// Zur Auswahl hinzufügen
selectedFiles.push(path);
}
// UI aktualisieren
updateSelectedFilesUI();
// Verzeichniseinträge aktualisieren (um Auswahlstatus zu aktualisieren)
const entries = Array.from(entriesGrid.querySelectorAll('.entry'));
entries.forEach(entry => {
const entryPath = entry.querySelector('.entry-name').textContent;
const fullPath = currentPath + '/' + entryPath;
if (isSelected(fullPath)) {
entry.classList.add('selected');
const toggleBtn = entry.querySelector('.btn');
if (toggleBtn) toggleBtn.textContent = '✓';
} else {
entry.classList.remove('selected');
const toggleBtn = entry.querySelector('.btn');
if (toggleBtn) toggleBtn.textContent = '+';
}
});
}
// Funktion zum Aktualisieren der UI für ausgewählte Dateien
function updateSelectedFilesUI() {
if (selectedFiles.length === 0) {
emptySelectionEl.style.display = 'block';
selectedFilesContainer.style.display = 'none';
} else {
emptySelectionEl.style.display = 'none';
selectedFilesContainer.style.display = 'block';
// Liste der ausgewählten Dateien aktualisieren
selectedFilesList.innerHTML = '';
selectedFiles.forEach(file => {
const listItem = document.createElement('li');
listItem.className = 'selected-file';
const filePathEl = document.createElement('span');
filePathEl.className = 'file-path';
filePathEl.textContent = file;
const removeBtn = document.createElement('button');
removeBtn.className = 'btn btn-sm btn-danger';
removeBtn.textContent = '×';
removeBtn.addEventListener('click', () => {
toggleSelection(file);
});
listItem.appendChild(filePathEl);
listItem.appendChild(removeBtn);
selectedFilesList.appendChild(listItem);
});
}
}
// Funktion zum Speichern als Shortcut
async function saveAsShortcut() {
if (selectedFiles.length === 0) {
showNotification('danger', 'Bitte wähle zuerst Dateien aus.');
return;
}
saveShortcutModal.style.display = 'block';
}
// Funktion zum Erstellen eines Shortcuts
async function createShortcut(event) {
event.preventDefault();
const name = shortcutNameInput.value.trim();
if (!name) {
showNotification('danger', 'Bitte gib einen Namen für den Shortcut ein.');
return;
}
try {
const response = await fetch('/api/add_shortcut', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
name,
paths: selectedFiles
})
});
if (!response.ok) {
const data = await response.json();
throw new Error(data.error || 'Fehler beim Erstellen des Shortcuts');
}
const result = await response.json();
// Modal schließen
saveShortcutModal.style.display = 'none';
// Formular zurücksetzen
shortcutNameInput.value = '';
showNotification('success', result.message);
} catch (error) {
showNotification('danger', error.message);
}
}
// Funktion zum Generieren einer Ausgabedatei
function showGenerateModal() {
if (selectedFiles.length === 0) {
showNotification('danger', 'Bitte wähle zuerst Dateien aus.');
return;
}
outputFileInput.value = 'output.txt';
includeTreeCheckbox.checked = false;
generateModal.style.display = 'block';
}
// Funktion zum Generieren einer Textdatei
async function generateTextFile(event) {
event.preventDefault();
const outputFile = outputFileInput.value.trim();
const includeTree = includeTreeCheckbox.checked;
if (!outputFile) {
showNotification('danger', 'Bitte gib einen Namen für die Ausgabedatei ein.');
return;
}
try {
// Erstelle einen temporären Shortcut für die ausgewählten Dateien
const tempShortcutName = `temp_${Date.now()}`;
await fetch('/api/add_shortcut', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: tempShortcutName,
paths: selectedFiles
})
});
// Verwende den temporären Shortcut, um die Textdatei zu generieren
const response = await fetch('/api/use_shortcut', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: tempShortcutName,
output_file: outputFile,
include_tree: includeTree
})
});
if (!response.ok) {
const data = await response.json();
throw new Error(data.error || 'Fehler beim Generieren der Textdatei');
}
const result = await response.json();
// Entferne den temporären Shortcut
await fetch('/api/remove_shortcut', {
method: 'DELETE',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: tempShortcutName
})
});
// Modal schließen
generateModal.style.display = 'none';
// Download-Link aktualisieren
downloadLink.href = `/api/download/${encodeURIComponent(result.output_file)}`;
// Download-Modal anzeigen
downloadModal.style.display = 'block';
} catch (error) {
showNotification('danger', error.message);
}
}
// Event-Listener
navigateUpBtn.addEventListener('click', navigateUp);
saveAsShortcutBtn.addEventListener('click', saveAsShortcut);
generateOutputBtn.addEventListener('click', showGenerateModal);
createShortcutForm.addEventListener('submit', createShortcut);
generateForm.addEventListener('submit', generateTextFile);
// Initialisiere die Anwendung
document.addEventListener('DOMContentLoaded', () => {
// Lade das aktuelle Verzeichnis
loadDirectory('/');
// Initialisiere die ausgewählten Dateien
updateSelectedFilesUI();
});
</script>
</body>
</html>