feat: add file browser and file download with multi-backend support (#142)
* feat: add file browser and file download with multi-backend support Adds a built-in File Browser page and a File Download system to Hermes Web UI, enabling users to browse, edit, preview, upload, and download files from the workspace directly from the web dashboard. File Browser (/hermes/files): - New view FilesView.vue plus components under components/hermes/files/ (FileTree, FileList, FileBreadcrumb, FileToolbar, FileContextMenu, FileEditor, FilePreview, FileRenameModal, FileUploadModal) - New Pinia store stores/hermes/files.ts for directory tree, selection, and editing state - New API module api/hermes/files.ts - New server routes routes/hermes/files.ts with CRUD, rename, upload, and directory listing - New service services/hermes/file-provider.ts with a pluggable provider architecture (local filesystem + multi-terminal backends) File Download: - New server route routes/hermes/download.ts and client API api/hermes/download.ts - Integration in chat messages (MessageItem.vue, MarkdownRenderer.vue) to surface downloadable file references Packaging: - package.json: add a prepare script so the package can be installed directly from a git URL with dist/ built automatically i18n: add files/download translations to en.ts and zh.ts. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: use clipboard fallback for non-secure HTTP contexts navigator.clipboard is undefined on HTTP intranet deployments (only available in secure contexts). The previous synchronous calls threw silently and the success toast still fired, making 'copy' actions appear broken. - Add packages/client/src/utils/clipboard.ts with execCommand fallback via a hidden textarea - Use the helper in FileContextMenu (copy file path), CodexLoginModal (copy user code), NousLoginModal (copy user code), ChatPanel (copy session id) - Each call now awaits the result and shows success/failure based on the actual outcome Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -79,6 +79,7 @@ export default {
|
||||
channels: 'Channels',
|
||||
gateways: 'Gateways',
|
||||
terminal: 'Terminal',
|
||||
files: 'Files',
|
||||
groupConversation: 'Conversation',
|
||||
groupPlatform: 'Platform',
|
||||
groupAgent: 'Agent',
|
||||
@@ -548,6 +549,66 @@ export default {
|
||||
cost: 'Cost',
|
||||
noData: 'No usage data',
|
||||
},
|
||||
// Files
|
||||
files: {
|
||||
title: 'Files',
|
||||
tree: 'Directory Tree',
|
||||
list: 'File List',
|
||||
breadcrumbRoot: 'Home',
|
||||
newFile: 'New File',
|
||||
newFolder: 'New Folder',
|
||||
upload: 'Upload',
|
||||
refresh: 'Refresh',
|
||||
open: 'Open',
|
||||
edit: 'Edit',
|
||||
preview: 'Preview',
|
||||
download: 'Download',
|
||||
copyPath: 'Copy Path',
|
||||
rename: 'Rename',
|
||||
delete: 'Delete',
|
||||
name: 'Name',
|
||||
size: 'Size',
|
||||
modified: 'Modified',
|
||||
actions: 'Actions',
|
||||
emptyDir: 'Empty directory',
|
||||
loading: 'Loading...',
|
||||
confirmDelete: 'Are you sure you want to delete "{name}"?',
|
||||
confirmDeleteDir: 'Are you sure you want to delete directory "{name}" and all its contents?',
|
||||
deleteFailed: 'Delete failed',
|
||||
deleted: 'Deleted',
|
||||
renameTo: 'Rename to',
|
||||
newFileName: 'File name',
|
||||
newFolderName: 'Folder name',
|
||||
created: 'Created',
|
||||
createFailed: 'Create failed',
|
||||
renamed: 'Renamed',
|
||||
renameFailed: 'Rename failed',
|
||||
uploadSuccess: 'Uploaded {count} file(s)',
|
||||
uploadFailed: 'Upload failed',
|
||||
saveFailed: 'Save failed',
|
||||
saved: 'Saved',
|
||||
unsavedChanges: 'You have unsaved changes. Discard?',
|
||||
pathCopied: 'Path copied',
|
||||
fileTooLarge: 'File too large (max 10MB)',
|
||||
permissionDenied: 'Cannot modify protected file',
|
||||
notFound: 'File or directory not found',
|
||||
backendError: 'File operation failed',
|
||||
dragDropHint: 'Drag files here to upload',
|
||||
closeEditor: 'Close Editor',
|
||||
saveFile: 'Save',
|
||||
},
|
||||
// Download
|
||||
download: {
|
||||
downloading: 'Downloading...',
|
||||
downloadFailed: 'Download failed',
|
||||
fileNotFound: 'File not found or deleted',
|
||||
fileTooLarge: 'File too large (exceeds limit)',
|
||||
backendError: 'File read failed, remote environment may be unavailable',
|
||||
backendTimeout: 'File read timed out',
|
||||
unsupportedBackend: 'Current terminal backend does not support file download',
|
||||
invalidPath: 'Invalid file path',
|
||||
download: 'Download',
|
||||
},
|
||||
|
||||
// Changelog
|
||||
changelog: {
|
||||
|
||||
@@ -79,6 +79,7 @@ export default {
|
||||
channels: '频道',
|
||||
gateways: '网关',
|
||||
terminal: '终端',
|
||||
files: '文件',
|
||||
groupConversation: '对话',
|
||||
groupPlatform: '平台',
|
||||
groupAgent: '代理',
|
||||
@@ -550,6 +551,66 @@ export default {
|
||||
cost: '费用',
|
||||
noData: '暂无用量数据',
|
||||
},
|
||||
// 文件管理
|
||||
files: {
|
||||
title: '文件',
|
||||
tree: '目录树',
|
||||
list: '文件列表',
|
||||
breadcrumbRoot: '根目录',
|
||||
newFile: '新建文件',
|
||||
newFolder: '新建文件夹',
|
||||
upload: '上传',
|
||||
refresh: '刷新',
|
||||
open: '打开',
|
||||
edit: '编辑',
|
||||
preview: '预览',
|
||||
download: '下载',
|
||||
copyPath: '复制路径',
|
||||
rename: '重命名',
|
||||
delete: '删除',
|
||||
name: '名称',
|
||||
size: '大小',
|
||||
modified: '修改时间',
|
||||
actions: '操作',
|
||||
emptyDir: '空目录',
|
||||
loading: '加载中...',
|
||||
confirmDelete: '确定要删除「{name}」吗?',
|
||||
confirmDeleteDir: '确定要删除目录「{name}」及其所有内容吗?',
|
||||
deleteFailed: '删除失败',
|
||||
deleted: '已删除',
|
||||
renameTo: '重命名为',
|
||||
newFileName: '文件名',
|
||||
newFolderName: '文件夹名',
|
||||
created: '已创建',
|
||||
createFailed: '创建失败',
|
||||
renamed: '已重命名',
|
||||
renameFailed: '重命名失败',
|
||||
uploadSuccess: '已上传 {count} 个文件',
|
||||
uploadFailed: '上传失败',
|
||||
saveFailed: '保存失败',
|
||||
saved: '已保存',
|
||||
unsavedChanges: '有未保存的更改,是否丢弃?',
|
||||
pathCopied: '路径已复制',
|
||||
fileTooLarge: '文件过大(最大 10MB)',
|
||||
permissionDenied: '无法修改受保护的文件',
|
||||
notFound: '文件或目录不存在',
|
||||
backendError: '文件操作失败',
|
||||
dragDropHint: '拖拽文件到此处上传',
|
||||
closeEditor: '关闭编辑器',
|
||||
saveFile: '保存',
|
||||
},
|
||||
// 下载
|
||||
download: {
|
||||
downloading: '正在下载...',
|
||||
downloadFailed: '下载失败',
|
||||
fileNotFound: '文件不存在或已被删除',
|
||||
fileTooLarge: '文件过大(超过限制)',
|
||||
backendError: '文件读取失败,远程环境可能不可用',
|
||||
backendTimeout: '文件读取超时',
|
||||
unsupportedBackend: '当前 terminal backend 暂不支持文件下载',
|
||||
invalidPath: '无效的文件路径',
|
||||
download: '下载',
|
||||
},
|
||||
|
||||
// 更新日志
|
||||
changelog: {
|
||||
|
||||
Reference in New Issue
Block a user