0cc31ee999
* 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>
627 lines
21 KiB
TypeScript
627 lines
21 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',
|
|
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',
|
|
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',
|
|
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)',
|
|
},
|
|
|
|
// 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.',
|
|
noProviders: 'No providers found. Add a custom provider to get started.',
|
|
builtIn: 'Built-in',
|
|
customType: 'Custom',
|
|
provider: 'Provider',
|
|
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}',
|
|
},
|
|
|
|
// 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',
|
|
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: {
|
|
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',
|
|
},
|
|
}
|