Merge remote-tracking branch 'origin/dev-1.10.1' into dev-1.10.1

This commit is contained in:
LukeGus
2026-01-12 23:58:52 -06:00
3 changed files with 49 additions and 16 deletions
+2 -2
View File
@@ -211,7 +211,7 @@ class UserDataImport {
newHostData, newHostData,
targetUserId, targetUserId,
options.userDataKey, options.userDataKey,
); ) as Record<string, unknown>;
} }
delete processedHostData.id; delete processedHostData.id;
@@ -294,7 +294,7 @@ class UserDataImport {
newCredentialData, newCredentialData,
targetUserId, targetUserId,
options.userDataKey, options.userDataKey,
); ) as Record<string, unknown>;
} }
delete processedCredentialData.id; delete processedCredentialData.id;
+29 -14
View File
@@ -13,6 +13,7 @@ import { AdminSettings } from "@/ui/desktop/apps/admin/AdminSettings.tsx";
import { UserProfile } from "@/ui/desktop/user/UserProfile.tsx"; import { UserProfile } from "@/ui/desktop/user/UserProfile.tsx";
import { NetworkGraphView } from "@/ui/desktop/dashboard/network-graph"; import { NetworkGraphView } from "@/ui/desktop/dashboard/network-graph";
import { Toaster } from "@/components/ui/sonner.tsx"; import { Toaster } from "@/components/ui/sonner.tsx";
import { toast } from "sonner";
import { CommandPalette } from "@/ui/desktop/apps/command-palette/CommandPalette.tsx"; import { CommandPalette } from "@/ui/desktop/apps/command-palette/CommandPalette.tsx";
import { getUserInfo, logoutUser, isElectron } from "@/ui/main-axios.ts"; import { getUserInfo, logoutUser, isElectron } from "@/ui/main-axios.ts";
import { useTheme } from "@/components/theme-provider"; import { useTheme } from "@/components/theme-provider";
@@ -89,30 +90,44 @@ function AppContent() {
useEffect(() => { useEffect(() => {
const path = window.location.pathname; const path = window.location.pathname;
const match = path.match(/^\/hosts\/([a-zA-Z0-9_-]+)\/terminal$/); // New format: /terminal/{hostNameOrId}
if (match) { const terminalMatch = path.match(/^\/terminal\/([a-zA-Z0-9_-]+)$/);
const hostId = match[1]; // Legacy format: /hosts/{id}/terminal (backward compatible)
const legacyMatch = path.match(/^\/hosts\/([a-zA-Z0-9_-]+)\/terminal$/);
const openTerminalForHost = async () => { const hostIdentifier = terminalMatch?.[1] || legacyMatch?.[1];
if (hostIdentifier) {
const openTerminal = async () => {
try { try {
const { getSSHHostById } = await import("@/ui/main-axios.ts"); const { getSSHHostById, getSSHHosts } = await import("@/ui/main-axios.ts");
const host = await getSSHHostById(parseInt(hostId, 10)); let host = null;
// Pure numeric → lookup by ID
if (/^\d+$/.test(hostIdentifier)) {
host = await getSSHHostById(parseInt(hostIdentifier, 10));
} else {
// Non-numeric → lookup by name (first match)
const hosts = await getSSHHosts();
host = hosts.find((h: { name?: string }) => h.name === hostIdentifier) || null;
}
if (host) { if (host) {
addTab({ addTab({
type: "terminal", type: "terminal",
title: host.name || host.ip, title: host.name || host.ip,
data: { data: { host, initialCommand: "" },
host,
initialCommand: "",
},
}); });
// Clean URL to prevent re-opening on refresh
window.history.replaceState({}, "", "/");
} else {
toast.error(`Host "${hostIdentifier}" not found`);
} }
} catch (error) { } catch (error) {
console.error("Failed to open terminal for host:", error); console.error("Failed to open terminal:", error);
toast.error("Failed to open terminal for host");
} }
}; };
openTerminal();
openTerminalForHost();
} }
}, [addTab]); }, [addTab]);
@@ -1349,6 +1349,24 @@ export const Terminal = forwardRef<TerminalHandle, SSHTerminalProps>(
return true; return true;
} }
// Ctrl+Alt+<key> → Ctrl+<key> remapping for browser-blocked shortcuts
// Browsers intercept Ctrl+W/T/N/Q, so we use Ctrl+Alt as an alternative
if (e.ctrlKey && e.altKey && !e.metaKey && !e.shiftKey) {
const key = e.key.toLowerCase();
const blockedKeys = ["w", "t", "n", "q"];
if (blockedKeys.includes(key)) {
e.preventDefault();
e.stopPropagation();
const ctrlCode = key.charCodeAt(0) - 96;
if (webSocketRef.current?.readyState === 1) {
webSocketRef.current.send(
JSON.stringify({ type: "input", data: String.fromCharCode(ctrlCode) }),
);
}
return false;
}
}
if (showAutocompleteRef.current) { if (showAutocompleteRef.current) {
if (e.key === "Escape") { if (e.key === "Escape") {
e.preventDefault(); e.preventDefault();