feat: Added none password option and fixed some navbar issues (still present)

This commit is contained in:
LukeGus
2025-10-22 00:16:45 -05:00
parent 40232af503
commit 471e2ff3fa
7 changed files with 268 additions and 91 deletions

View File

@@ -90,9 +90,9 @@ export function HostManagerEditor({
Array<{ id: number; username: string; authType: string }>
>([]);
const [authTab, setAuthTab] = useState<"password" | "key" | "credential">(
"password",
);
const [authTab, setAuthTab] = useState<
"password" | "key" | "credential" | "none"
>("password");
const [keyInputMethod, setKeyInputMethod] = useState<"upload" | "paste">(
"upload",
);
@@ -179,7 +179,7 @@ export function HostManagerEditor({
folder: z.string().optional(),
tags: z.array(z.string().min(1)).default([]),
pin: z.boolean().default(false),
authType: z.enum(["password", "key", "credential"]),
authType: z.enum(["password", "key", "credential", "none"]),
credentialId: z.number().optional().nullable(),
password: z.string().optional(),
key: z.any().optional().nullable(),
@@ -241,6 +241,11 @@ export function HostManagerEditor({
}),
})
.superRefine((data, ctx) => {
if (data.authType === "none") {
// No credentials required for "none" auth type - will use keyboard-interactive
return;
}
if (data.authType === "password") {
if (
!data.password ||
@@ -356,7 +361,9 @@ export function HostManagerEditor({
? "credential"
: cleanedHost.key
? "key"
: "password";
: cleanedHost.password
? "password"
: "none";
setAuthTab(defaultAuthType);
const formData = {
@@ -367,7 +374,7 @@ export function HostManagerEditor({
folder: cleanedHost.folder || "",
tags: cleanedHost.tags || [],
pin: Boolean(cleanedHost.pin),
authType: defaultAuthType as "password" | "key" | "credential",
authType: defaultAuthType as "password" | "key" | "credential" | "none",
credentialId: null,
password: "",
key: null,
@@ -922,7 +929,8 @@ export function HostManagerEditor({
const newAuthType = value as
| "password"
| "key"
| "credential";
| "credential"
| "none";
setAuthTab(newAuthType);
form.setValue("authType", newAuthType);
}}
@@ -936,6 +944,7 @@ export function HostManagerEditor({
<TabsTrigger value="credential">
{t("hosts.credential")}
</TabsTrigger>
<TabsTrigger value="none">{t("hosts.none")}</TabsTrigger>
</TabsList>
<TabsContent value="password">
<FormField
@@ -1154,6 +1163,19 @@ export function HostManagerEditor({
)}
/>
</TabsContent>
<TabsContent value="none">
<Alert className="mt-2">
<AlertDescription>
<strong>{t("hosts.noneAuthTitle")}</strong>
<div className="mt-2">
{t("hosts.noneAuthDescription")}
</div>
<div className="mt-2 text-sm">
{t("hosts.noneAuthDetails")}
</div>
</AlertDescription>
</Alert>
</TabsContent>
</Tabs>
</TabsContent>
<TabsContent value="terminal">

View File

@@ -613,7 +613,6 @@ export const Terminal = forwardRef<TerminalHandle, SSHTerminalProps>(
fontSize: 14,
fontFamily:
'"Caskaydia Cove Nerd Font Mono", "SF Mono", Consolas, "Liberation Mono", monospace',
theme: { background: "#18181b", foreground: "#f7f7f7" },
allowTransparency: true,
convertEol: true,
windowsMode: false,
@@ -626,6 +625,8 @@ export const Terminal = forwardRef<TerminalHandle, SSHTerminalProps>(
minimumContrastRatio: 1,
letterSpacing: 0,
lineHeight: 1.2,
theme: { background: "#18181b", foreground: "#f7f7f7" },
};
const fitAddon = new FitAddon();

View File

@@ -104,6 +104,7 @@ export function Host({ host }: HostProps): React.ReactElement {
className="min-w-[160px]"
>
<DropdownMenuItem
className="font-semibold"
onClick={() =>
addTab({ type: "server", title, hostConfig: host })
}
@@ -111,13 +112,17 @@ export function Host({ host }: HostProps): React.ReactElement {
Open Server Details
</DropdownMenuItem>
<DropdownMenuItem
className="font-semibold"
onClick={() =>
addTab({ type: "file_manager", title, hostConfig: host })
}
>
Open File Manager
</DropdownMenuItem>
<DropdownMenuItem onClick={() => alert("Settings clicked")}>
<DropdownMenuItem
className="font-semibold"
onClick={() => alert("Settings clicked")}
>
Edit
</DropdownMenuItem>
</DropdownMenuContent>

View File

@@ -58,12 +58,15 @@ export function TopNavbar({
const [isRecording, setIsRecording] = useState(false);
const [selectedTabIds, setSelectedTabIds] = useState<number[]>([]);
const [snippetsSidebarOpen, setSnippetsSidebarOpen] = useState(false);
const [justDroppedTabId, setJustDroppedTabId] = useState<number | null>(null); // New state variable
const [dragState, setDragState] = useState<{
draggedId: number | null;
draggedIndex: number | null;
currentX: number;
startX: number;
targetIndex: number | null;
}>({
draggedId: null,
draggedIndex: null,
currentX: 0,
startX: 0,
@@ -72,6 +75,8 @@ export function TopNavbar({
const containerRef = React.useRef<HTMLDivElement | null>(null);
const tabRefs = React.useRef<Map<number, HTMLDivElement>>(new Map());
const prevTabsRef = React.useRef<TabData[]>([]);
const handleTabActivate = (tabId: number) => {
setCurrentTab(tabId);
};
@@ -249,6 +254,37 @@ export function TopNavbar({
}
};
React.useEffect(() => {
if (prevTabsRef.current.length > 0 && tabs !== prevTabsRef.current) {
// Check if tabs actually changed
console.log("Tabs AFTER reorder (IDs and references):");
tabs.forEach((newTab, newIdx) => {
const oldTab = prevTabsRef.current.find((t) => t.id === newTab.id);
console.log(
` [${newIdx}] ID: ${newTab.id}, Ref:`,
newTab,
`(Old Ref:`,
oldTab,
`)`,
);
if (oldTab && oldTab !== newTab) {
console.warn(` Tab ID ${newTab.id} object reference CHANGED!`);
} else if (oldTab && oldTab === newTab) {
console.info(` Tab ID ${newTab.id} object reference PRESERVED.`);
}
});
// Clear prevTabsRef.current only after the comparison is done
prevTabsRef.current = [];
}
}, [tabs]); // Depend only on tabs
React.useEffect(() => {
if (justDroppedTabId !== null) {
const timer = setTimeout(() => setJustDroppedTabId(null), 50); // Clear after a short delay
return () => clearTimeout(timer);
}
}, [justDroppedTabId]);
const handleDragStart = (e: React.DragEvent, index: number) => {
console.log("Drag start:", index, e.clientX);
@@ -259,6 +295,7 @@ export function TopNavbar({
e.dataTransfer.setDragImage(img, 0, 0);
setDragState({
draggedId: tabs[index].id,
draggedIndex: index,
startX: e.clientX,
currentX: e.clientX,
@@ -333,11 +370,25 @@ export function TopNavbar({
// Moving right - find the rightmost tab whose midpoint we've passed
for (let i = draggedIndex + 1; i < tabBoundaries.length; i++) {
if (draggedCenter > tabBoundaries[i].mid) {
newTargetIndex = i;
newTargetIndex = i; // Reverted from i + 1 to i
} else {
break;
}
}
// Edge case: if dragged past the last tab, target should be at the very end
const lastTabIndex = tabBoundaries.length - 1;
if (lastTabIndex >= 0) {
// Ensure there's at least one tab
const lastTabEl = tabRefs.current.get(lastTabIndex);
if (lastTabEl) {
const lastTabRect = lastTabEl.getBoundingClientRect();
const containerRect = containerRef.current.getBoundingClientRect();
const lastTabEndInContainer = lastTabRect.right - containerRect.left;
if (currentX > lastTabEndInContainer) {
newTargetIndex = tabBoundaries.length; // Insert at the very end
}
}
}
}
return newTargetIndex;
@@ -378,57 +429,40 @@ export function TopNavbar({
dragState.targetIndex !== null &&
dragState.draggedIndex !== dragState.targetIndex
) {
console.log("Tabs before reorder (IDs and references):");
tabs.forEach((tab, idx) =>
console.log(` [${idx}] ID: ${tab.id}, Ref:`, tab),
);
prevTabsRef.current = tabs; // Store current tabs before reorder
reorderTabs(dragState.draggedIndex, dragState.targetIndex);
// Delay clearing drag state to prevent visual jitter
// This allows the reorder to complete and re-render before removing transforms
setTimeout(() => {
setDragState({
draggedIndex: null,
startX: 0,
currentX: 0,
targetIndex: null,
});
}, 0);
} else {
// No reorder needed, clear immediately
setDragState({
draggedIndex: null,
startX: 0,
currentX: 0,
targetIndex: null,
});
if (dragState.draggedId !== null) {
setJustDroppedTabId(dragState.draggedId);
}
}
// Immediately reset drag state after drop to ensure a single re-render
// with updated tabs and cleared drag state.
setDragState({
draggedId: null,
draggedIndex: null,
startX: 0,
currentX: 0,
targetIndex: null,
});
};
const handleDragEnd = () => {
console.log("Drag end:", dragState);
if (
dragState.draggedIndex !== null &&
dragState.targetIndex !== null &&
dragState.draggedIndex !== dragState.targetIndex
) {
reorderTabs(dragState.draggedIndex, dragState.targetIndex);
// Delay clearing drag state to prevent visual jitter
setTimeout(() => {
setDragState({
draggedIndex: null,
startX: 0,
currentX: 0,
targetIndex: null,
});
}, 0);
} else {
// No reorder needed, clear immediately
setDragState({
draggedIndex: null,
startX: 0,
currentX: 0,
targetIndex: null,
});
}
// Immediately reset drag state. If a drop occurred, handleDrop has already
// initiated the state clear. If the drag was cancelled (e.g., dropped
// outside a valid target), this clears the drag state.
setDragState({
draggedId: null,
draggedIndex: null,
startX: 0,
currentX: 0,
targetIndex: null,
});
};
const isSplitScreenActive =
@@ -492,47 +526,64 @@ export function TopNavbar({
isSplitScreenActive);
const disableClose = (isSplitScreenActive && isActive) || isSplit;
const isDragging = dragState.draggedIndex === index;
const dragOffset = isDragging
const isDraggingThisTab = dragState.draggedIndex === index;
const isTheDraggedTab = tab.id === dragState.draggedId;
const isDroppedAndSnapping = tab.id === justDroppedTabId; // New condition
const dragOffset = isDraggingThisTab
? dragState.currentX - dragState.startX
: 0;
// Diagnostic logs
if (dragState.draggedIndex !== null) {
console.log(
`Tab ID: ${tab.id}, Index: ${index}, isDraggingThisTab: ${isDraggingThisTab}, draggedOriginalIndex: ${dragState.draggedIndex}, currentTargetIndex: ${dragState.targetIndex}, isDroppedAndSnapping: ${isDroppedAndSnapping}`,
);
}
// Calculate transform
let transform = "";
if (isDragging) {
// Dragged tab follows cursor
if (isDraggingThisTab) {
transform = `translateX(${dragOffset}px)`;
} else if (
dragState.draggedIndex !== null &&
dragState.targetIndex !== null
) {
// Other tabs shift to make room
const draggedIndex = dragState.draggedIndex;
const targetIndex = dragState.targetIndex;
const draggedOriginalIndex = dragState.draggedIndex;
const currentTargetIndex = dragState.targetIndex;
// Determine if this tab should shift left or right
if (
draggedIndex < targetIndex &&
index > draggedIndex &&
index <= targetIndex
draggedOriginalIndex < currentTargetIndex && // Dragging rightwards
index > draggedOriginalIndex && // This tab is to the right of the original position
index <= currentTargetIndex // This tab is at or before the target position
) {
// Shifting left
const draggedTabEl = tabRefs.current.get(draggedIndex);
const draggedWidth =
draggedTabEl?.getBoundingClientRect().width || 0;
transform = `translateX(-${draggedWidth + 4}px)`;
// Shift left to make space
const draggedTabWidth =
tabRefs.current
.get(draggedOriginalIndex)
?.getBoundingClientRect().width || 0;
const gap = 4;
transform = `translateX(-${draggedTabWidth + gap}px)`;
} else if (
draggedIndex > targetIndex &&
index >= targetIndex &&
index < draggedIndex
draggedOriginalIndex > currentTargetIndex && // Dragging leftwards
index >= currentTargetIndex && // This tab is at or after the target position
index < draggedOriginalIndex // This tab is to the left of the original position
) {
// Shifting right
const draggedTabEl = tabRefs.current.get(draggedIndex);
const draggedWidth =
draggedTabEl?.getBoundingClientRect().width || 0;
transform = `translateX(${draggedWidth + 4}px)`;
// Shift right to make space
const draggedTabWidth =
tabRefs.current
.get(draggedOriginalIndex)
?.getBoundingClientRect().width || 0;
const gap = 4;
transform = `translateX(${draggedTabWidth + gap}px)`;
}
}
// Diagnostic log for transform
if (dragState.draggedIndex !== null) {
console.log(` Tab ID: ${tab.id}, Transform: ${transform}`);
}
return (
<div
key={tab.id}
@@ -554,10 +605,13 @@ export function TopNavbar({
onDragEnd={handleDragEnd}
style={{
transform,
transition: isDragging ? "none" : "transform 200ms ease-out",
zIndex: isDragging ? 1000 : 1,
transition:
isDraggingThisTab || isDroppedAndSnapping
? "none"
: "transform 200ms ease-out",
zIndex: isDraggingThisTab ? 1000 : 1,
position: "relative",
cursor: isDragging ? "grabbing" : "grab",
cursor: isDraggingThisTab ? "grabbing" : "grab",
userSelect: "none",
WebkitUserSelect: "none",
}}
@@ -592,7 +646,7 @@ export function TopNavbar({
disableActivate={disableActivate}
disableSplit={disableSplit}
disableClose={disableClose}
isDragging={isDragging}
isDragging={isDraggingThisTab}
isDragOver={false}
/>
</div>