v1.8.0 #429

Merged
LukeGus merged 198 commits from dev-1.8.0 into main 2025-11-05 16:36:16 +00:00
4 changed files with 59 additions and 51 deletions
Showing only changes of commit f9411bec00 - Show all commits

View File

@@ -720,9 +720,6 @@ export const Terminal = forwardRef<TerminalHandle, SSHTerminalProps>(
setVisible(true);
return () => {
console.log(
`🔴 Terminal UNMOUNTING - this should NOT happen during drag!`,
);
isUnmountingRef.current = true;
shouldNotReconnectRef.current = true;
isReconnectingRef.current = false;
@@ -745,21 +742,10 @@ export const Terminal = forwardRef<TerminalHandle, SSHTerminalProps>(
}, [xtermRef, terminal]);
useEffect(() => {
console.log(`📡 Terminal connection useEffect triggered:`, {
terminal: !!terminal,
hostConfig: !!hostConfig,
visible,
isConnected,
isConnecting,
});
if (!terminal || !hostConfig || !visible) return;
if (isConnected || isConnecting) return;
console.log(
`🔌 Initiating NEW connection - this should only happen on mount!`,
);
setIsConnecting(true);
const readyFonts =

View File

@@ -133,23 +133,10 @@ export function AppView({
const isJustReorder =
!lengthChanged && tabIdsChanged && !currentTabChanged && !splitChanged;
console.log("AppView useEffect:", {
lengthChanged,
currentTabChanged,
splitChanged,
tabIdsChanged,
isJustReorder,
willCallHideThenFit:
(lengthChanged || currentTabChanged || splitChanged) && !isJustReorder,
});
if (
(lengthChanged || currentTabChanged || splitChanged) &&
!isJustReorder
) {
console.log(
"CALLING hideThenFit - this will set ready=false and cause Terminal isVisible to become false!",
);
hideThenFit();
}

View File

@@ -16,6 +16,7 @@ interface TabProps {
tabType: string;
title?: string;
isActive?: boolean;
isSplit?: boolean;
onActivate?: () => void;
onClose?: () => void;
onSplit?: () => void;
@@ -32,6 +33,7 @@ export function Tab({
tabType,
title,
isActive,
isSplit = false,
onActivate,
onClose,
onSplit,
@@ -47,7 +49,7 @@ export function Tab({
// Firefox-style tab classes using cn utility
const tabBaseClasses = cn(
"relative flex items-center gap-1.5 px-3 min-w-fit max-w-[200px]",
"relative flex items-center gap-1.5 px-3 w-full min-w-0",
"rounded-t-lg border-t-2 border-l-2 border-r-2",
"transition-all duration-150 h-[42px]",
isDragOver &&
@@ -63,10 +65,34 @@ export function Tab({
"bg-background/80 text-muted-foreground border-border hover:bg-background/90",
);
// Helper function to split title into base and suffix
const splitTitle = (fullTitle: string): { base: string; suffix: string } => {
const match = fullTitle.match(/^(.*?)(\s*\(\d+\))$/);
if (match) {
return { base: match[1], suffix: match[2] };
}
return { base: fullTitle, suffix: "" };
};
if (tabType === "home") {
return (
<div
className={tabBaseClasses}
className={cn(
"relative flex items-center gap-1.5 px-3 flex-shrink-0 cursor-pointer",
"rounded-t-lg border-t-2 border-l-2 border-r-2",
"transition-all duration-150 h-[42px]",
isDragOver &&
"bg-background/40 text-muted-foreground border-border opacity-60",
isDragging && "opacity-70",
!isDragOver &&
!isDragging &&
isActive &&
"bg-background text-foreground border-border z-10",
!isDragOver &&
!isDragging &&
!isActive &&
"bg-background/80 text-muted-foreground border-border hover:bg-background/90",
)}
onClick={!disableActivate ? onActivate : undefined}
style={{
marginBottom: "-2px",
@@ -98,18 +124,18 @@ export function Tab({
? t("nav.userProfile")
: t("nav.terminal"));
const { base, suffix } = splitTitle(displayTitle);
return (
<div
className={tabBaseClasses}
className={cn(tabBaseClasses, "cursor-pointer")}
onClick={!disableActivate ? onActivate : undefined}
style={{
marginBottom: "-2px",
borderBottom: isActive ? "2px solid white" : "none",
}}
>
<div
className="flex items-center gap-1.5 flex-1 min-w-0"
onClick={!disableActivate ? onActivate : undefined}
>
<div className="flex items-center gap-1.5 flex-1 min-w-0">
{isServer ? (
<ServerIcon className="h-4 w-4 flex-shrink-0" />
) : isFileManager ? (
@@ -119,7 +145,8 @@ export function Tab({
) : (
<TerminalIcon className="h-4 w-4 flex-shrink-0" />
)}
<span className="truncate text-sm">{displayTitle}</span>
<span className="truncate text-sm flex-1 min-w-0">{base}</span>
{suffix && <span className="text-sm flex-shrink-0">{suffix}</span>}
</div>
{canSplit && (
@@ -136,7 +163,9 @@ export function Tab({
disableSplit ? t("nav.cannotSplitTab") : t("nav.splitScreen")
}
>
<SeparatorVertical className="h-4 w-4" />
<SeparatorVertical
className={cn("h-4 w-4", isSplit && "text-white")}
/>
</Button>
)}
@@ -159,21 +188,21 @@ export function Tab({
}
if (tabType === "ssh_manager") {
const displayTitle = title || t("nav.sshManager");
const { base, suffix } = splitTitle(displayTitle);
return (
<div
className={tabBaseClasses}
className={cn(tabBaseClasses, "cursor-pointer")}
onClick={!disableActivate ? onActivate : undefined}
style={{
marginBottom: "-2px",
borderBottom: isActive ? "2px solid white" : "none",
}}
>
<div
className="flex items-center gap-1.5 flex-1 min-w-0"
onClick={!disableActivate ? onActivate : undefined}
>
<span className="truncate text-sm">
{title || t("nav.sshManager")}
</span>
<div className="flex items-center gap-1.5 flex-1 min-w-0">
<span className="truncate text-sm flex-1 min-w-0">{base}</span>
{suffix && <span className="text-sm flex-shrink-0">{suffix}</span>}
</div>
{canClose && (
@@ -195,19 +224,21 @@ export function Tab({
}
if (tabType === "admin") {
const displayTitle = title || t("nav.admin");
const { base, suffix } = splitTitle(displayTitle);
return (
<div
className={tabBaseClasses}
className={cn(tabBaseClasses, "cursor-pointer")}
onClick={!disableActivate ? onActivate : undefined}
style={{
marginBottom: "-2px",
borderBottom: isActive ? "2px solid white" : "none",
}}
>
<div
className="flex items-center gap-1.5 flex-1 min-w-0"
onClick={!disableActivate ? onActivate : undefined}
>
<span className="truncate text-sm">{title || t("nav.admin")}</span>
<div className="flex items-center gap-1.5 flex-1 min-w-0">
<span className="truncate text-sm flex-1 min-w-0">{base}</span>
{suffix && <span className="text-sm flex-shrink-0">{suffix}</span>}
</div>
{canClose && (

View File

@@ -486,7 +486,7 @@ export function TopNavbar({
>
<div
ref={containerRef}
className="h-full p-1 pr-2 border-r-2 border-dark-border w-[calc(100%-6rem)] flex items-center overflow-x-auto overflow-y-hidden gap-1 thin-scrollbar"
className="h-full p-1 pr-2 border-r-2 border-dark-border w-[calc(100%-6rem)] flex items-center overflow-x-auto gap-1"
>
{tabs.map((tab: TabData, index: number) => {
const isActive = tab.id === currentTab;
@@ -601,12 +601,16 @@ export function TopNavbar({
cursor: isDraggingThisTab ? "grabbing" : "grab",
userSelect: "none",
WebkitUserSelect: "none",
flex: tab.type === "home" ? "0 0 auto" : "1 1 150px",
minWidth: tab.type === "home" ? "auto" : "150px",
display: "flex",
}}
>
<Tab
tabType={tab.type}
title={tab.title}
isActive={isActive}
isSplit={isSplit}
onActivate={() => handleTabActivate(tab.id)}
onClose={
isTerminal ||