Folder-Browser für Workspace-Auswahl, Layout/Style Anpassungen
This commit is contained in:
1 parent
f4ad949db4
commit
aa2d9f1200
6 files changed
+179
-23
No files matched your search
@@ -46,16 +46,7 @@
|
||||
}
|
||||
|
||||
.fe-btn {
|
||||
background: var(--f3-bg);
|
||||
border: var(--border-width) solid var(--f3-border);
|
||||
padding: var(--s1) var(--s2);
|
||||
cursor: pointer;
|
||||
color: var(--text);
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.fe-btn:hover:not(:disabled) {
|
||||
border-color: var(--primary);
|
||||
}
|
||||
|
||||
.fe-btn:disabled {
|
||||
@@ -78,6 +69,10 @@
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.fe-sidebar-full {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.fe-main {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
@@ -118,10 +113,6 @@
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.fe-error {
|
||||
color: #ff6b6b;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* Preview */
|
||||
|
||||
@@ -200,12 +191,3 @@
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.fe-select-btn {
|
||||
background: var(--primary);
|
||||
border-color: var(--primary);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.fe-select-btn:hover {
|
||||
opacity: 0.85;
|
||||
}
|
||||
@@ -32,6 +32,20 @@
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.input-with-btn {
|
||||
display: flex;
|
||||
gap: var(--s2);
|
||||
}
|
||||
|
||||
.input-with-btn input {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.input-addon-btn {
|
||||
flex-shrink: 0;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
.form-actions {
|
||||
display: flex;
|
||||
gap: var(--s4);
|
||||
|
||||
+4
-1
@@ -69,7 +69,10 @@
|
||||
<label>Kontext</label>
|
||||
<textarea id="sessionContext" rows="4" placeholder="Zusätzlicher Kontext für den System-Prompt (optional)"></textarea>
|
||||
<label>Workspace</label>
|
||||
<input type="text" id="sessionWorkspace" placeholder="Absoluter Pfad zum Arbeitsverzeichnis (optional)">
|
||||
<div class="input-with-btn">
|
||||
<input type="text" id="sessionWorkspace" placeholder="Absoluter Pfad zum Arbeitsverzeichnis (optional)">
|
||||
<button type="button" id="sessionWorkspaceBrowse" class="input-addon-btn">📂</button>
|
||||
</div>
|
||||
<div class="form-actions">
|
||||
<button id="sessionSaveBtn">Speichern</button>
|
||||
<button id="sessionCancelBtn">Abbrechen</button>
|
||||
|
||||
@@ -258,6 +258,114 @@ function feDoSelect() {
|
||||
closeFileExplorer();
|
||||
}
|
||||
|
||||
// ── Folder Browser Mode ──
|
||||
// Nutzt /api/workspace/browse mit absolutem Pfad, zum Ordner-Auswählen.
|
||||
|
||||
function openFolderBrowser(opts = {}) {
|
||||
if (_feState) closeFileExplorer();
|
||||
|
||||
_feState = {
|
||||
mode: 'folder',
|
||||
onSelect: opts.onSelect || null,
|
||||
currentAbsPath: opts.startPath || '/',
|
||||
history: []
|
||||
};
|
||||
|
||||
const overlay = document.createElement('div');
|
||||
overlay.id = 'feOverlay';
|
||||
overlay.className = 'fe-overlay';
|
||||
overlay.addEventListener('click', (e) => { if (e.target === overlay) closeFileExplorer(); });
|
||||
|
||||
const modal = document.createElement('div');
|
||||
modal.className = 'fe-modal';
|
||||
modal.innerHTML = `
|
||||
<div class="fe-header">
|
||||
<div class="fe-nav">
|
||||
<button class="fe-btn" id="feBack" title="Zurück" disabled>←</button>
|
||||
<span class="fe-path" id="fePath">/</span>
|
||||
</div>
|
||||
<button class="fe-btn" id="feClose">✕</button>
|
||||
</div>
|
||||
<div class="fe-body">
|
||||
<div class="fe-sidebar fe-sidebar-full" id="feSidebar">
|
||||
<div class="fe-loading">Laden...</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="fe-footer" id="feFooter">
|
||||
<span class="fe-info" id="feInfo"></span>
|
||||
<button class="fe-btn fe-select-btn" id="feSelectBtn">Diesen Ordner wählen</button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
overlay.appendChild(modal);
|
||||
document.body.appendChild(overlay);
|
||||
|
||||
document.getElementById('feBack').addEventListener('click', fbGoBack);
|
||||
document.getElementById('feClose').addEventListener('click', closeFileExplorer);
|
||||
document.getElementById('feSelectBtn').addEventListener('click', () => {
|
||||
if (_feState && _feState.onSelect) _feState.onSelect(_feState.currentAbsPath);
|
||||
closeFileExplorer();
|
||||
});
|
||||
|
||||
_feState._keyHandler = (e) => { if (e.key === 'Escape') closeFileExplorer(); };
|
||||
document.addEventListener('keydown', _feState._keyHandler);
|
||||
|
||||
fbLoadDir(opts.startPath || '/');
|
||||
}
|
||||
|
||||
async function fbLoadDir(absPath) {
|
||||
if (!_feState) return;
|
||||
|
||||
const pathEl = document.getElementById('fePath');
|
||||
const sidebar = document.getElementById('feSidebar');
|
||||
const info = document.getElementById('feInfo');
|
||||
|
||||
pathEl.textContent = absPath;
|
||||
info.textContent = absPath;
|
||||
sidebar.innerHTML = '<div class="fe-loading">Laden...</div>';
|
||||
|
||||
try {
|
||||
const resp = await fetch(`/api/workspace/browse?path=${encodeURIComponent(absPath)}`);
|
||||
if (!resp.ok) {
|
||||
const err = await resp.json().catch(() => ({}));
|
||||
sidebar.innerHTML = `<div class="fe-error">${err.error || 'Fehler'}</div>`;
|
||||
return;
|
||||
}
|
||||
|
||||
const data = await resp.json();
|
||||
_feState.currentAbsPath = data.current;
|
||||
pathEl.textContent = data.current;
|
||||
info.textContent = data.current;
|
||||
|
||||
sidebar.innerHTML = '';
|
||||
|
||||
for (const entry of data.entries) {
|
||||
const item = document.createElement('div');
|
||||
item.className = 'fe-item fe-dir';
|
||||
item.textContent = '📁 ' + entry.name;
|
||||
item.addEventListener('click', () => {
|
||||
_feState.history.push(_feState.currentAbsPath);
|
||||
fbLoadDir(entry.path);
|
||||
});
|
||||
sidebar.appendChild(item);
|
||||
}
|
||||
|
||||
if (data.entries.length === 0) {
|
||||
sidebar.innerHTML = '<div class="fe-empty">Leer</div>';
|
||||
}
|
||||
|
||||
document.getElementById('feBack').disabled = _feState.history.length === 0;
|
||||
} catch (e) {
|
||||
sidebar.innerHTML = '<div class="fe-error">Netzwerk-Fehler</div>';
|
||||
}
|
||||
}
|
||||
|
||||
function fbGoBack() {
|
||||
if (!_feState || _feState.history.length === 0) return;
|
||||
const prev = _feState.history.pop();
|
||||
fbLoadDir(prev);
|
||||
}
|
||||
|
||||
// ── Helpers ──
|
||||
|
||||
function getFileIcon(ext) {
|
||||
|
||||
@@ -79,6 +79,19 @@ registerPage('session', {
|
||||
imageInput.value = '';
|
||||
});
|
||||
|
||||
// Workspace Browse
|
||||
const workspaceBrowse = document.getElementById('sessionWorkspaceBrowse');
|
||||
if (workspaceBrowse) {
|
||||
workspaceBrowse.addEventListener('click', () => {
|
||||
openFolderBrowser({
|
||||
startPath: workspaceEl.value.trim() || '/',
|
||||
onSelect(path) {
|
||||
workspaceEl.value = path;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Speichern
|
||||
saveBtn.addEventListener('click', async () => {
|
||||
const data = {
|
||||
|
||||
Reference in new issue
Block a user