mirror of
https://github.com/open-webui/open-webui.git
synced 2026-08-23 22:14:58 -06:00
refac
This commit is contained in:
@@ -15,6 +15,17 @@ export type TerminalFeatures = {
|
||||
terminal?: boolean;
|
||||
};
|
||||
|
||||
export type TerminalFileRoot = {
|
||||
path: string;
|
||||
label: string;
|
||||
};
|
||||
|
||||
export type TerminalCwd = {
|
||||
cwd: string | null;
|
||||
home?: string;
|
||||
root?: TerminalFileRoot;
|
||||
};
|
||||
|
||||
import { WEBUI_API_BASE_URL } from '$lib/constants';
|
||||
|
||||
export type TerminalServer = {
|
||||
@@ -49,14 +60,19 @@ export const getCwd = async (
|
||||
baseUrl: string,
|
||||
apiKey: string,
|
||||
sessionId?: string
|
||||
): Promise<string | null> => {
|
||||
): Promise<TerminalCwd | null> => {
|
||||
const url = `${baseUrl.replace(/\/$/, '')}/files/cwd`;
|
||||
const headers: Record<string, string> = { Authorization: `Bearer ${apiKey}` };
|
||||
if (sessionId) headers['X-Session-Id'] = sessionId;
|
||||
const res = await fetch(url, { headers }).catch(() => null);
|
||||
if (!res || !res.ok) return null;
|
||||
const json = await res.json().catch(() => null);
|
||||
return json?.cwd ?? null;
|
||||
if (!json) return null;
|
||||
return {
|
||||
cwd: json?.cwd ?? null,
|
||||
home: json?.home,
|
||||
root: json?.root
|
||||
};
|
||||
};
|
||||
|
||||
export const listFiles = async (
|
||||
|
||||
@@ -25,7 +25,9 @@
|
||||
deleteEntry,
|
||||
moveEntry,
|
||||
setCwd,
|
||||
type FileEntry
|
||||
type FileEntry,
|
||||
type TerminalFileRoot,
|
||||
type TerminalCwd
|
||||
} from '$lib/apis/terminal';
|
||||
import { isCodeFile } from '$lib/utils/codeHighlight';
|
||||
import Folder from '../icons/Folder.svelte';
|
||||
@@ -88,6 +90,7 @@
|
||||
|
||||
// ── Directory state ──────────────────────────────────────────────────
|
||||
let currentPath = savedPath;
|
||||
let fileRoot: TerminalFileRoot | null = null;
|
||||
let entries: FileEntry[] = [];
|
||||
let loading = false;
|
||||
let error: string | null = null;
|
||||
@@ -280,11 +283,8 @@
|
||||
terminalEnabled = config?.features?.terminal !== false;
|
||||
}
|
||||
|
||||
const rawCwd = await getCwd(terminal.url, terminal.key, chatId ?? undefined);
|
||||
const cwd = rawCwd ? normalizePath(rawCwd) : null;
|
||||
const dir = cwd ? (cwd.endsWith('/') ? cwd : cwd + '/') : '/';
|
||||
savedPath = dir;
|
||||
loadDir(dir);
|
||||
savedPath = applyCwd(await getCwd(terminal.url, terminal.key, chatId ?? undefined));
|
||||
loadDir(savedPath);
|
||||
})();
|
||||
}
|
||||
}
|
||||
@@ -305,7 +305,52 @@
|
||||
/** Normalize Windows backslashes to forward slashes. */
|
||||
const normalizePath = (p: string) => p.replace(/\\/g, '/');
|
||||
|
||||
const asDirectoryPath = (path: string) => {
|
||||
const normalized = normalizePath(path || '/');
|
||||
return normalized.endsWith('/') ? normalized : `${normalized}/`;
|
||||
};
|
||||
|
||||
const setFileRoot = (root?: TerminalFileRoot) => {
|
||||
fileRoot = root?.path
|
||||
? {
|
||||
path: asDirectoryPath(root.path),
|
||||
label: root.label || 'Home'
|
||||
}
|
||||
: null;
|
||||
};
|
||||
|
||||
const isInsideFileRoot = (path: string) => {
|
||||
if (!fileRoot) return true;
|
||||
const directory = asDirectoryPath(path);
|
||||
return directory === fileRoot.path || directory.startsWith(fileRoot.path);
|
||||
};
|
||||
|
||||
const clampToFileRoot = (path: string) => {
|
||||
if (!fileRoot) return asDirectoryPath(path);
|
||||
return isInsideFileRoot(path) ? asDirectoryPath(path) : fileRoot.path;
|
||||
};
|
||||
|
||||
const applyCwd = (cwd: TerminalCwd | null) => {
|
||||
setFileRoot(cwd?.root);
|
||||
const path = cwd?.cwd ? asDirectoryPath(cwd.cwd) : (fileRoot?.path ?? '/');
|
||||
return clampToFileRoot(path);
|
||||
};
|
||||
|
||||
const buildBreadcrumbs = (path: string) => {
|
||||
if (fileRoot) {
|
||||
const directory = clampToFileRoot(path);
|
||||
const relative = directory === fileRoot.path ? '' : directory.slice(fileRoot.path.length);
|
||||
const parts = relative.split('/').filter(Boolean);
|
||||
return parts.reduce(
|
||||
(acc, part) => {
|
||||
const prev = acc[acc.length - 1];
|
||||
acc.push({ label: part, path: `${prev.path}${part}/` });
|
||||
return acc;
|
||||
},
|
||||
[{ label: fileRoot.label, path: fileRoot.path }]
|
||||
);
|
||||
}
|
||||
|
||||
const parts = path.split('/').filter(Boolean);
|
||||
const isDrive = /^[A-Za-z]:$/.test(parts[0] ?? '');
|
||||
const root = isDrive ? { label: parts[0], path: `${parts[0]}/` } : { label: '/', path: '/' };
|
||||
@@ -348,6 +393,7 @@
|
||||
const loadDir = async (path: string) => {
|
||||
const terminal = selectedTerminal;
|
||||
if (!terminal) return;
|
||||
const directory = clampToFileRoot(path);
|
||||
|
||||
loading = true;
|
||||
error = null;
|
||||
@@ -355,15 +401,15 @@
|
||||
previewPort = null;
|
||||
clearFilePreview();
|
||||
clearSelection();
|
||||
currentPath = path;
|
||||
savedPath = path;
|
||||
pushNavHistory(path);
|
||||
currentPath = directory;
|
||||
savedPath = directory;
|
||||
pushNavHistory(directory);
|
||||
|
||||
const result = await listFiles(terminal.url, terminal.key, path, chatId ?? undefined);
|
||||
const result = await listFiles(terminal.url, terminal.key, directory, chatId ?? undefined);
|
||||
loading = false;
|
||||
|
||||
// Set working directory on the terminal server (fire-and-forget)
|
||||
setCwd(terminal.url, terminal.key, path, chatId ?? undefined);
|
||||
setCwd(terminal.url, terminal.key, directory, chatId ?? undefined);
|
||||
|
||||
if (result === null) {
|
||||
error =
|
||||
@@ -788,6 +834,10 @@
|
||||
handledDisplayFile = true;
|
||||
showFileNavPath.set(null);
|
||||
filePath = normalizePath(filePath);
|
||||
if (!isInsideFileRoot(filePath)) {
|
||||
await loadDir(fileRoot?.path ?? '/');
|
||||
return;
|
||||
}
|
||||
|
||||
const lastSlash = filePath.lastIndexOf('/');
|
||||
const dir = lastSlash > 0 ? filePath.substring(0, lastSlash + 1) : '/';
|
||||
@@ -810,6 +860,10 @@
|
||||
if (!filePath || !selectedTerminal) return;
|
||||
showFileNavDir.set(null);
|
||||
filePath = normalizePath(filePath);
|
||||
if (!isInsideFileRoot(filePath)) {
|
||||
await loadDir(fileRoot?.path ?? '/');
|
||||
return;
|
||||
}
|
||||
|
||||
const lastSlash = filePath.lastIndexOf('/');
|
||||
const dir = lastSlash > 0 ? filePath.substring(0, lastSlash + 1) : '/';
|
||||
@@ -835,10 +889,9 @@
|
||||
|
||||
if (chatId || savedPath === '/') {
|
||||
// Fetch session-specific cwd from the server (or global default for new chats)
|
||||
const rawCwd = await getCwd(terminal.url, terminal.key, chatId ?? undefined);
|
||||
const cwd = rawCwd ? normalizePath(rawCwd) : null;
|
||||
if (cwd) savedPath = cwd.endsWith('/') ? cwd : cwd + '/';
|
||||
savedPath = applyCwd(await getCwd(terminal.url, terminal.key, chatId ?? undefined));
|
||||
}
|
||||
savedPath = clampToFileRoot(savedPath);
|
||||
loadDir(savedPath);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user