feat: DefaultPath als Base-Path für User-Workspaces
- WorkspaceHelper.cs: Zentrale Workspace-Auflösung (relativ → absolut) - Session-Workspace wird relativ zum User-DefaultPath gespeichert - System-Prompt bekommt aufgelösten absoluten Pfad - Path-Traversal-Schutz: '..' in Workspace-Pfaden blockiert - Browse-API gibt '..' nicht mehr als Eintrag zurück - Folder-Browser: basePath-Limit, Back-Button navigiert zum Parent - DefaultPath nur durch Admin bearbeitbar (Profil: readonly) - Legacy: Absolute Pfade in session.Workspace weiterhin kompatibel
This commit is contained in:
1 parent
8e7bda50f9
commit
4e1ed81f37
12 files changed
+240
-106
No files matched your search
@@ -94,7 +94,7 @@
|
||||
color: var(--text);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: var(--badge-spacing);
|
||||
}
|
||||
|
||||
.fe-dir-label {
|
||||
|
||||
+1
-5
@@ -398,11 +398,7 @@
|
||||
<label>Anzeigename</label>
|
||||
<input type="text" id="profileDisplayName" placeholder="Anzeigename">
|
||||
<label>Standard-Pfad</label>
|
||||
<span class="setting-hint">Wird als Basis-Pfad beim Auswählen eines Workspace in Sessions verwendet.</span>
|
||||
<div class="input-with-btn">
|
||||
<input type="text" id="profileDefaultPath" placeholder="z.B. /home/user/projects" autocomplete="off">
|
||||
<button type="button" id="profileDefaultPathBrowse" class="icon-btn"><i class="fa fa-folder"></i></button>
|
||||
</div>
|
||||
<input type="text" id="profileDefaultPath" disabled placeholder="Wird vom Administrator festgelegt">
|
||||
<label>Persona</label>
|
||||
<span class="setting-hint">Wird im System-Prompt verwendet, wenn der Agent "Kennt sein Gegenüber" aktiviert hat.</span>
|
||||
<textarea id="profilePersona" rows="8" placeholder="Beschreibe dich selbst – Name, Rolle, Präferenzen, Kommunikationsstil..."></textarea>
|
||||
|
||||
@@ -504,6 +504,7 @@ function openFolderBrowser(opts = {}) {
|
||||
mode: 'folder',
|
||||
onSelect: opts.onSelect || null,
|
||||
currentAbsPath: opts.startPath || '/',
|
||||
basePath: (opts.basePath || '').replace(/\/+$/, '') || null,
|
||||
history: []
|
||||
};
|
||||
|
||||
@@ -592,16 +593,37 @@ async function fbLoadDir(absPath) {
|
||||
sidebar.innerHTML = '<div class="fe-empty">Leer</div>';
|
||||
}
|
||||
|
||||
document.getElementById('feBack').disabled = _feState.history.length === 0;
|
||||
const currentNorm = data.current.replace(/\/+$/, '');
|
||||
const atBase = _feState.basePath && currentNorm === _feState.basePath;
|
||||
const atRoot = currentNorm === '' || currentNorm === '/';
|
||||
document.getElementById('feBack').disabled = atBase || (atRoot && _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);
|
||||
if (!_feState) return;
|
||||
// Nicht über den basePath hinaus navigieren
|
||||
const currentNorm = _feState.currentAbsPath.replace(/\/+$/, '');
|
||||
if (_feState.basePath && currentNorm === _feState.basePath) return;
|
||||
|
||||
if (_feState.history.length > 0) {
|
||||
const prev = _feState.history.pop();
|
||||
// Prüfe ob der vorherige Pfad über dem basePath liegt
|
||||
if (_feState.basePath && !prev.replace(/\/+$/, '').startsWith(_feState.basePath)) {
|
||||
_feState.history.length = 0;
|
||||
fbLoadDir(_feState.basePath);
|
||||
return;
|
||||
}
|
||||
fbLoadDir(prev);
|
||||
} else {
|
||||
// Kein History-Eintrag → zum Parent navigieren
|
||||
const parent = currentNorm.includes('/') ? currentNorm.substring(0, currentNorm.lastIndexOf('/')) || '/' : '/';
|
||||
if (parent !== currentNorm) {
|
||||
fbLoadDir(parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── Folder Browser: Create Directory ──
|
||||
|
||||
@@ -10,7 +10,6 @@ registerPage('profile', {
|
||||
var usernameInput = document.getElementById('profileUsername');
|
||||
var displayNameInput = document.getElementById('profileDisplayName');
|
||||
var defaultPathInput = document.getElementById('profileDefaultPath');
|
||||
var defaultPathBrowse = document.getElementById('profileDefaultPathBrowse');
|
||||
var personaInput = document.getElementById('profilePersona');
|
||||
var saveBtn = document.getElementById('profileSaveBtn');
|
||||
var messageDiv = document.getElementById('profileMessage');
|
||||
@@ -43,18 +42,6 @@ registerPage('profile', {
|
||||
displayNameInput.value = profile.displayName || '';
|
||||
defaultPathInput.value = profile.defaultPath || '';
|
||||
|
||||
// Default Path Browse
|
||||
if (defaultPathBrowse) {
|
||||
defaultPathBrowse.addEventListener('click', function() {
|
||||
openFolderBrowser({
|
||||
startPath: defaultPathInput.value.trim() || '/',
|
||||
onSelect: function(path) {
|
||||
defaultPathInput.value = path;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Profil speichern
|
||||
saveBtn.addEventListener('click', async function() {
|
||||
saveBtn.disabled = true;
|
||||
@@ -64,7 +51,6 @@ registerPage('profile', {
|
||||
try {
|
||||
var updated = await apiUpdateProfile({
|
||||
displayName: displayNameInput.value.trim(),
|
||||
defaultPath: defaultPathInput.value.trim() || null,
|
||||
persona: _profileMde.value().trim()
|
||||
});
|
||||
|
||||
|
||||
@@ -129,10 +129,29 @@ registerPage('session', {
|
||||
const workspaceBrowse = document.getElementById('sessionWorkspaceBrowse');
|
||||
if (workspaceBrowse) {
|
||||
workspaceBrowse.addEventListener('click', () => {
|
||||
const defaultPath = (currentUser && currentUser.defaultPath) || '';
|
||||
const currentVal = workspaceEl.value.trim();
|
||||
// Für den Folder-Browser den absoluten Startpfad zusammenbauen
|
||||
let startPath = '/';
|
||||
if (defaultPath && currentVal) {
|
||||
startPath = defaultPath.replace(/\/+$/, '') + '/' + currentVal;
|
||||
} else if (defaultPath) {
|
||||
startPath = defaultPath;
|
||||
} else if (currentVal) {
|
||||
startPath = currentVal;
|
||||
}
|
||||
openFolderBrowser({
|
||||
startPath: workspaceEl.value.trim() || (currentUser && currentUser.defaultPath) || '/',
|
||||
onSelect(path) {
|
||||
workspaceEl.value = path;
|
||||
startPath,
|
||||
basePath: defaultPath || null,
|
||||
onSelect(absolutePath) {
|
||||
// Relativen Pfad zum DefaultPath berechnen
|
||||
if (defaultPath && absolutePath.startsWith(defaultPath)) {
|
||||
let rel = absolutePath.slice(defaultPath.length).replace(/^\/+/, '');
|
||||
workspaceEl.value = rel || '';
|
||||
} else {
|
||||
// Kein DefaultPath oder Pfad außerhalb → absoluten Pfad beibehalten (Legacy)
|
||||
workspaceEl.value = absolutePath;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user