Complete Chinese localization for remaining UI components
- Add comprehensive Chinese translations for Host Manager component - Translate all form labels, buttons, and descriptions - Add translations for SSH configuration warnings and instructions - Localize tunnel connection settings and port forwarding options - Localize SSH Tools panel - Translate key recording functionality - Add translations for settings and configuration options - Translate homepage welcome messages and navigation elements - Add Chinese translations for login success messages - Localize "Updates & Releases" section title - Translate sidebar "Host Manager" button - Fix translation key display issues - Remove duplicate translation keys in both language files - Ensure all components properly reference translation keys - Fix hosts.tunnelConnections key mapping This completes the full Chinese localization of the Termix application, achieving near 100% UI translation coverage while maintaining English as the default language.
This commit is contained in:
@@ -1,4 +1,21 @@
|
|||||||
{
|
{
|
||||||
|
"sshTools": {
|
||||||
|
"title": "SSH Tools",
|
||||||
|
"closeTools": "Close SSH Tools",
|
||||||
|
"keyRecording": "Key Recording",
|
||||||
|
"startKeyRecording": "Start Key Recording",
|
||||||
|
"stopKeyRecording": "Stop Key Recording",
|
||||||
|
"selectTerminals": "Select terminals:",
|
||||||
|
"typeCommands": "Type commands (all keys supported):",
|
||||||
|
"commandsWillBeSent": "Commands will be sent to {{count}} selected terminal(s).",
|
||||||
|
"settings": "Settings",
|
||||||
|
"enableRightClickCopyPaste": "Enable right‑click copy/paste",
|
||||||
|
"shareIdeas": "Have ideas for what should come next for ssh tools? Share them on"
|
||||||
|
},
|
||||||
|
"homepage": {
|
||||||
|
"loggedInTitle": "Logged in!",
|
||||||
|
"loggedInMessage": "You are logged in! Use the sidebar to access all available tools. To get started, create an SSH Host in the SSH Manager tab. Once created, you can connect to that host using the other apps in the sidebar."
|
||||||
|
},
|
||||||
"common": {
|
"common": {
|
||||||
"close": "Close",
|
"close": "Close",
|
||||||
"online": "Online",
|
"online": "Online",
|
||||||
@@ -91,6 +108,7 @@
|
|||||||
"splitScreen": "Split Screen",
|
"splitScreen": "Split Screen",
|
||||||
"closeTab": "Close Tab",
|
"closeTab": "Close Tab",
|
||||||
"sshManager": "SSH Manager",
|
"sshManager": "SSH Manager",
|
||||||
|
"hostManager": "Host Manager",
|
||||||
"cannotSplitTab": "Cannot split this tab"
|
"cannotSplitTab": "Cannot split this tab"
|
||||||
},
|
},
|
||||||
"admin": {
|
"admin": {
|
||||||
@@ -199,7 +217,46 @@
|
|||||||
"connectionSuccess": "Connection Successful",
|
"connectionSuccess": "Connection Successful",
|
||||||
"connectionDetails": "Connection Details",
|
"connectionDetails": "Connection Details",
|
||||||
"organization": "Organization",
|
"organization": "Organization",
|
||||||
"addTags": "Add tags (space to add)"
|
"addTags": "Add tags (space to add)",
|
||||||
|
"sourcePort": "Source Port",
|
||||||
|
"endpointPort": "Endpoint Port",
|
||||||
|
"retryInterval": "Retry Interval (seconds)",
|
||||||
|
"connection": "Connection",
|
||||||
|
"remove": "Remove",
|
||||||
|
"addConnection": "Add Connection",
|
||||||
|
"sshpassRequired": "Sshpass Required For Password Authentication",
|
||||||
|
"sshpassInstallCommand": "Install Command: sudo apt install sshpass",
|
||||||
|
"sshServerConfig": "SSH Server Configuration Required",
|
||||||
|
"sshServerConfigInstructions": "Run the following commands to allow password authentication:",
|
||||||
|
"sshConfigCommand1": "sudo sed -i 's/#PasswordAuthentication yes/PasswordAuthentication yes/' /etc/ssh/sshd_config",
|
||||||
|
"sshConfigCommand2": "sudo systemctl restart sshd",
|
||||||
|
"localPortForwarding": "Local Port Forwarding",
|
||||||
|
"localPortForwardingDesc": "Forward a local port to a remote server through the SSH connection",
|
||||||
|
"remotePortForwarding": "Remote Port Forwarding",
|
||||||
|
"remotePortForwardingDesc": "Forward a remote port to a local server through the SSH connection",
|
||||||
|
"dynamicPortForwarding": "Dynamic Port Forwarding (SOCKS Proxy)",
|
||||||
|
"dynamicPortForwardingDesc": "Create a SOCKS proxy on the local machine to route traffic through the SSH connection",
|
||||||
|
"bindAddress": "Bind Address",
|
||||||
|
"hostViewer": "Host Viewer",
|
||||||
|
"configuration": "Configuration",
|
||||||
|
"maxRetries": "Max Retries",
|
||||||
|
"tunnelConnections": "Tunnel Connections",
|
||||||
|
"enableTerminalDesc": "Enable/disable host visibility in Terminal tab",
|
||||||
|
"enableTunnelDesc": "Enable/disable host visibility in Tunnel tab",
|
||||||
|
"enableFileManagerDesc": "Enable/disable host visibility in File Manager tab",
|
||||||
|
"autoStartDesc": "Automatically start this tunnel when the container launches",
|
||||||
|
"defaultPathDesc": "Default directory when opening file manager for this host",
|
||||||
|
"otherInstallMethods": "Other installation methods:",
|
||||||
|
"sshpassOSInstructions": {
|
||||||
|
"centos": "CentOS/RHEL/Fedora: sudo yum install sshpass or sudo dnf install sshpass",
|
||||||
|
"macos": "macOS: brew install hudochenkov/sshpass/sshpass",
|
||||||
|
"windows": "Windows: Use WSL or consider SSH key authentication"
|
||||||
|
},
|
||||||
|
"sshServerConfigReverse": "For reverse SSH tunnels, the endpoint SSH server must allow:",
|
||||||
|
"gatewayPorts": "GatewayPorts yes (bind remote ports)",
|
||||||
|
"allowTcpForwarding": "AllowTcpForwarding yes (port forwarding)",
|
||||||
|
"permitRootLogin": "PermitRootLogin yes (if using root)",
|
||||||
|
"editSshConfig": "Edit /etc/ssh/sshd_config and restart SSH: sudo systemctl restart sshd"
|
||||||
},
|
},
|
||||||
"terminal": {
|
"terminal": {
|
||||||
"title": "Terminal",
|
"title": "Terminal",
|
||||||
@@ -619,7 +676,6 @@
|
|||||||
"noSshHosts": "No SSH Hosts",
|
"noSshHosts": "No SSH Hosts",
|
||||||
"sshHosts": "SSH Hosts",
|
"sshHosts": "SSH Hosts",
|
||||||
"importSshHosts": "Import SSH Hosts from JSON",
|
"importSshHosts": "Import SSH Hosts from JSON",
|
||||||
"hostViewer": "Host Viewer",
|
|
||||||
"clientId": "Client ID",
|
"clientId": "Client ID",
|
||||||
"clientSecret": "Client Secret",
|
"clientSecret": "Client Secret",
|
||||||
"error": "Error",
|
"error": "Error",
|
||||||
@@ -677,11 +733,6 @@
|
|||||||
"maxRetries": "Max Retries",
|
"maxRetries": "Max Retries",
|
||||||
"upload": "Upload",
|
"upload": "Upload",
|
||||||
"updateKey": "Update Key",
|
"updateKey": "Update Key",
|
||||||
"hostViewer": "Host Viewer",
|
|
||||||
"sshpassRequired": "Sshpass Required For Password Authentication",
|
|
||||||
"sshServerConfigRequired": "SSH Server Configuration Required",
|
|
||||||
"sshManagerAlreadyOpen": "SSH Manager already open",
|
|
||||||
"disabledDuringSplitScreen": "Disabled during split screen",
|
|
||||||
"productionFolder": "Production",
|
"productionFolder": "Production",
|
||||||
"databaseServer": "Database Server",
|
"databaseServer": "Database Server",
|
||||||
"unknownError": "Unknown error",
|
"unknownError": "Unknown error",
|
||||||
|
|||||||
@@ -1,4 +1,21 @@
|
|||||||
{
|
{
|
||||||
|
"sshTools": {
|
||||||
|
"title": "SSH 工具",
|
||||||
|
"closeTools": "关闭 SSH 工具",
|
||||||
|
"keyRecording": "按键录制",
|
||||||
|
"startKeyRecording": "开始按键录制",
|
||||||
|
"stopKeyRecording": "停止按键录制",
|
||||||
|
"selectTerminals": "选择终端:",
|
||||||
|
"typeCommands": "输入命令(支持所有按键):",
|
||||||
|
"commandsWillBeSent": "命令将发送到 {{count}} 个选中的终端。",
|
||||||
|
"settings": "设置",
|
||||||
|
"enableRightClickCopyPaste": "启用右键复制/粘贴",
|
||||||
|
"shareIdeas": "对 SSH 工具有什么想法?在此分享"
|
||||||
|
},
|
||||||
|
"homepage": {
|
||||||
|
"loggedInTitle": "登录成功!",
|
||||||
|
"loggedInMessage": "您已登录!使用侧边栏访问所有可用工具。要开始使用,请在 SSH 管理器选项卡中创建 SSH 主机。创建后,您可以使用侧边栏中的其他应用程序连接到该主机。"
|
||||||
|
},
|
||||||
"common": {
|
"common": {
|
||||||
"close": "关闭",
|
"close": "关闭",
|
||||||
"online": "在线",
|
"online": "在线",
|
||||||
@@ -91,6 +108,7 @@
|
|||||||
"splitScreen": "分屏",
|
"splitScreen": "分屏",
|
||||||
"closeTab": "关闭标签页",
|
"closeTab": "关闭标签页",
|
||||||
"sshManager": "SSH 管理器",
|
"sshManager": "SSH 管理器",
|
||||||
|
"hostManager": "主机管理器",
|
||||||
"cannotSplitTab": "无法分割此标签页"
|
"cannotSplitTab": "无法分割此标签页"
|
||||||
},
|
},
|
||||||
"admin": {
|
"admin": {
|
||||||
@@ -199,7 +217,46 @@
|
|||||||
"connectionSuccess": "连接成功",
|
"connectionSuccess": "连接成功",
|
||||||
"connectionDetails": "连接详情",
|
"connectionDetails": "连接详情",
|
||||||
"organization": "组织管理",
|
"organization": "组织管理",
|
||||||
"addTags": "添加标签(空格添加)"
|
"addTags": "添加标签(空格添加)",
|
||||||
|
"sourcePort": "源端口",
|
||||||
|
"endpointPort": "目标端口",
|
||||||
|
"retryInterval": "重试间隔(秒)",
|
||||||
|
"connection": "连接",
|
||||||
|
"remove": "移除",
|
||||||
|
"addConnection": "添加连接",
|
||||||
|
"sshpassRequired": "密码认证需要安装 Sshpass",
|
||||||
|
"sshpassInstallCommand": "安装命令:sudo apt install sshpass",
|
||||||
|
"sshServerConfig": "需要配置 SSH 服务器",
|
||||||
|
"sshServerConfigInstructions": "运行以下命令以允许密码认证:",
|
||||||
|
"sshConfigCommand1": "sudo sed -i 's/#PasswordAuthentication yes/PasswordAuthentication yes/' /etc/ssh/sshd_config",
|
||||||
|
"sshConfigCommand2": "sudo systemctl restart sshd",
|
||||||
|
"localPortForwarding": "本地端口转发",
|
||||||
|
"localPortForwardingDesc": "通过 SSH 连接将本地端口转发到远程服务器",
|
||||||
|
"remotePortForwarding": "远程端口转发",
|
||||||
|
"remotePortForwardingDesc": "通过 SSH 连接将远程端口转发到本地服务器",
|
||||||
|
"dynamicPortForwarding": "动态端口转发(SOCKS 代理)",
|
||||||
|
"dynamicPortForwardingDesc": "在本地计算机上创建 SOCKS 代理,通过 SSH 连接路由流量",
|
||||||
|
"bindAddress": "绑定地址",
|
||||||
|
"hostViewer": "主机查看器",
|
||||||
|
"configuration": "配置",
|
||||||
|
"maxRetries": "最大重试次数",
|
||||||
|
"tunnelConnections": "隧道连接",
|
||||||
|
"enableTerminalDesc": "启用/禁用在终端选项卡中显示此主机",
|
||||||
|
"enableTunnelDesc": "启用/禁用在隧道选项卡中显示此主机",
|
||||||
|
"enableFileManagerDesc": "启用/禁用在文件管理器选项卡中显示此主机",
|
||||||
|
"autoStartDesc": "容器启动时自动启动此隧道",
|
||||||
|
"defaultPathDesc": "打开此主机文件管理器时的默认目录",
|
||||||
|
"otherInstallMethods": "其他安装方法:",
|
||||||
|
"sshpassOSInstructions": {
|
||||||
|
"centos": "CentOS/RHEL/Fedora: sudo yum install sshpass 或 sudo dnf install sshpass",
|
||||||
|
"macos": "macOS: brew install hudochenkov/sshpass/sshpass",
|
||||||
|
"windows": "Windows: 使用 WSL 或考虑使用 SSH 密钥认证"
|
||||||
|
},
|
||||||
|
"sshServerConfigReverse": "对于反向 SSH 隧道,端点 SSH 服务器必须允许:",
|
||||||
|
"gatewayPorts": "GatewayPorts yes(绑定远程端口)",
|
||||||
|
"allowTcpForwarding": "AllowTcpForwarding yes(端口转发)",
|
||||||
|
"permitRootLogin": "PermitRootLogin yes(如果使用 root)",
|
||||||
|
"editSshConfig": "编辑 /etc/ssh/sshd_config 并重启 SSH: sudo systemctl restart sshd"
|
||||||
},
|
},
|
||||||
"terminal": {
|
"terminal": {
|
||||||
"title": "终端",
|
"title": "终端",
|
||||||
@@ -619,7 +676,6 @@
|
|||||||
"noSshHosts": "没有 SSH 主机",
|
"noSshHosts": "没有 SSH 主机",
|
||||||
"sshHosts": "SSH 主机",
|
"sshHosts": "SSH 主机",
|
||||||
"importSshHosts": "从 JSON 导入 SSH 主机",
|
"importSshHosts": "从 JSON 导入 SSH 主机",
|
||||||
"hostViewer": "主机查看器",
|
|
||||||
"clientId": "客户端 ID",
|
"clientId": "客户端 ID",
|
||||||
"clientSecret": "客户端密钥",
|
"clientSecret": "客户端密钥",
|
||||||
"error": "错误",
|
"error": "错误",
|
||||||
@@ -677,7 +733,6 @@
|
|||||||
"maxRetries": "最大重试次数",
|
"maxRetries": "最大重试次数",
|
||||||
"upload": "上传",
|
"upload": "上传",
|
||||||
"updateKey": "更新密钥",
|
"updateKey": "更新密钥",
|
||||||
"hostViewer": "主机查看器",
|
|
||||||
"sshpassRequired": "密码认证需要 Sshpass",
|
"sshpassRequired": "密码认证需要 Sshpass",
|
||||||
"sshServerConfigRequired": "需要 SSH 服务器配置",
|
"sshServerConfigRequired": "需要 SSH 服务器配置",
|
||||||
"sshManagerAlreadyOpen": "SSH 管理器已打开",
|
"sshManagerAlreadyOpen": "SSH 管理器已打开",
|
||||||
|
|||||||
@@ -701,7 +701,7 @@ export function HostManagerHostEditor({editingHost, onFormSubmit}: SSHManagerHos
|
|||||||
name="enableTerminal"
|
name="enableTerminal"
|
||||||
render={({field}) => (
|
render={({field}) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>Enable Terminal</FormLabel>
|
<FormLabel>{t('hosts.enableTerminal')}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Switch
|
<Switch
|
||||||
checked={field.value}
|
checked={field.value}
|
||||||
@@ -709,7 +709,7 @@ export function HostManagerHostEditor({editingHost, onFormSubmit}: SSHManagerHos
|
|||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormDescription>
|
<FormDescription>
|
||||||
Enable/disable host visibility in Terminal tab.
|
{t('hosts.enableTerminalDesc')}
|
||||||
</FormDescription>
|
</FormDescription>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
@@ -721,7 +721,7 @@ export function HostManagerHostEditor({editingHost, onFormSubmit}: SSHManagerHos
|
|||||||
name="enableTunnel"
|
name="enableTunnel"
|
||||||
render={({field}) => (
|
render={({field}) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>Enable Tunnel</FormLabel>
|
<FormLabel>{t('hosts.enableTunnel')}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Switch
|
<Switch
|
||||||
checked={field.value}
|
checked={field.value}
|
||||||
@@ -729,7 +729,7 @@ export function HostManagerHostEditor({editingHost, onFormSubmit}: SSHManagerHos
|
|||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormDescription>
|
<FormDescription>
|
||||||
Enable/disable host visibility in Tunnel tab.
|
{t('hosts.enableTunnelDesc')}
|
||||||
</FormDescription>
|
</FormDescription>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
@@ -739,44 +739,27 @@ export function HostManagerHostEditor({editingHost, onFormSubmit}: SSHManagerHos
|
|||||||
<>
|
<>
|
||||||
<Alert className="mt-4">
|
<Alert className="mt-4">
|
||||||
<AlertDescription>
|
<AlertDescription>
|
||||||
<strong>Sshpass Required For Password Authentication</strong>
|
<strong>{t('hosts.sshpassRequired')}</strong>
|
||||||
<div>
|
<div>
|
||||||
For password-based SSH authentication, sshpass must be installed on
|
{t('hosts.sshpassInstallCommand')}
|
||||||
both the local and remote servers. Install with: <code
|
|
||||||
className="bg-muted px-1 rounded inline">sudo apt install
|
|
||||||
sshpass</code> (Debian/Ubuntu) or the equivalent for your OS.
|
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-2">
|
<div className="mt-2">
|
||||||
<strong>Other installation methods:</strong>
|
<strong>{t('hosts.otherInstallMethods')}</strong>
|
||||||
<div>• CentOS/RHEL/Fedora: <code
|
<div>• {t('hosts.sshpassOSInstructions.centos')}</div>
|
||||||
className="bg-muted px-1 rounded inline">sudo yum install
|
<div>• {t('hosts.sshpassOSInstructions.macos')}</div>
|
||||||
sshpass</code> or <code
|
<div>• {t('hosts.sshpassOSInstructions.windows')}</div>
|
||||||
className="bg-muted px-1 rounded inline">sudo dnf install
|
|
||||||
sshpass</code></div>
|
|
||||||
<div>• macOS: <code className="bg-muted px-1 rounded inline">brew
|
|
||||||
install hudochenkov/sshpass/sshpass</code></div>
|
|
||||||
<div>• Windows: Use WSL or consider SSH key authentication</div>
|
|
||||||
</div>
|
</div>
|
||||||
</AlertDescription>
|
</AlertDescription>
|
||||||
</Alert>
|
</Alert>
|
||||||
|
|
||||||
<Alert className="mt-4">
|
<Alert className="mt-4">
|
||||||
<AlertDescription>
|
<AlertDescription>
|
||||||
<strong>SSH Server Configuration Required</strong>
|
<strong>{t('hosts.sshServerConfig')}</strong>
|
||||||
<div>For reverse SSH tunnels, the endpoint SSH server must allow:</div>
|
<div>{t('hosts.sshServerConfigReverse')}</div>
|
||||||
<div>• <code className="bg-muted px-1 rounded inline">GatewayPorts
|
<div>• <code className="bg-muted px-1 rounded inline">{t('hosts.gatewayPorts')}</code></div>
|
||||||
yes</code> (bind remote ports)
|
<div>• <code className="bg-muted px-1 rounded inline">{t('hosts.allowTcpForwarding')}</code></div>
|
||||||
</div>
|
<div>• <code className="bg-muted px-1 rounded inline">{t('hosts.permitRootLogin')}</code></div>
|
||||||
<div>• <code className="bg-muted px-1 rounded inline">AllowTcpForwarding
|
<div className="mt-2">{t('hosts.editSshConfig')}</div>
|
||||||
yes</code> (port forwarding)
|
|
||||||
</div>
|
|
||||||
<div>• <code className="bg-muted px-1 rounded inline">PermitRootLogin
|
|
||||||
yes</code> (if using root)
|
|
||||||
</div>
|
|
||||||
<div className="mt-2">Edit <code
|
|
||||||
className="bg-muted px-1 rounded inline">/etc/ssh/sshd_config</code> and
|
|
||||||
restart SSH: <code className="bg-muted px-1 rounded inline">sudo
|
|
||||||
systemctl restart sshd</code></div>
|
|
||||||
</AlertDescription>
|
</AlertDescription>
|
||||||
</Alert>
|
</Alert>
|
||||||
|
|
||||||
@@ -793,7 +776,7 @@ export function HostManagerHostEditor({editingHost, onFormSubmit}: SSHManagerHos
|
|||||||
className="p-4 border rounded-lg bg-muted/50">
|
className="p-4 border rounded-lg bg-muted/50">
|
||||||
<div
|
<div
|
||||||
className="flex items-center justify-between mb-3">
|
className="flex items-center justify-between mb-3">
|
||||||
<h4 className="text-sm font-bold">Connection {index + 1}</h4>
|
<h4 className="text-sm font-bold">{t('hosts.connection')} {index + 1}</h4>
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
@@ -803,7 +786,7 @@ export function HostManagerHostEditor({editingHost, onFormSubmit}: SSHManagerHos
|
|||||||
field.onChange(newConnections);
|
field.onChange(newConnections);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Remove
|
{t('hosts.remove')}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-12 gap-4">
|
<div className="grid grid-cols-12 gap-4">
|
||||||
@@ -812,7 +795,7 @@ export function HostManagerHostEditor({editingHost, onFormSubmit}: SSHManagerHos
|
|||||||
name={`tunnelConnections.${index}.sourcePort`}
|
name={`tunnelConnections.${index}.sourcePort`}
|
||||||
render={({field: sourcePortField}) => (
|
render={({field: sourcePortField}) => (
|
||||||
<FormItem className="col-span-4">
|
<FormItem className="col-span-4">
|
||||||
<FormLabel>Source Port
|
<FormLabel>{t('hosts.sourcePort')}
|
||||||
(Source refers to the Current
|
(Source refers to the Current
|
||||||
Connection Details in the
|
Connection Details in the
|
||||||
General tab)</FormLabel>
|
General tab)</FormLabel>
|
||||||
@@ -828,7 +811,7 @@ export function HostManagerHostEditor({editingHost, onFormSubmit}: SSHManagerHos
|
|||||||
name={`tunnelConnections.${index}.endpointPort`}
|
name={`tunnelConnections.${index}.endpointPort`}
|
||||||
render={({field: endpointPortField}) => (
|
render={({field: endpointPortField}) => (
|
||||||
<FormItem className="col-span-4">
|
<FormItem className="col-span-4">
|
||||||
<FormLabel>Endpoint Port
|
<FormLabel>{t('hosts.endpointPort')}
|
||||||
(Remote)</FormLabel>
|
(Remote)</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input
|
<Input
|
||||||
@@ -911,7 +894,7 @@ export function HostManagerHostEditor({editingHost, onFormSubmit}: SSHManagerHos
|
|||||||
name={`tunnelConnections.${index}.maxRetries`}
|
name={`tunnelConnections.${index}.maxRetries`}
|
||||||
render={({field: maxRetriesField}) => (
|
render={({field: maxRetriesField}) => (
|
||||||
<FormItem className="col-span-4">
|
<FormItem className="col-span-4">
|
||||||
<FormLabel>Max Retries</FormLabel>
|
<FormLabel>{t('hosts.maxRetries', 'Max Retries')}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input
|
<Input
|
||||||
placeholder="3" {...maxRetriesField} />
|
placeholder="3" {...maxRetriesField} />
|
||||||
@@ -928,8 +911,7 @@ export function HostManagerHostEditor({editingHost, onFormSubmit}: SSHManagerHos
|
|||||||
name={`tunnelConnections.${index}.retryInterval`}
|
name={`tunnelConnections.${index}.retryInterval`}
|
||||||
render={({field: retryIntervalField}) => (
|
render={({field: retryIntervalField}) => (
|
||||||
<FormItem className="col-span-4">
|
<FormItem className="col-span-4">
|
||||||
<FormLabel>Retry Interval
|
<FormLabel>{t('hosts.retryInterval')}</FormLabel>
|
||||||
(seconds)</FormLabel>
|
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input
|
<Input
|
||||||
placeholder="10" {...retryIntervalField} />
|
placeholder="10" {...retryIntervalField} />
|
||||||
@@ -955,8 +937,7 @@ export function HostManagerHostEditor({editingHost, onFormSubmit}: SSHManagerHos
|
|||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormDescription>
|
<FormDescription>
|
||||||
Automatically start this tunnel
|
{t('hosts.autoStartDesc')}
|
||||||
when the container launches.
|
|
||||||
</FormDescription>
|
</FormDescription>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
@@ -978,7 +959,7 @@ export function HostManagerHostEditor({editingHost, onFormSubmit}: SSHManagerHos
|
|||||||
}]);
|
}]);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Add Tunnel Connection
|
{t('hosts.addConnection')}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
@@ -996,7 +977,7 @@ export function HostManagerHostEditor({editingHost, onFormSubmit}: SSHManagerHos
|
|||||||
name="enableFileManager"
|
name="enableFileManager"
|
||||||
render={({field}) => (
|
render={({field}) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>Enable File Manager</FormLabel>
|
<FormLabel>{t('hosts.enableFileManager')}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Switch
|
<Switch
|
||||||
checked={field.value}
|
checked={field.value}
|
||||||
@@ -1004,7 +985,7 @@ export function HostManagerHostEditor({editingHost, onFormSubmit}: SSHManagerHos
|
|||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormDescription>
|
<FormDescription>
|
||||||
Enable/disable host visibility in File Manager tab.
|
{t('hosts.enableFileManagerDesc')}
|
||||||
</FormDescription>
|
</FormDescription>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
@@ -1017,12 +998,11 @@ export function HostManagerHostEditor({editingHost, onFormSubmit}: SSHManagerHos
|
|||||||
name="defaultPath"
|
name="defaultPath"
|
||||||
render={({field}) => (
|
render={({field}) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>Default Path</FormLabel>
|
<FormLabel>{t('hosts.defaultPath')}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder={t('placeholders.homePath')} {...field} />
|
<Input placeholder={t('placeholders.homePath')} {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormDescription>Set default directory shown when connected via
|
<FormDescription>{t('hosts.defaultPathDesc')}</FormDescription>
|
||||||
File Manager</FormDescription>
|
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import {HomepageUpdateLog} from "@/ui/Homepage/HompageUpdateLog.tsx";
|
|||||||
import {HomepageAlertManager} from "@/ui/Homepage/HomepageAlertManager.tsx";
|
import {HomepageAlertManager} from "@/ui/Homepage/HomepageAlertManager.tsx";
|
||||||
import {Button} from "@/components/ui/button.tsx";
|
import {Button} from "@/components/ui/button.tsx";
|
||||||
import { getUserInfo, getDatabaseHealth } from "@/ui/main-axios.ts";
|
import { getUserInfo, getDatabaseHealth } from "@/ui/main-axios.ts";
|
||||||
|
import {useTranslation} from "react-i18next";
|
||||||
|
|
||||||
interface HomepageProps {
|
interface HomepageProps {
|
||||||
onSelectView: (view: string) => void;
|
onSelectView: (view: string) => void;
|
||||||
@@ -32,6 +33,7 @@ export function Homepage({
|
|||||||
onAuthSuccess,
|
onAuthSuccess,
|
||||||
isTopbarOpen = true
|
isTopbarOpen = true
|
||||||
}: HomepageProps): React.ReactElement {
|
}: HomepageProps): React.ReactElement {
|
||||||
|
const {t} = useTranslation();
|
||||||
const [loggedIn, setLoggedIn] = useState(isAuthenticated);
|
const [loggedIn, setLoggedIn] = useState(isAuthenticated);
|
||||||
const [isAdmin, setIsAdmin] = useState(false);
|
const [isAdmin, setIsAdmin] = useState(false);
|
||||||
const [username, setUsername] = useState<string | null>(null);
|
const [username, setUsername] = useState<string | null>(null);
|
||||||
@@ -95,11 +97,9 @@ export function Homepage({
|
|||||||
<div className="flex flex-col items-center gap-6 w-[400px]">
|
<div className="flex flex-col items-center gap-6 w-[400px]">
|
||||||
<div
|
<div
|
||||||
className="text-center bg-[#18181b] border-2 border-[#303032] rounded-lg p-6 w-full shadow-lg">
|
className="text-center bg-[#18181b] border-2 border-[#303032] rounded-lg p-6 w-full shadow-lg">
|
||||||
<h3 className="text-xl font-bold mb-3 text-white">Logged in!</h3>
|
<h3 className="text-xl font-bold mb-3 text-white">{t('homepage.loggedInTitle')}</h3>
|
||||||
<p className="text-gray-300 leading-relaxed">
|
<p className="text-gray-300 leading-relaxed">
|
||||||
You are logged in! Use the sidebar to access all available tools. To get started,
|
{t('homepage.loggedInMessage')}
|
||||||
create an SSH Host in the SSH Manager tab. Once created, you can connect to that
|
|
||||||
host using the other apps in the sidebar.
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import {Alert, AlertDescription, AlertTitle} from "@/components/ui/alert.tsx";
|
|||||||
import {Button} from "@/components/ui/button.tsx";
|
import {Button} from "@/components/ui/button.tsx";
|
||||||
import {Separator} from "@/components/ui/separator.tsx";
|
import {Separator} from "@/components/ui/separator.tsx";
|
||||||
import { getReleasesRSS, getVersionInfo } from "@/ui/main-axios.ts";
|
import { getReleasesRSS, getVersionInfo } from "@/ui/main-axios.ts";
|
||||||
|
import {useTranslation} from "react-i18next";
|
||||||
|
|
||||||
interface HomepageUpdateLogProps extends React.ComponentProps<"div"> {
|
interface HomepageUpdateLogProps extends React.ComponentProps<"div"> {
|
||||||
loggedIn: boolean;
|
loggedIn: boolean;
|
||||||
@@ -51,6 +52,7 @@ interface VersionResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function HomepageUpdateLog({loggedIn}: HomepageUpdateLogProps) {
|
export function HomepageUpdateLog({loggedIn}: HomepageUpdateLogProps) {
|
||||||
|
const {t} = useTranslation();
|
||||||
const [releases, setReleases] = useState<RSSResponse | null>(null);
|
const [releases, setReleases] = useState<RSSResponse | null>(null);
|
||||||
const [versionInfo, setVersionInfo] = useState<VersionResponse | null>(null);
|
const [versionInfo, setVersionInfo] = useState<VersionResponse | null>(null);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
@@ -90,7 +92,7 @@ export function HomepageUpdateLog({loggedIn}: HomepageUpdateLogProps) {
|
|||||||
return (
|
return (
|
||||||
<div className="w-[400px] h-[600px] flex flex-col border-2 border-[#303032] rounded-lg bg-[#18181b] p-4 shadow-lg">
|
<div className="w-[400px] h-[600px] flex flex-col border-2 border-[#303032] rounded-lg bg-[#18181b] p-4 shadow-lg">
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-lg font-bold mb-3 text-white">Updates & Releases</h3>
|
<h3 className="text-lg font-bold mb-3 text-white">{t('common.updatesAndReleases')}</h3>
|
||||||
|
|
||||||
<Separator className="p-0.25 mt-3 mb-3 bg-[#303032]"/>
|
<Separator className="p-0.25 mt-3 mb-3 bg-[#303032]"/>
|
||||||
|
|
||||||
|
|||||||
@@ -433,9 +433,9 @@ export function LeftSidebar({
|
|||||||
<SidebarGroup className="!m-0 !p-0 !-mb-2">
|
<SidebarGroup className="!m-0 !p-0 !-mb-2">
|
||||||
<Button className="m-2 flex flex-row font-semibold border-2 !border-[#303032]" variant="outline"
|
<Button className="m-2 flex flex-row font-semibold border-2 !border-[#303032]" variant="outline"
|
||||||
onClick={openSshManagerTab} disabled={!!sshManagerTab || isSplitScreenActive}
|
onClick={openSshManagerTab} disabled={!!sshManagerTab || isSplitScreenActive}
|
||||||
title={sshManagerTab ? 'SSH Manager already open' : isSplitScreenActive ? 'Disabled during split screen' : undefined}>
|
title={sshManagerTab ? t('interface.sshManagerAlreadyOpen') : isSplitScreenActive ? t('interface.disabledDuringSplitScreen') : undefined}>
|
||||||
<HardDrive strokeWidth="2.5"/>
|
<HardDrive strokeWidth="2.5"/>
|
||||||
Host Manager
|
{t('nav.hostManager')}
|
||||||
</Button>
|
</Button>
|
||||||
</SidebarGroup>
|
</SidebarGroup>
|
||||||
<Separator className="p-0.25"/>
|
<Separator className="p-0.25"/>
|
||||||
|
|||||||
@@ -330,13 +330,13 @@ export function TopNavbar({isTopbarOpen, setIsTopbarOpen}: TopNavbarProps): Reac
|
|||||||
onClick={(e) => e.stopPropagation()}
|
onClick={(e) => e.stopPropagation()}
|
||||||
>
|
>
|
||||||
<div className="flex items-center justify-between p-4 border-b border-[#303032]">
|
<div className="flex items-center justify-between p-4 border-b border-[#303032]">
|
||||||
<h2 className="text-lg font-semibold text-white">SSH Tools</h2>
|
<h2 className="text-lg font-semibold text-white">{t('sshTools.title')}</h2>
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => setToolsSheetOpen(false)}
|
onClick={() => setToolsSheetOpen(false)}
|
||||||
className="h-8 w-8 p-0 hover:bg-red-500 hover:text-white transition-colors flex items-center justify-center"
|
className="h-8 w-8 p-0 hover:bg-red-500 hover:text-white transition-colors flex items-center justify-center"
|
||||||
title="Close SSH Tools"
|
title={t('sshTools.closeTools')}
|
||||||
>
|
>
|
||||||
<span className="text-lg font-bold leading-none">×</span>
|
<span className="text-lg font-bold leading-none">×</span>
|
||||||
</Button>
|
</Button>
|
||||||
@@ -345,7 +345,7 @@ export function TopNavbar({isTopbarOpen, setIsTopbarOpen}: TopNavbarProps): Reac
|
|||||||
<div className="flex-1 overflow-y-auto p-4">
|
<div className="flex-1 overflow-y-auto p-4">
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<h1 className="font-semibold">
|
<h1 className="font-semibold">
|
||||||
Key Recording
|
{t('sshTools.keyRecording')}
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
@@ -357,7 +357,7 @@ export function TopNavbar({isTopbarOpen, setIsTopbarOpen}: TopNavbarProps): Reac
|
|||||||
className="flex-1"
|
className="flex-1"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
>
|
>
|
||||||
Start Key Recording
|
{t('sshTools.startKeyRecording')}
|
||||||
</Button>
|
</Button>
|
||||||
) : (
|
) : (
|
||||||
<Button
|
<Button
|
||||||
@@ -365,7 +365,7 @@ export function TopNavbar({isTopbarOpen, setIsTopbarOpen}: TopNavbarProps): Reac
|
|||||||
className="flex-1"
|
className="flex-1"
|
||||||
variant="destructive"
|
variant="destructive"
|
||||||
>
|
>
|
||||||
Stop Key Recording
|
{t('sshTools.stopKeyRecording')}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -373,8 +373,7 @@ export function TopNavbar({isTopbarOpen, setIsTopbarOpen}: TopNavbarProps): Reac
|
|||||||
{isRecording && (
|
{isRecording && (
|
||||||
<>
|
<>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<label className="text-sm font-medium text-white">Select
|
<label className="text-sm font-medium text-white">{t('sshTools.selectTerminals')}</label>
|
||||||
terminals:</label>
|
|
||||||
<div className="flex flex-wrap gap-2 max-h-32 overflow-y-auto mt-2">
|
<div className="flex flex-wrap gap-2 max-h-32 overflow-y-auto mt-2">
|
||||||
{terminalTabs.map(tab => (
|
{terminalTabs.map(tab => (
|
||||||
<Button
|
<Button
|
||||||
@@ -396,8 +395,7 @@ export function TopNavbar({isTopbarOpen, setIsTopbarOpen}: TopNavbarProps): Reac
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<label className="text-sm font-medium text-white">Type commands (all
|
<label className="text-sm font-medium text-white">{t('sshTools.typeCommands')}</label>
|
||||||
keys supported):</label>
|
|
||||||
<Input
|
<Input
|
||||||
id="ssh-tools-input"
|
id="ssh-tools-input"
|
||||||
placeholder={t('placeholders.typeHere')}
|
placeholder={t('placeholders.typeHere')}
|
||||||
@@ -408,8 +406,7 @@ export function TopNavbar({isTopbarOpen, setIsTopbarOpen}: TopNavbarProps): Reac
|
|||||||
readOnly
|
readOnly
|
||||||
/>
|
/>
|
||||||
<p className="text-xs text-muted-foreground">
|
<p className="text-xs text-muted-foreground">
|
||||||
Commands will be sent to {selectedTabIds.length} selected
|
{t('sshTools.commandsWillBeSent', { count: selectedTabIds.length })}
|
||||||
terminal(s).
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
@@ -420,7 +417,7 @@ export function TopNavbar({isTopbarOpen, setIsTopbarOpen}: TopNavbarProps): Reac
|
|||||||
<Separator className="my-4"/>
|
<Separator className="my-4"/>
|
||||||
|
|
||||||
<h1 className="font-semibold">
|
<h1 className="font-semibold">
|
||||||
Settings
|
{t('sshTools.settings')}
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
@@ -433,14 +430,14 @@ export function TopNavbar({isTopbarOpen, setIsTopbarOpen}: TopNavbarProps): Reac
|
|||||||
htmlFor="enable-copy-paste"
|
htmlFor="enable-copy-paste"
|
||||||
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70 text-white"
|
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70 text-white"
|
||||||
>
|
>
|
||||||
Enable right‑click copy/paste
|
{t('sshTools.enableRightClickCopyPaste')}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Separator className="my-4"/>
|
<Separator className="my-4"/>
|
||||||
|
|
||||||
<p className="pt-2 pb-2 text-sm text-gray-500">
|
<p className="pt-2 pb-2 text-sm text-gray-500">
|
||||||
Have ideas for what should come next for ssh tools? Share them on{" "}
|
{t('sshTools.shareIdeas')}{" "}
|
||||||
<a
|
<a
|
||||||
href="https://github.com/LukeGus/Termix/issues/new"
|
href="https://github.com/LukeGus/Termix/issues/new"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
|
|||||||
Reference in New Issue
Block a user