feat: terminal theme improvemnts, file manager improvements, host manager improvements (ready for initial user testing?)

This commit is contained in:
LukeGus
2026-05-20 14:55:41 -05:00
parent 9f0aaa16a5
commit bc713aca7d
14 changed files with 406 additions and 451 deletions
+8 -11
View File
@@ -557,10 +557,6 @@ function FileManagerContent({ initialHost, onClose }: FileManagerProps) {
return false;
}
if (isLoading && currentLoadingPathRef.current !== path) {
return false;
}
let resolvedPath = path;
if (path.includes("$") || path.startsWith("~")) {
resolvedPath = await resolveSSHPath(sshSessionId, path);
@@ -711,6 +707,7 @@ function FileManagerContent({ initialHost, onClose }: FileManagerProps) {
const navigateTo = useCallback(
(path: string) => {
if (sshSessionId) setIsLoading(true);
setCurrentPath(path);
setNavHistory((prev) => {
const next = [...prev.slice(0, navIndex + 1), path];
@@ -718,24 +715,26 @@ function FileManagerContent({ initialHost, onClose }: FileManagerProps) {
return next;
});
},
[navIndex],
[navIndex, sshSessionId],
);
const goBack = useCallback(() => {
if (navIndex > 0) {
if (sshSessionId) setIsLoading(true);
const newIndex = navIndex - 1;
setNavIndex(newIndex);
setCurrentPath(navHistory[newIndex]);
}
}, [navIndex, navHistory]);
}, [navIndex, navHistory, sshSessionId]);
const goForward = useCallback(() => {
if (navIndex < navHistory.length - 1) {
if (sshSessionId) setIsLoading(true);
const newIndex = navIndex + 1;
setNavIndex(newIndex);
setCurrentPath(navHistory[newIndex]);
}
}, [navIndex, navHistory]);
}, [navIndex, navHistory, sshSessionId]);
const goUp = useCallback(() => {
if (currentPath === "/") return;
@@ -1291,6 +1290,7 @@ function FileManagerContent({ initialHost, onClose }: FileManagerProps) {
async function handleFileOpen(file: FileItem) {
if (file.type === "directory") {
if (sshSessionId) setIsLoading(true);
setCurrentPath(file.path);
} else if (file.type === "link") {
await handleSymlinkClick(file);
@@ -2599,11 +2599,10 @@ function FileManagerContent({ initialHost, onClose }: FileManagerProps) {
variant="ghost"
size="icon"
onClick={handleRefreshDirectory}
disabled={isLoading}
className="size-8 rounded-none"
>
<RefreshCw
className={`size-4 ${isLoading ? "animate-spin" : ""}`}
className={`size-4 ${isLoading && !!sshSessionId ? "animate-spin [animation-duration:0.5s]" : ""}`}
/>
</Button>
</div>
@@ -2878,8 +2877,6 @@ function FileManagerContent({ initialHost, onClose }: FileManagerProps) {
onFileOpen={handleFileOpen}
onSelectionChange={setSelection}
currentPath={currentPath}
isLoading={isLoading}
isConnected={!!sshSessionId}
onPathChange={navigateTo}
onRefresh={handleRefreshDirectory}
onUpload={handleFilesDropped}
@@ -21,7 +21,6 @@ import {
} from "lucide-react";
import { useTranslation } from "react-i18next";
import type { FileItem } from "@/types/index";
import { SimpleLoader } from "@/lib/SimpleLoader.tsx";
interface CreateIntent {
id: string;
@@ -66,8 +65,6 @@ interface FileManagerGridProps {
onFileOpen: (file: FileItem) => void;
onSelectionChange: (files: FileItem[]) => void;
currentPath: string;
isLoading?: boolean;
isConnected?: boolean;
onPathChange: (path: string) => void;
onRefresh: () => void;
onUpload?: (files: FileList) => void;
@@ -191,8 +188,6 @@ export function FileManagerGrid({
onFileOpen,
onSelectionChange,
currentPath,
isLoading,
isConnected,
onPathChange,
onRefresh,
onUpload,
@@ -1240,11 +1235,6 @@ export function FileManagerGrid({
</div>,
document.body,
)}
<SimpleLoader
visible={!!isLoading && !!isConnected}
message={t("common.loading")}
/>
</div>
);
}
+1 -1
View File
@@ -83,7 +83,7 @@ const GuacamoleAppInner: React.FC<GuacamoleAppInnerProps> = ({
.then((status) => {
if (status.guacd.status !== "connected") {
setError(
"Remote desktop service (guacd) is not available. Please ensure guacd is running and accessible.",
"Remote desktop service (guacd) is not available. Please ensure guacd is running and accessible and configured properly in admin settings.",
);
return;
}
+44 -49
View File
@@ -53,6 +53,46 @@ import { ConnectionLog } from "@/ssh/connection-log/ConnectionLog.tsx";
import { toast } from "sonner";
import { Button } from "@/components/button";
// Background/foreground per UI theme for "Termix Default" — must match index.css
const TERMIX_DEFAULT_COLORS: Record<
string,
{ background: string; foreground: string }
> = {
dark: { background: "#0c0d0b", foreground: "#fafafa" },
light: { background: "#ffffff", foreground: "#111210" },
dracula: { background: "#282a36", foreground: "#f8f8f2" },
catppuccin: { background: "#1e1e2e", foreground: "#cdd6f4" },
nord: { background: "#2e3440", foreground: "#eceff4" },
solarized: { background: "#002b36", foreground: "#839496" },
"tokyo-night": { background: "#1a1b26", foreground: "#a9b1d6" },
"one-dark": { background: "#282c34", foreground: "#abb2bf" },
gruvbox: { background: "#282828", foreground: "#ebdbb2" },
};
function resolveTermixThemeColors(activeTheme: string, appTheme: string) {
if (activeTheme !== "termix") {
return (
TERMINAL_THEMES[activeTheme]?.colors || TERMINAL_THEMES.termixDark.colors
);
}
let resolvedUiTheme = appTheme;
if (appTheme === "system") {
resolvedUiTheme = window.matchMedia("(prefers-color-scheme: dark)").matches
? "dark"
: "light";
}
const uiColors =
TERMIX_DEFAULT_COLORS[resolvedUiTheme] ?? TERMIX_DEFAULT_COLORS.dark;
const base = TERMINAL_THEMES.termixDark.colors;
return {
...base,
background: uiColors.background,
foreground: uiColors.foreground,
cursor: uiColors.foreground,
cursorAccent: uiColors.background,
};
}
interface HostConfig {
id?: number;
instanceId?: string;
@@ -130,27 +170,8 @@ const TerminalInner = forwardRef<TerminalHandle, SSHTerminalProps>(
DEFAULT_TERMINAL_CONFIG.theme,
};
const isDarkMode =
appTheme === "dark" ||
appTheme === "dracula" ||
appTheme === "gentlemansChoice" ||
appTheme === "midnightEspresso" ||
appTheme === "catppuccinMocha" ||
(appTheme === "system" &&
window.matchMedia("(prefers-color-scheme: dark)").matches);
let themeColors;
const activeTheme = previewTheme || config.theme;
if (activeTheme === "termix") {
themeColors = isDarkMode
? TERMINAL_THEMES.termixDark.colors
: TERMINAL_THEMES.termixLight.colors;
} else {
themeColors =
TERMINAL_THEMES[activeTheme]?.colors ||
TERMINAL_THEMES.termixDark.colors;
}
const themeColors = resolveTermixThemeColors(activeTheme, appTheme);
const backgroundColor = themeColors.background;
const fitAddonRef = useRef<FitAddon | null>(null);
const webSocketRef = useRef<WebSocket | null>(null);
@@ -1828,18 +1849,8 @@ const TerminalInner = forwardRef<TerminalHandle, SSHTerminalProps>(
...hostConfig.terminalConfig,
};
let themeColors;
const activeTheme = previewTheme || config.theme;
if (activeTheme === "termix") {
themeColors = isDarkMode
? TERMINAL_THEMES.termixDark.colors
: TERMINAL_THEMES.termixLight.colors;
} else {
themeColors =
TERMINAL_THEMES[activeTheme]?.colors ||
TERMINAL_THEMES.termixDark.colors;
}
const themeColors = resolveTermixThemeColors(activeTheme, appTheme);
const fontConfig = TERMINAL_FONTS.find(
(f) => f.value === config.fontFamily,
@@ -1896,13 +1907,7 @@ const TerminalInner = forwardRef<TerminalHandle, SSHTerminalProps>(
// Refresh terminal to apply new theme colors to existing buffer content
hardRefresh();
}, [
terminal,
hostConfig.terminalConfig,
previewTheme,
isDarkMode,
isFitted,
]);
}, [terminal, hostConfig.terminalConfig, previewTheme, appTheme, isFitted]);
useEffect(() => {
if (!terminal || !xtermRef.current) return;
@@ -1917,18 +1922,8 @@ const TerminalInner = forwardRef<TerminalHandle, SSHTerminalProps>(
);
const fontFamily = fontConfig?.fallback || TERMINAL_FONTS[0].fallback;
let themeColors;
const activeTheme = previewTheme || config.theme;
if (activeTheme === "termix") {
themeColors = isDarkMode
? TERMINAL_THEMES.termixDark.colors
: TERMINAL_THEMES.termixLight.colors;
} else {
themeColors =
TERMINAL_THEMES[activeTheme]?.colors ||
TERMINAL_THEMES.termixDark.colors;
}
const themeColors = resolveTermixThemeColors(activeTheme, appTheme);
// Set initial options before opening the terminal
terminal.options = {