mirror of
https://github.com/OpenHands/OpenHands.git
synced 2025-12-26 05:48:36 +08:00
Improvements to file list UI (#3794)
* move filematching logic into server * wait until ready before returning * show loading message instead of empty * logspam * delint * fix type * add a few more default ignores
This commit is contained in:
parent
93f271579c
commit
c6105f264f
@ -4,18 +4,17 @@ import TreeNode from "./TreeNode";
|
||||
import { I18nKey } from "#/i18n/declaration";
|
||||
|
||||
interface ExplorerTreeProps {
|
||||
files: string[];
|
||||
files: string[] | null;
|
||||
defaultOpen?: boolean;
|
||||
}
|
||||
|
||||
function ExplorerTree({ files, defaultOpen = false }: ExplorerTreeProps) {
|
||||
const { t } = useTranslation();
|
||||
if (files.length === 0) {
|
||||
return (
|
||||
<div className="text-sm text-gray-400 pt-4">
|
||||
{t(I18nKey.EXPLORER$EMPTY_WORKSPACE_MESSAGE)}
|
||||
</div>
|
||||
);
|
||||
if (!files?.length) {
|
||||
const message = !files
|
||||
? I18nKey.EXPLORER$LOADING_WORKSPACE_MESSAGE
|
||||
: I18nKey.EXPLORER$EMPTY_WORKSPACE_MESSAGE;
|
||||
return <div className="text-sm text-gray-400 pt-4">{t(message)}</div>;
|
||||
}
|
||||
return (
|
||||
<div className="w-full h-full pt-[4px]">
|
||||
|
||||
@ -90,7 +90,7 @@ function ExplorerActions({
|
||||
function FileExplorer() {
|
||||
const [isHidden, setIsHidden] = React.useState(false);
|
||||
const [isDragging, setIsDragging] = React.useState(false);
|
||||
const [files, setFiles] = React.useState<string[]>([]);
|
||||
const [files, setFiles] = React.useState<string[] | null>(null);
|
||||
const { curAgentState } = useSelector((state: RootState) => state.agent);
|
||||
const fileInputRef = React.useRef<HTMLInputElement | null>(null);
|
||||
const dispatch = useDispatch();
|
||||
|
||||
@ -441,6 +441,11 @@
|
||||
"zh-CN": "工作区没有文件",
|
||||
"de": "Keine Dateien im Arbeitsbereich"
|
||||
},
|
||||
"EXPLORER$LOADING_WORKSPACE_MESSAGE": {
|
||||
"en": "Loading workspace...",
|
||||
"zh-CN": "正在加载工作区...",
|
||||
"de": "Arbeitsbereich wird geladen..."
|
||||
},
|
||||
"EXPLORER$REFRESH_ERROR_MESSAGE": {
|
||||
"en": "Error refreshing workspace",
|
||||
"zh-CN": "工作区刷新错误",
|
||||
|
||||
@ -17,8 +17,6 @@ from pathlib import Path
|
||||
import pexpect
|
||||
from fastapi import FastAPI, HTTPException, Request, UploadFile
|
||||
from fastapi.responses import JSONResponse
|
||||
from pathspec import PathSpec
|
||||
from pathspec.patterns import GitWildMatchPattern
|
||||
from pydantic import BaseModel
|
||||
from uvicorn import run
|
||||
|
||||
@ -649,52 +647,12 @@ if __name__ == '__main__':
|
||||
if not os.path.exists(full_path) or not os.path.isdir(full_path):
|
||||
return []
|
||||
|
||||
# Check if .gitignore exists
|
||||
gitignore_path = os.path.join(full_path, '.gitignore')
|
||||
if os.path.exists(gitignore_path):
|
||||
# Use PathSpec to parse .gitignore
|
||||
with open(gitignore_path, 'r') as f:
|
||||
spec = PathSpec.from_lines(GitWildMatchPattern, f.readlines())
|
||||
else:
|
||||
# Fallback to default exclude list if .gitignore doesn't exist
|
||||
default_exclude = [
|
||||
'.git',
|
||||
'.DS_Store',
|
||||
'.svn',
|
||||
'.hg',
|
||||
'.idea',
|
||||
'.vscode',
|
||||
'.settings',
|
||||
'.pytest_cache',
|
||||
'__pycache__',
|
||||
'node_modules',
|
||||
'vendor',
|
||||
'build',
|
||||
'dist',
|
||||
'bin',
|
||||
'logs',
|
||||
'log',
|
||||
'tmp',
|
||||
'temp',
|
||||
'coverage',
|
||||
'venv',
|
||||
'env',
|
||||
]
|
||||
spec = PathSpec.from_lines(GitWildMatchPattern, default_exclude)
|
||||
|
||||
entries = os.listdir(full_path)
|
||||
|
||||
# Filter entries using PathSpec
|
||||
filtered_entries = [
|
||||
os.path.join(full_path, entry)
|
||||
for entry in entries
|
||||
if not spec.match_file(os.path.relpath(entry, str(full_path)))
|
||||
]
|
||||
|
||||
# Separate directories and files
|
||||
directories = []
|
||||
files = []
|
||||
for entry in filtered_entries:
|
||||
for entry in entries:
|
||||
# Remove leading slash and any parent directory components
|
||||
entry_relative = entry.lstrip('/').split('/')[-1]
|
||||
|
||||
|
||||
@ -159,6 +159,8 @@ class EventStreamRuntime(Runtime):
|
||||
# will initialize both the event stream and the env vars
|
||||
super().__init__(config, event_stream, sid, plugins, env_vars)
|
||||
|
||||
self._wait_until_alive()
|
||||
|
||||
logger.info(
|
||||
f'Container initialized with plugins: {[plugin.name for plugin in self.plugins]}'
|
||||
)
|
||||
|
||||
@ -5,6 +5,8 @@ import uuid
|
||||
import warnings
|
||||
|
||||
import requests
|
||||
from pathspec import PathSpec
|
||||
from pathspec.patterns import GitWildMatchPattern
|
||||
|
||||
from openhands.security.options import SecurityAnalyzers
|
||||
from openhands.server.data_models.feedback import FeedbackDataModel, store_feedback
|
||||
@ -378,6 +380,14 @@ async def get_security_analyzers():
|
||||
return sorted(SecurityAnalyzers.keys())
|
||||
|
||||
|
||||
FILES_TO_IGNORE = [
|
||||
'.git/',
|
||||
'.DS_Store',
|
||||
'node_modules/',
|
||||
'__pycache__/',
|
||||
]
|
||||
|
||||
|
||||
@app.get('/api/list-files')
|
||||
async def list_files(request: Request, path: str | None = None):
|
||||
"""List files in the specified path.
|
||||
@ -407,6 +417,23 @@ async def list_files(request: Request, path: str | None = None):
|
||||
)
|
||||
runtime: Runtime = request.state.session.agent_session.runtime
|
||||
file_list = runtime.list_files(path)
|
||||
file_list = [f for f in file_list if f not in FILES_TO_IGNORE]
|
||||
|
||||
def filter_for_gitignore(file_list, base_path):
|
||||
gitignore_path = os.path.join(base_path, '.gitignore')
|
||||
try:
|
||||
read_action = FileReadAction(gitignore_path)
|
||||
observation = runtime.run_action(read_action)
|
||||
spec = PathSpec.from_lines(
|
||||
GitWildMatchPattern, observation.content.splitlines()
|
||||
)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
return file_list
|
||||
file_list = [entry for entry in file_list if not spec.match_file(entry)]
|
||||
return file_list
|
||||
|
||||
file_list = filter_for_gitignore(file_list, '')
|
||||
return file_list
|
||||
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user