* feat: 添加文件下载功能,支持多 Terminal Backend 实现基于 FileProvider 抽象的文件下载能力,支持 local、Docker、SSH、 Singularity 四种 backend。 主要变更: - 新增 FileProvider 接口及四种后端实现(含 SSH 命令注入防护) - 新增 GET /api/hermes/download 下载路由(含 MIME 类型检测) - 前端 Markdown 文件链接拦截下载 + 附件下载按钮 - 中英文 i18n 翻译 - 更新 README、CLAUDE.md 和设计文档 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: 添加文件浏览器与下载功能,支持目录浏览、文件编辑、预览和上传 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * build: add prepare script so 'npm install git+url' auto-builds dist/ Allows installing this package directly from git without a pre-built dist/. When cloned via npm, prepare runs 'npm run build' if dist/ is missing, producing the artifacts declared in the files[] field before packing. 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> * i18n: backfill files/download translations for de, es, fr, ja, ko, pt Add nav.files, files.* (39 keys), and download.* (9 keys) so the file browser UI is fully localized in these six locales instead of falling back to English. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(files): close preview when navigating or affected file changes Opening a preview and then navigating directories, deleting the previewed file, or renaming it left the preview pane stuck on stale content because previewFile was never cleared. - stores/hermes/files.ts: - fetchEntries clears previewFile on path change (in-place refresh keeps the preview). - deleteEntry / renameEntry clear preview/editor state when the affected entry matches the previewed/edited file or its parent. - Add isAffected(target, changed, isDir) helper. - components/hermes/files/FilePreview.vue: replace the misleading common.cancel close button with a dedicated files.closePreview key plus an X icon and quaternary style. - i18n: add files.closePreview to all 8 locales. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * chore: 清理已完成功能的计划与设计文档 文件浏览器与文件下载功能均已被上游合并,对应的开发计划 与设计稿不再需要在 fork 中保留: - plans/2025-07-20-file-browser.md - plans/2026-04-20-file-download.md - specs/2025-07-20-file-browser-design.md - specs/2026-04-20-file-download-design.md 清理后本 fork 与 upstream/main 代码层面完全对齐。 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs: 添加 thinking 块分离与折叠展示设计稿(#164) 针对上游 issue #164,设计 assistant 消息中 <think>/<thinking>/<reasoning> 标签的识别、分离与可折叠展示方案。 关键决策(经 rubber-duck 审查修订): - 不修改 Message.content 与持久化字段,确保 localStorage 向前兼容 - 耗时摘要改为纯运行时派生(store 内 Map),避免刷新/重连丢失 - 首版即实现代码块保护,避免误识别 - 流结束时未闭合标签降级为正文,防止吞答案 - 解析 computed 与 duration interval 分离,规避性能风险 - 解析器放置 packages/client/src/utils/ 避免反向依赖 - 显式不支持同名嵌套(罕见场景文档化) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs: 添加 thinking 块分离与折叠实施计划(#164) 12 Task TDD 计划: - Task 1-7:utils/thinking-parser.ts 纯函数模块 + 单元测试 - Task 8-9:chat store thinkingObservation Map 接入 SSE - Task 10:8 语言 i18n 新增 6 条 key - Task 11:MessageItem.vue 渲染折叠 UI + SCSS - Task 12:构建/测试/手动验证/推送 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat(thinking-parser): 首个闭合 <think> 标签拆分 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * test(thinking-parser): 覆盖多段/变体标签/大小写/空输入 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * test(thinking-parser): 流式 pending 与终止态降级 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat(thinking-parser): 代码块保护避免误识别伪标签 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * test(thinking-parser): 同名嵌套与 chunk 边界行为 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat(thinking-parser): countThinkingChars 辅助函数 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat(thinking-parser): detectThinkingBoundary 边界检测 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat(chat-store): 新增 thinkingObservation 运行时 Map Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat(chat-store): message.delta 写入 thinking 边界 + switchSession 清理 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * i18n: 新增 thinking 块 6 条 key(8 语言) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat(chat): MessageItem 渲染 thinking 折叠区 - 复用 tool-line 风格 chevron - 两条响应链:parse computed + duration interval - 流式+pending 强制展开 - show_reasoning 控制默认态 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat(chat): 支持思考块实时流式与历史展示 - 扩展 Message 接口增加 reasoning 字段,mapHermesMessages 从 HermesMessage.reasoning 透传历史会话的思考内容。 - RunEvent 类型新增 text 字段,chat store 处理三个新 SSE 事件: reasoning.delta / thinking.delta / reasoning.available。 - 思考时长观察:仅在 reasoning.delta 累积时记录起始时间戳, reasoning.available 时记录结束时间戳;无实时 delta 时不显示时长。 - MessageItem 采用双源渲染(reasoning 字段优先,<think> 标签作 fallback),duration > 0 才展示耗时。 - 新增 3 条单测覆盖三个 SSE 事件;测试 32/32 通过。 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(chat): reasoning 块不再短暂展示正文 根因:上游 hermes-agent run_agent.py:11275 在每次模型响应结束时用 assistant content[:500] 作为 reasoning.available 的 preview 负载, 致使 Web UI 把正文写入 last.reasoning,思考块短暂显示正文直到会话 轮询/刷新从 session DB 读回正确的 reasoning 字段。 修复: - reasoning.available 事件不再写入 last.reasoning,仅用于标记计时 结束(noteReasoningEnd);真实推理由 reasoning.delta 或会话 DB 提供 - 新增 scrubBuggyReasoningInCache:hydration 时治愈 localStorage 里 已被污染的 assistant 消息(reasoning == content 或前缀时丢弃) - 两个 cache 加载入口(loadSessions / switchSession)均接入 scrubber 测试:新增 4 条单测,全套 280/280 通过。 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
705 lines
24 KiB
TypeScript
705 lines
24 KiB
TypeScript
export default {
|
|
// Login
|
|
login: {
|
|
title: 'Hermes Web UI',
|
|
description: 'Enter your access token to continue. Find it in the server startup logs.',
|
|
placeholder: 'Access token',
|
|
submit: 'Login',
|
|
tokenRequired: 'Please enter your access token',
|
|
invalidToken: 'Invalid token',
|
|
connectionFailed: 'Cannot connect to server',
|
|
passwordLogin: 'Password',
|
|
tokenLogin: 'Token',
|
|
usernamePlaceholder: 'Username',
|
|
passwordPlaceholder: 'Password',
|
|
credentialsRequired: 'Please enter username and password',
|
|
invalidCredentials: 'Invalid username or password',
|
|
passwordMismatch: 'Passwords do not match',
|
|
passwordTooShort: 'Password must be at least 6 characters',
|
|
setupSuccess: 'Password login configured successfully',
|
|
passwordChanged: 'Password changed successfully',
|
|
passwordRemoved: 'Password login removed',
|
|
setupPassword: 'Set Up Password Login',
|
|
changePassword: 'Change Password',
|
|
changeUsername: 'Change Username',
|
|
removePasswordLogin: 'Remove',
|
|
username: 'Username',
|
|
currentPassword: 'Current Password',
|
|
newPassword: 'New Password',
|
|
confirmPassword: 'Confirm Password',
|
|
newUsername: 'New Username',
|
|
usernameChanged: 'Username changed successfully',
|
|
usernameTooShort: 'Username must be at least 2 characters',
|
|
setupDescription: 'Set up a username and password for convenient login. The access token will continue to work as a backup.',
|
|
removeConfirm: 'Are you sure you want to remove password login? You will need to use the access token to log in.',
|
|
passwordLoginNotConfigured: 'Password login is not configured',
|
|
passwordLoginConfigured: 'Password login enabled ({username})',
|
|
},
|
|
|
|
// Common
|
|
common: {
|
|
loading: 'Loading...',
|
|
cancel: 'Cancel',
|
|
delete: 'Delete',
|
|
edit: 'Edit',
|
|
save: 'Save',
|
|
retry: 'Retry',
|
|
saved: 'Saved',
|
|
update: 'Update',
|
|
create: 'Create',
|
|
saveFailed: 'Save failed',
|
|
deleteFailed: 'Delete failed',
|
|
ok: 'OK',
|
|
copied: 'Copied',
|
|
copy: 'Copy',
|
|
noData: 'No data',
|
|
fetch: 'Fetch',
|
|
add: 'Add',
|
|
enable: 'Enable',
|
|
disable: 'Disable',
|
|
configured: 'Configured',
|
|
notConfigured: 'Not configured',
|
|
confirm: 'Confirm',
|
|
expand: 'Expand',
|
|
collapse: 'Collapse',
|
|
start: 'Start',
|
|
stop: 'Stop',
|
|
},
|
|
|
|
// Sidebar
|
|
sidebar: {
|
|
chat: 'Chat',
|
|
search: 'Search',
|
|
jobs: 'Jobs',
|
|
models: 'Models',
|
|
profiles: 'Profiles',
|
|
skills: 'Skills',
|
|
memory: 'Memory',
|
|
logs: 'Logs',
|
|
usage: 'Usage',
|
|
channels: 'Channels',
|
|
gateways: 'Gateways',
|
|
terminal: 'Terminal',
|
|
groupChat: 'Group Chat',
|
|
files: 'Files',
|
|
groupConversation: 'Conversation',
|
|
groupPlatform: 'Platform',
|
|
groupAgent: 'Agent',
|
|
groupSystem: 'System',
|
|
groupMonitoring: 'Monitoring',
|
|
groupTools: 'Tools',
|
|
settings: 'Settings',
|
|
connected: 'Connected',
|
|
disconnected: 'Disconnected',
|
|
collapse: 'Collapse menu',
|
|
expand: 'Expand menu',
|
|
updateTip: 'Run "hermes-web-ui update" in terminal to update',
|
|
updateVersion: 'Upgrade to v{version}',
|
|
updating: 'Updating...',
|
|
updateSuccess: 'Update complete, please restart the server',
|
|
updateFailed: 'Update failed',
|
|
logout: 'Sign Out',
|
|
nodeVersionWarning: 'Detected Node.js v{version}. Please upgrade to version 23 or later.',
|
|
changelog: 'Changelog',
|
|
noChangelog: 'No changelog available',
|
|
},
|
|
|
|
// Chat
|
|
chat: {
|
|
contextRemaining: 'remaining',
|
|
emptyState: 'Start a conversation with Hermes Agent',
|
|
inputPlaceholder: 'Type a message... (Enter to send, Shift+Enter for new line)',
|
|
attachFiles: 'Attach files',
|
|
stop: 'Stop',
|
|
start: 'Start',
|
|
stopGateway: 'Stop Gateway',
|
|
send: 'Send',
|
|
contextUsed: 'Context used:',
|
|
sessions: 'Sessions',
|
|
noSessions: 'No sessions',
|
|
searchTitle: 'Search Sessions',
|
|
searchSubtitle: 'Search by title or message content',
|
|
searchHint: 'Cmd/Ctrl+K',
|
|
searchPlaceholder: 'Search sessions...',
|
|
searchEmpty: 'Recent sessions',
|
|
searchRecent: 'Recent session',
|
|
searchNoResults: 'No sessions match your search',
|
|
searchNoSnippet: 'No snippet available',
|
|
searchEnterHint: 'Enter to open · Esc to close',
|
|
searchFailed: 'Failed to search sessions',
|
|
newChat: 'New Chat',
|
|
deleteSession: 'Delete this session?',
|
|
sessionDeleted: 'Session deleted',
|
|
rename: 'Rename',
|
|
pin: 'Pin',
|
|
unpin: 'Unpin',
|
|
pinned: 'Pinned',
|
|
chatMode: 'Chat',
|
|
liveMode: 'Live',
|
|
liveSessions: 'Live Sessions',
|
|
recentBadge: 'Recent',
|
|
linkedSessions: '{count} linked',
|
|
noVisibleMessages: 'No human-visible messages.',
|
|
monitorRoleUser: 'User',
|
|
monitorRoleAssistant: 'Assistant',
|
|
copySessionId: 'Copy Session ID',
|
|
renamed: 'Renamed',
|
|
renameFailed: 'Rename failed',
|
|
renameSession: 'Rename Session',
|
|
enterNewTitle: 'Enter new title',
|
|
other: 'Other',
|
|
runFailed: 'Run failed',
|
|
error: 'Error',
|
|
tool: 'Tool',
|
|
arguments: 'Arguments',
|
|
result: 'Result',
|
|
truncated: '... (truncated)',
|
|
thinkingLabel: 'Thinking',
|
|
thinkingInProgress: 'Thinking…',
|
|
thinkingShow: 'Show thinking',
|
|
thinkingHide: 'Hide thinking',
|
|
thinkingDuration: 'Observed {duration}',
|
|
thinkingChars: '{count} chars',
|
|
},
|
|
|
|
// Jobs
|
|
jobs: {
|
|
title: 'Scheduled Jobs',
|
|
createJob: 'Create Job',
|
|
editJob: 'Edit Job',
|
|
noJobs: 'No scheduled jobs yet. Create one to get started.',
|
|
name: 'Name',
|
|
namePlaceholder: 'Job name',
|
|
schedule: 'Schedule (Cron Expression)',
|
|
schedulePlaceholder: 'e.g. 0 9 * * *',
|
|
quickPresets: 'Quick Presets',
|
|
selectPreset: 'Select a preset...',
|
|
presetEveryMinute: 'Every minute',
|
|
presetEvery5Min: 'Every 5 minutes',
|
|
presetEveryHour: 'Every hour',
|
|
presetEveryDay: 'Every day at 00:00',
|
|
presetEveryDay9: 'Every day at 09:00',
|
|
presetEveryMonday: 'Every Monday at 09:00',
|
|
presetEveryMonth: 'Every month 1st at 09:00',
|
|
prompt: 'Prompt',
|
|
promptPlaceholder: 'The prompt to execute',
|
|
deliverTarget: 'Deliver Target',
|
|
origin: 'Origin',
|
|
local: 'Local',
|
|
repeatCount: 'Repeat Count (optional)',
|
|
repeatPlaceholder: 'Leave empty for infinite',
|
|
jobCreated: 'Job created',
|
|
jobUpdated: 'Job updated',
|
|
nameRequired: 'Name is required',
|
|
scheduleRequired: 'Schedule is required',
|
|
loadFailed: 'Failed to load job',
|
|
jobPaused: 'Job paused',
|
|
jobResumed: 'Job resumed',
|
|
jobTriggered: 'Job triggered',
|
|
jobDeleted: 'Job deleted',
|
|
status: {
|
|
running: 'Running',
|
|
paused: 'Paused',
|
|
disabled: 'Disabled',
|
|
scheduled: 'Scheduled',
|
|
},
|
|
info: {
|
|
schedule: 'Schedule',
|
|
lastRun: 'Last Run',
|
|
nextRun: 'Next Run',
|
|
deliver: 'Deliver',
|
|
repeat: 'Repeat',
|
|
},
|
|
action: {
|
|
pause: 'Pause',
|
|
pauseJob: 'Pause job',
|
|
resume: 'Resume',
|
|
resumeJob: 'Resume job',
|
|
runNow: 'Run Now',
|
|
triggerImmediately: 'Trigger immediately',
|
|
},
|
|
},
|
|
|
|
// Skills
|
|
skills: {
|
|
title: 'Skills',
|
|
searchPlaceholder: 'Search skills...',
|
|
noMatch: 'No skills match your search',
|
|
noSkills: 'No skills found',
|
|
backTo: 'Back to',
|
|
attachedFiles: 'Attached Files',
|
|
loadFailed: 'Failed to load skill',
|
|
fileLoadFailed: 'Failed to load file',
|
|
toggleFailed: 'Failed to toggle skill',
|
|
},
|
|
|
|
// Memory
|
|
memory: {
|
|
title: 'Memory',
|
|
refresh: 'Refresh',
|
|
loadFailed: 'Failed to load memory',
|
|
myNotes: 'My Notes',
|
|
noNotes: 'No notes yet.',
|
|
notesPlaceholder: 'Write your notes...',
|
|
userProfile: 'User Profile',
|
|
noProfile: 'No profile yet.',
|
|
profilePlaceholder: 'Write your profile...',
|
|
soul: 'Soul',
|
|
noSoul: 'No soul configuration yet.',
|
|
soulPlaceholder: 'Write soul configuration...',
|
|
},
|
|
|
|
// Models
|
|
models: {
|
|
title: 'Models',
|
|
searchPlaceholder: 'Search models...',
|
|
addProvider: 'Add Provider',
|
|
providerType: 'Provider Type',
|
|
preset: 'Preset',
|
|
custom: 'Custom',
|
|
selectProvider: 'Select Provider',
|
|
chooseProvider: 'Choose a provider...',
|
|
name: 'Name',
|
|
autoGeneratedName: 'Auto-generated from Base URL',
|
|
baseUrl: 'Base URL',
|
|
baseUrlPlaceholder: 'e.g. https://api.example.com/v1',
|
|
apiKey: 'API Key',
|
|
apiKeyPlaceholder: 'sk-...',
|
|
defaultModel: 'Default Model',
|
|
selectOrInput: 'Select or type a model name...',
|
|
selectModel: 'Select a model...',
|
|
providerAdded: 'Provider added',
|
|
providerDeleted: 'Provider deleted',
|
|
deleteProvider: 'Delete Provider',
|
|
deleteConfirm: 'Are you sure you want to delete "{name}"?',
|
|
codexLoginTitle: 'OpenAI Codex Login',
|
|
codexWaiting: 'Enter this code at the authorization page to complete login:',
|
|
codexCopyCode: 'Code copied',
|
|
codexOpenLink: 'Open authorization page',
|
|
codexApproved: 'Login successful',
|
|
codexExpired: 'Authorization expired. Please try again.',
|
|
nousLoginTitle: 'Nous Portal Login',
|
|
nousWaiting: 'Enter this code at the authorization page to complete login:',
|
|
nousCopyCode: 'Code copied',
|
|
nousOpenLink: 'Open authorization page',
|
|
nousApproved: 'Login successful',
|
|
nousDenied: 'Authorization was denied. Please try again.',
|
|
nousExpired: 'Authorization expired. Please try again.',
|
|
customBadge: 'CUSTOM',
|
|
customModelPlaceholder: 'Custom model name',
|
|
customModelHint: 'Enter to load',
|
|
noProviders: 'No providers found. Add a custom provider to get started.',
|
|
builtIn: 'Built-in',
|
|
customType: 'Custom',
|
|
provider: 'Provider',
|
|
contextLength: 'Context Length',
|
|
contextLengthPlaceholder: 'e.g. 200000 (optional)',
|
|
local: 'Local ({host})',
|
|
selectProviderRequired: 'Please select a provider',
|
|
baseUrlRequired: 'Base URL is required',
|
|
apiKeyRequired: 'API Key is required',
|
|
modelRequired: 'Default Model is required',
|
|
enterBaseUrl: 'Please enter Base URL first',
|
|
unexpectedFormat: 'Unexpected response format',
|
|
foundModels: 'Found {count} models',
|
|
fetchFailed: 'Failed to fetch models',
|
|
},
|
|
|
|
// Profiles
|
|
gateways: {
|
|
title: 'Gateways',
|
|
running: 'Running',
|
|
stopped: 'Stopped',
|
|
started: 'Started',
|
|
startFailed: 'Failed to start gateway',
|
|
stopFailed: 'Failed to stop gateway',
|
|
},
|
|
profiles: {
|
|
title: 'Profiles',
|
|
create: 'Create Profile',
|
|
import: 'Import',
|
|
export: 'Export',
|
|
rename: 'Rename',
|
|
delete: 'Delete',
|
|
switchTo: 'Switch to',
|
|
switchConfirm: 'Switching to profile "{name}" will restart the gateway. Continue?',
|
|
switchSuccess: 'Switched to profile "{name}"',
|
|
switchFailed: 'Failed to switch profile. Gateway may need manual restart.',
|
|
createSuccess: 'Profile "{name}" created',
|
|
createFailed: 'Failed to create profile',
|
|
renameSuccess: 'Profile renamed',
|
|
renameFailed: 'Failed to rename profile',
|
|
deleteConfirm: 'Are you sure you want to delete profile "{name}"?',
|
|
deleteSuccess: 'Profile deleted',
|
|
deleteFailed: 'Failed to delete profile',
|
|
exportSuccess: 'Profile exported',
|
|
exportFailed: 'Failed to export profile',
|
|
importSuccess: 'Profile imported',
|
|
importFailed: 'Failed to import profile',
|
|
importSelectFile: 'Select archive file',
|
|
importInvalidFile: 'Please select a valid archive (.tar.gz, .tgz, .gz, .zip)',
|
|
name: 'Profile Name',
|
|
namePlaceholder: 'English letters, numbers, hyphens only',
|
|
newName: 'New Name',
|
|
newNamePlaceholder: 'Enter new name',
|
|
cloneFromCurrent: 'Clone from current profile',
|
|
archivePath: 'Archive Path',
|
|
archivePathPlaceholder: 'Server path to archive file',
|
|
importName: 'Profile Name (optional)',
|
|
importNamePlaceholder: 'Leave empty to use archive name',
|
|
active: 'Active',
|
|
model: 'Model',
|
|
gateway: 'Gateway',
|
|
alias: 'Alias',
|
|
provider: 'Provider',
|
|
path: 'Path',
|
|
skills: 'Skills',
|
|
hasEnv: 'Has .env',
|
|
hasSoulMd: 'Has soul.md',
|
|
noProfiles: 'No profiles found. Create one to get started.',
|
|
},
|
|
|
|
// Logs
|
|
logs: {
|
|
title: 'Logs',
|
|
all: 'All',
|
|
searchPlaceholder: 'Search...',
|
|
refresh: 'Refresh',
|
|
noEntries: 'No log entries',
|
|
},
|
|
|
|
// Settings
|
|
settings: {
|
|
title: 'Settings',
|
|
saved: 'Saved',
|
|
saveFailed: 'Save failed',
|
|
tabs: {
|
|
display: 'Display',
|
|
account: 'Account',
|
|
agent: 'Agent',
|
|
memory: 'Memory',
|
|
session: 'Session',
|
|
privacy: 'Privacy',
|
|
apiServer: 'API Server',
|
|
models: 'Models',
|
|
},
|
|
models: {
|
|
apiKey: 'API Key',
|
|
apiKeyPlaceholder: 'Enter API key',
|
|
save: 'Save',
|
|
saved: 'Saved',
|
|
saveFailed: 'Save failed',
|
|
noProviders: 'No providers configured',
|
|
},
|
|
display: {
|
|
streaming: 'Stream Responses',
|
|
streamingHint: 'Show AI replies in real-time',
|
|
compact: 'Compact Mode',
|
|
compactHint: 'Reduce message spacing',
|
|
showReasoning: 'Show Reasoning',
|
|
showReasoningHint: 'Show model thinking process',
|
|
showCost: 'Show Cost',
|
|
showCostHint: 'Show token usage in replies',
|
|
inlineDiffs: 'Inline Diffs',
|
|
inlineDiffsHint: 'Show code changes inline',
|
|
bellOnComplete: 'Completion Sound',
|
|
bellOnCompleteHint: 'Play sound when AI finishes',
|
|
busyInputMode: 'Busy Input Mode',
|
|
busyInputModeHint: 'Allow input while AI is processing',
|
|
theme: 'Theme',
|
|
themeHint: 'Choose light, dark, or follow system preference',
|
|
themeLight: 'Light',
|
|
themeDark: 'Dark',
|
|
themeSystem: 'System',
|
|
},
|
|
agent: {
|
|
maxTurns: 'Max Turns',
|
|
maxTurnsHint: 'Maximum interaction rounds per conversation',
|
|
gatewayTimeout: 'Gateway Timeout',
|
|
gatewayTimeoutHint: 'Request timeout in seconds',
|
|
restartDrainTimeout: 'Restart Drain Timeout',
|
|
restartDrainTimeoutHint: 'Drain timeout before restart in seconds',
|
|
toolEnforcement: 'Tool Enforcement',
|
|
toolEnforcementHint: 'Control tool call execution mode',
|
|
auto: 'Auto',
|
|
always: 'Always',
|
|
never: 'Never',
|
|
},
|
|
memory: {
|
|
enabled: 'Enable Memory',
|
|
enabledHint: 'Allow AI to remember conversation context',
|
|
userProfile: 'User Profile',
|
|
userProfileHint: 'Allow AI to remember user preferences',
|
|
charLimit: 'Memory Char Limit',
|
|
charLimitHint: 'Max characters for MEMORY.md',
|
|
userCharLimit: 'User Profile Char Limit',
|
|
userCharLimitHint: 'Max characters for USER.md',
|
|
},
|
|
session: {
|
|
mode: 'Reset Mode',
|
|
modeHint: 'Trigger condition for session reset',
|
|
modeBoth: 'Idle + Scheduled',
|
|
modeIdle: 'Idle Only',
|
|
modeHourly: 'Scheduled Only',
|
|
idleMinutes: 'Idle Timeout',
|
|
idleMinutesHint: 'Wait time before auto-reset (minutes)',
|
|
atHour: 'Scheduled Reset Time',
|
|
humanOnly: 'Show human sessions only',
|
|
humanOnlyHint: 'Hide sub-agent/session monitor noise by default',
|
|
liveMonitorHumanOnly: 'Live monitor: show human sessions only',
|
|
liveMonitorHumanOnlyHint: 'Hide sub-agent/session monitor noise in the Live monitor by default',
|
|
atHourHint: 'Reset session at this hour daily',
|
|
},
|
|
privacy: {
|
|
redactPii: 'Redact PII',
|
|
redactPiiHint: 'Auto-detect and hide sensitive info (passwords, keys, etc.)',
|
|
},
|
|
apiServer: {
|
|
enable: 'Enable',
|
|
enableHint: 'Enable API server',
|
|
host: 'Host',
|
|
hostHint: 'Listen address',
|
|
port: 'Port',
|
|
portHint: 'Listen port',
|
|
key: 'Key',
|
|
keyHint: 'API access key',
|
|
cors: 'CORS Origins',
|
|
corsHint: 'Allowed cross-origin sources',
|
|
},
|
|
},
|
|
|
|
// Platform channel settings
|
|
platform: {
|
|
requireMention: "Require {'@'}Mention",
|
|
requireMentionGroup: "Require {'@'}mention in groups to respond",
|
|
requireMentionChannel: "Require {'@'}mention in channels to respond",
|
|
requireMentionRoom: "Require {'@'}mention in rooms to respond",
|
|
reactions: 'Reactions',
|
|
reactionsHint: 'React to messages with emoji',
|
|
freeResponseChats: 'Free Response Chats',
|
|
freeResponseChatsHint: "Chat IDs that respond without {'@'}mention (comma-separated)",
|
|
freeResponseChannels: 'Free Response Channels',
|
|
freeResponseChannelsHint: "Channel IDs that respond without {'@'}mention (comma-separated)",
|
|
freeResponseRooms: 'Free Response Rooms',
|
|
freeResponseRoomsHint: "Room IDs that respond without {'@'}mention (comma-separated)",
|
|
mentionPatterns: 'Custom Mention Patterns',
|
|
mentionPatternsHint: 'Additional trigger patterns',
|
|
autoThread: 'Auto Thread',
|
|
autoThreadHint: "Auto-create reply threads after {'@'}mention",
|
|
autoThreadHintRoom: 'Auto-create reply threads in rooms',
|
|
dmMentionThreads: 'DM Mention Threads',
|
|
dmMentionThreadsHint: 'Use thread replies for mentions in DMs',
|
|
allowBots: 'Allow Bot Messages',
|
|
allowBotsHint: 'Respond to messages from other bots',
|
|
allowedChannels: 'Allowed Channels',
|
|
allowedChannelsHint: 'Whitelist channel IDs (comma-separated)',
|
|
ignoredChannels: 'Ignored Channels',
|
|
ignoredChannelsHint: 'Channels where bot never responds (comma-separated)',
|
|
noThreadChannels: 'No-Thread Channels',
|
|
noThreadChannelsHint: 'Channels where bot responds without threads (comma-separated)',
|
|
botToken: 'Bot Token',
|
|
botTokenHint: 'Bot token from developer portal',
|
|
accessToken: 'Access Token',
|
|
accessTokenHint: 'Matrix access token',
|
|
homeserver: 'Homeserver URL',
|
|
homeserverHint: 'Matrix homeserver URL',
|
|
appId: 'App ID',
|
|
appIdHint: 'Feishu App ID',
|
|
appSecret: 'App Secret',
|
|
appSecretHint: 'Feishu App Secret',
|
|
clientId: 'Client ID',
|
|
clientIdHint: 'DingTalk Client ID',
|
|
clientSecret: 'Client Secret',
|
|
clientSecretHint: 'DingTalk Client Secret',
|
|
botId: 'Bot ID',
|
|
botIdHint: 'WeCom Bot ID',
|
|
wecomSecretHint: 'WeCom Bot Secret',
|
|
waEnabled: 'Enable WhatsApp',
|
|
waEnabledHint: 'Enable WhatsApp via QR code pairing',
|
|
weixinToken: 'Weixin Token',
|
|
weixinTokenHint: 'From weixin CLI QR login (hermes weixin)',
|
|
accountId: 'Account ID',
|
|
accountIdHint: 'Weixin account ID',
|
|
qrLogin: 'QR Login',
|
|
qrRelogin: 'Re-login',
|
|
qrFetching: 'Fetching QR code...',
|
|
qrScanHint: 'Scan with WeChat to login',
|
|
qrScanedHint: 'Scaned, please confirm on phone...',
|
|
},
|
|
|
|
// Language
|
|
language: {
|
|
label: 'Language',
|
|
zh: '中文',
|
|
en: 'English',
|
|
},
|
|
|
|
// Terminal
|
|
terminal: {
|
|
sessions: 'Sessions',
|
|
newTab: 'New Terminal',
|
|
closeSession: 'Close this session?',
|
|
sessionExited: 'Exited',
|
|
processExited: 'Process exited with code {code}',
|
|
},
|
|
|
|
// Group Chat
|
|
groupChat: {
|
|
title: 'Group Chat',
|
|
createRoom: 'Create Room',
|
|
joinByCode: 'Join by Code',
|
|
roomName: 'Room Name',
|
|
roomNamePlaceholder: 'Enter room name',
|
|
inviteCode: 'Invite Code',
|
|
autoGenerate: 'Auto-generate',
|
|
noRooms: 'No rooms yet',
|
|
selectOrCreate: 'Select or create a room to start chatting',
|
|
agents: 'Agents',
|
|
addAgent: 'Add Agent',
|
|
selectProfile: 'Select a profile',
|
|
agentAdded: 'Agent added',
|
|
agentAlreadyInRoom: 'Agent already in this room',
|
|
noAgents: 'No agents in this room',
|
|
members: 'members',
|
|
roomCreated: 'Room created',
|
|
roomDeleted: 'Room deleted',
|
|
deleteRoomConfirm: 'Delete this room?',
|
|
you: 'You',
|
|
joined: 'Joined room',
|
|
joinFailed: 'Failed to join room',
|
|
inputPlaceholder: 'Type a message... (Enter to send)',
|
|
enterCode: 'Enter invite code',
|
|
yourName: 'Your Name',
|
|
yourNamePlaceholder: 'Enter your display name',
|
|
yourDescription: 'Description (optional)',
|
|
yourDescriptionPlaceholder: 'Tell others who you are...',
|
|
agentName: 'Agent Name',
|
|
agentNamePlaceholder: 'Custom name (leave empty to use profile name)',
|
|
agentDesc: 'Agent Description',
|
|
agentDescPlaceholder: 'Describe what this agent does...',
|
|
agentReplying: 'is replying...',
|
|
agentCompressing: 'is compressing context...',
|
|
compressionSettings: 'Compression Settings',
|
|
triggerTokens: 'Trigger Tokens',
|
|
triggerTokensDesc: 'Token threshold to trigger context compression',
|
|
maxHistoryTokens: 'Max History Tokens',
|
|
maxHistoryTokensDesc: 'Maximum tokens for compressed context sent to LLM',
|
|
tailMessageCount: 'Tail Message Count',
|
|
tailMessageCountDesc: 'Number of recent messages to keep verbatim after compression',
|
|
compressionConfig: 'Compression Config',
|
|
compressionSaved: 'Compression config saved',
|
|
compressNow: 'Compress Now',
|
|
compressingInProgress: 'Compression in progress, please wait',
|
|
},
|
|
|
|
// Usage
|
|
usage: {
|
|
title: 'Usage Statistics',
|
|
refresh: 'Refresh',
|
|
totalTokens: 'Total Tokens',
|
|
inputTokens: 'Input',
|
|
outputTokens: 'Output',
|
|
totalSessions: 'Total Sessions',
|
|
avgPerDay: '~{n}/day avg',
|
|
estimatedCost: 'Est. Cost',
|
|
cacheHitRate: 'Cache Hit Rate',
|
|
modelBreakdown: 'Model Breakdown',
|
|
dailyTrend: 'Daily Usage (Last 30 Days)',
|
|
date: 'Date',
|
|
tokens: 'Tokens',
|
|
cache: 'Cache',
|
|
sessions: 'Sessions',
|
|
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',
|
|
closePreview: 'Close',
|
|
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',
|
|
downloadFile: 'Download file',
|
|
},
|
|
|
|
// Changelog
|
|
changelog: {
|
|
new_0_4_4_1: 'Add file browser with multi-backend support (local/Docker/SSH/Singularity)',
|
|
new_0_4_4_2: 'Add file download from chat message attachments',
|
|
new_0_4_4_3: 'Add live badge on active chat sessions',
|
|
new_0_4_4_4: 'Add StepFun and Nous Portal provider support',
|
|
new_0_4_4_5: 'Fix special character search queries causing 500 error',
|
|
new_0_4_5_1: 'Add group chat with multi-agent rooms, @mention routing, and typing status recovery',
|
|
new_0_4_5_2: 'Rewrite model-context config to use YAML with context_length setting',
|
|
new_0_4_5_3: 'Add gpt-5.5 to OpenAI Codex model list',
|
|
new_0_4_5_4: 'Replace jobs proxy with local controller and optimize model loading',
|
|
new_0_4_5_5: 'Add i18n support for custom model feature in ModelSelector',
|
|
new_0_4_5_6: 'Fix sidebar i18n missing key warnings',
|
|
new_0_4_5_7: 'Clear all localStorage on logout',
|
|
new_0_4_5_8: 'Add periodic log rotation to prevent unbounded log growth',
|
|
new_0_4_3_1: 'Add username/password login alongside token authentication',
|
|
new_0_4_3_2: 'Add account settings for managing credentials (setup, change password, change username)',
|
|
new_0_4_3_3: 'Add logout button to sidebar',
|
|
new_0_4_3_4: 'Add version changelog popup (click version number)',
|
|
new_0_4_2_1: 'Add token usage tracking, context display, and dynamic context length',
|
|
new_0_4_2_2: 'Add session search modal',
|
|
new_0_4_2_3: 'Restore group chat system with Socket.IO and SQLite persistence',
|
|
new_0_4_2_4: 'Add pinned sessions and live monitor in Chat page',
|
|
new_0_4_2_5: 'Fix builtin provider detection and model matching',
|
|
new_0_4_1_1: 'Fix auth bypass, SPA serving, and provider improvements',
|
|
},
|
|
}
|