diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index 6daaadd9..a0ce9a75 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -1600,6 +1600,15 @@ "commandAutocompleteDesc": "Enable Tab key autocomplete suggestions for terminal commands based on your command history", "defaultSnippetFoldersCollapsed": "Collapse Snippet Folders by Default", "defaultSnippetFoldersCollapsedDesc": "When enabled, all snippet folders will be collapsed when you open the snippets tab", + "showHostTags": "Show Host Tags", + "showHostTagsDesc": "Display tags under each host in the sidebar. Disable to hide all tags.", + "account": "Account", + "appearance": "Appearance", + "languageLocalization": "Language & Localization", + "fileManagerSettings": "File Manager", + "terminalSettings": "Terminal", + "hostSidebarSettings": "Host & Sidebar", + "snippetsSettings": "Snippets", "currentPassword": "Current Password", "passwordChangedSuccess": "Password changed successfully! Please log in again.", "failedToChangePassword": "Failed to change password. Please check your current password and try again." @@ -1642,7 +1651,7 @@ "searchHosts": "Search hosts by name, username, IP, folder, tags...", "enterPassword": "Enter your password", "totpCode": "6-digit TOTP code", - "searchHostsAny": "Search hosts by any info...", + "searchHostsAny": "Search hosts (try: tag:prod, user:root, ip:192.168)...", "confirmPassword": "Enter your password to confirm", "typeHere": "Type here", "fileName": "Enter file name (e.g., example.txt)", diff --git a/src/ui/desktop/apps/dashboard/Dashboard.tsx b/src/ui/desktop/apps/dashboard/Dashboard.tsx index 61a72bfa..9f019d9b 100644 --- a/src/ui/desktop/apps/dashboard/Dashboard.tsx +++ b/src/ui/desktop/apps/dashboard/Dashboard.tsx @@ -616,23 +616,35 @@ export function Dashboard({ {t("dashboard.noRecentActivity")}

) : ( - recentActivity.map((item) => ( - - )) + recentActivity + .filter((item, index, array) => { + // Always show the first item + if (index === 0) return true; + + // Show if different from previous item (by hostId and type) + const prevItem = array[index - 1]; + return !( + item.hostId === prevItem.hostId && + item.type === prevItem.type + ); + }) + .map((item) => ( + + )) )} diff --git a/src/ui/desktop/apps/file-manager/FileManager.tsx b/src/ui/desktop/apps/file-manager/FileManager.tsx index d45dde8e..4c698d2a 100644 --- a/src/ui/desktop/apps/file-manager/FileManager.tsx +++ b/src/ui/desktop/apps/file-manager/FileManager.tsx @@ -336,6 +336,7 @@ function FileManagerContent({ initialHost, onClose }: FileManagerProps) { credentialId: currentHost.credentialId, userId: currentHost.userId, forceKeyboardInteractive: currentHost.forceKeyboardInteractive, + jumpHosts: currentHost.jumpHosts, useSocks5: currentHost.useSocks5, socks5Host: currentHost.socks5Host, socks5Port: currentHost.socks5Port, @@ -774,6 +775,7 @@ function FileManagerContent({ initialHost, onClose }: FileManagerProps) { sshKey: currentHost.key, keyPassword: currentHost.keyPassword, credentialId: currentHost.credentialId, + jumpHosts: currentHost.jumpHosts, useSocks5: currentHost.useSocks5, socks5Host: currentHost.socks5Host, socks5Port: currentHost.socks5Port, @@ -1325,6 +1327,7 @@ function FileManagerContent({ initialHost, onClose }: FileManagerProps) { authType: currentHost.authType, credentialId: currentHost.credentialId, userId: currentHost.userId, + jumpHosts: currentHost.jumpHosts, useSocks5: currentHost.useSocks5, socks5Host: currentHost.socks5Host, socks5Port: currentHost.socks5Port, @@ -1482,6 +1485,7 @@ function FileManagerContent({ initialHost, onClose }: FileManagerProps) { authType: credentials.password ? "password" : "key", credentialId: currentHost.credentialId, userId: currentHost.userId, + jumpHosts: currentHost.jumpHosts, useSocks5: currentHost.useSocks5, socks5Host: currentHost.socks5Host, socks5Port: currentHost.socks5Port, diff --git a/src/ui/desktop/apps/file-manager/components/TerminalWindow.tsx b/src/ui/desktop/apps/file-manager/components/TerminalWindow.tsx index 84e3191e..5dc47dfa 100644 --- a/src/ui/desktop/apps/file-manager/components/TerminalWindow.tsx +++ b/src/ui/desktop/apps/file-manager/components/TerminalWindow.tsx @@ -60,6 +60,15 @@ export function TerminalWindow({ const handleMaximize = () => { maximizeWindow(windowId); + // Trigger resize after maximize/restore + if (resizeTimeoutRef.current) { + clearTimeout(resizeTimeoutRef.current); + } + resizeTimeoutRef.current = setTimeout(() => { + if (terminalRef.current?.fit) { + terminalRef.current.fit(); + } + }, 150); }; const handleFocus = () => { diff --git a/src/ui/desktop/apps/host-manager/HostManagerEditor.tsx b/src/ui/desktop/apps/host-manager/HostManagerEditor.tsx index c5ccbbde..5cc3af24 100644 --- a/src/ui/desktop/apps/host-manager/HostManagerEditor.tsx +++ b/src/ui/desktop/apps/host-manager/HostManagerEditor.tsx @@ -219,6 +219,7 @@ function QuickActionItem({ placeholder={t("hosts.quickActionName")} value={quickAction.name} onChange={(e) => onUpdate(e.target.value, quickAction.snippetId)} + onBlur={(e) => onUpdate(e.target.value.trim(), quickAction.snippetId)} className="flex-1" /> @@ -1196,6 +1197,10 @@ export function HostManagerEditor({ field.ref(e); ipInputRef.current = e; }} + onBlur={(e) => { + field.onChange(e.target.value.trim()); + field.onBlur(); + }} /> @@ -1238,6 +1243,10 @@ export function HostManagerEditor({ placeholder={t("placeholders.username")} disabled={shouldDisable} {...field} + onBlur={(e) => { + field.onChange(e.target.value.trim()); + field.onBlur(); + }} /> @@ -1259,6 +1268,10 @@ export function HostManagerEditor({ { + field.onChange(e.target.value.trim()); + field.onBlur(); + }} /> @@ -1283,6 +1296,10 @@ export function HostManagerEditor({ field.onChange(e); setFolderDropdownOpen(true); }} + onBlur={(e) => { + field.onChange(e.target.value.trim()); + field.onBlur(); + }} /> {folderDropdownOpen && filteredFolders.length > 0 && ( @@ -1878,6 +1895,10 @@ export function HostManagerEditor({ { + field.onChange(e.target.value.trim()); + field.onBlur(); + }} /> @@ -1927,6 +1948,10 @@ export function HostManagerEditor({ { + field.onChange(e.target.value.trim()); + field.onBlur(); + }} /> @@ -2047,6 +2072,23 @@ export function HostManagerEditor({ newChain, ); }} + onBlur={(e) => { + const currentChain = + form.watch( + "socks5ProxyChain", + ) || []; + const newChain = [ + ...currentChain, + ]; + newChain[index] = { + ...newChain[index], + host: e.target.value.trim(), + }; + form.setValue( + "socks5ProxyChain", + newChain, + ); + }} /> @@ -2142,6 +2184,23 @@ export function HostManagerEditor({ newChain, ); }} + onBlur={(e) => { + const currentChain = + form.watch( + "socks5ProxyChain", + ) || []; + const newChain = [ + ...currentChain, + ]; + newChain[index] = { + ...newChain[index], + username: e.target.value.trim(), + }; + form.setValue( + "socks5ProxyChain", + newChain, + ); + }} /> @@ -2694,6 +2753,10 @@ export function HostManagerEditor({ { + field.onChange(e.target.value.trim()); + field.onBlur(); + }} /> @@ -2769,6 +2832,10 @@ export function HostManagerEditor({ { + field.onChange(e.target.value.trim()); + field.onBlur(); + }} /> @@ -2780,7 +2847,14 @@ export function HostManagerEditor({ render={({ field }) => ( - + { + field.onChange(e.target.value.trim()); + field.onBlur(); + }} + /> )} @@ -3057,6 +3131,10 @@ export function HostManagerEditor({ }), ); }} + onBlur={(e) => { + endpointHostField.onChange(e.target.value.trim()); + endpointHostField.onBlur(); + }} /> {sshConfigDropdownOpen[index] && @@ -3244,6 +3322,10 @@ export function HostManagerEditor({ { + field.onChange(e.target.value.trim()); + field.onBlur(); + }} /> diff --git a/src/ui/desktop/apps/host-manager/HostManagerViewer.tsx b/src/ui/desktop/apps/host-manager/HostManagerViewer.tsx index 91beed2e..70f2b136 100644 --- a/src/ui/desktop/apps/host-manager/HostManagerViewer.tsx +++ b/src/ui/desktop/apps/host-manager/HostManagerViewer.tsx @@ -48,8 +48,6 @@ import { Pencil, FolderMinus, Copy, - Activity, - Clock, Palette, Trash, Cloud, @@ -63,6 +61,8 @@ import { FolderOpen, Share2, Users, + ArrowDownUp, + Container, } from "lucide-react"; import type { SSHHost, @@ -583,46 +583,6 @@ export function HostManagerViewer({ onEditHost }: SSHManagerHostViewerProps) { } }; - const getMonitoringStatus = (host: SSHHost) => { - try { - const statsConfig = host.statsConfig - ? JSON.parse(host.statsConfig) - : DEFAULT_STATS_CONFIG; - - const formatInterval = (seconds: number): string => { - if (seconds >= 60) { - const minutes = Math.round(seconds / 60); - return `${minutes}m`; - } - return `${seconds}s`; - }; - - const statusEnabled = statsConfig.statusCheckEnabled !== false; - const metricsEnabled = statsConfig.metricsEnabled !== false; - const statusInterval = statusEnabled - ? formatInterval(statsConfig.statusCheckInterval || 30) - : null; - const metricsInterval = metricsEnabled - ? formatInterval(statsConfig.metricsInterval || 30) - : null; - - return { - statusEnabled, - metricsEnabled, - statusInterval, - metricsInterval, - bothDisabled: !statusEnabled && !metricsEnabled, - }; - } catch { - return { - statusEnabled: true, - metricsEnabled: true, - statusInterval: "30s", - metricsInterval: "30s", - bothDisabled: false, - }; - } - }; const filteredAndSortedHosts = useMemo(() => { let filtered = hosts; @@ -1419,48 +1379,15 @@ export function HostManagerViewer({ onEditHost }: SSHManagerHostViewerProps) { {t("hosts.fileManagerBadge")} )} - - {(() => { - const monitoringStatus = - getMonitoringStatus(host); - - if (monitoringStatus.bothDisabled) { - return ( - - - {t("hosts.monitoringDisabledBadge")} - - ); - } - - return ( - <> - {monitoringStatus.statusEnabled && ( - - - {t("hosts.statusMonitoring")}:{" "} - {monitoringStatus.statusInterval} - - )} - {monitoringStatus.metricsEnabled && ( - - - {t("hosts.metricsMonitoring")}:{" "} - {monitoringStatus.metricsInterval} - - )} - - ); - })()} + {host.enableDocker && ( + + + Docker + + )} @@ -1519,6 +1446,60 @@ export function HostManagerViewer({ onEditHost }: SSHManagerHostViewerProps) { )} + {host.enableTunnel && ( + + + + + +

Open Tunnels

+
+
+ )} + {host.enableDocker && ( + + + + + +

Open Docker

+
+
+ )}