[Init] Initial commit - NetMesh terminal manager
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
This commit is contained in:
164
components/sftp/hooks/useSftpPanePath.ts
Normal file
164
components/sftp/hooks/useSftpPanePath.ts
Normal file
@@ -0,0 +1,164 @@
|
||||
import React, { useCallback, useMemo, useRef, useState } from "react";
|
||||
import type { SftpFileEntry } from "../../../types";
|
||||
import type { SftpPane } from "../../../application/state/sftp/types";
|
||||
import {
|
||||
isWindowsPath,
|
||||
normalizeSftpNavigationPath,
|
||||
} from "../../../application/state/sftp/utils";
|
||||
import { filterHiddenFiles, isNavigableDirectory } from "../utils";
|
||||
|
||||
interface UseSftpPanePathParams {
|
||||
connection: SftpPane["connection"] | null;
|
||||
files: SftpFileEntry[];
|
||||
showHiddenFiles: boolean;
|
||||
onNavigateTo: (path: string) => void;
|
||||
}
|
||||
|
||||
interface UseSftpPanePathResult {
|
||||
isEditingPath: boolean;
|
||||
editingPathValue: string;
|
||||
showPathSuggestions: boolean;
|
||||
pathSuggestionIndex: number;
|
||||
pathInputRef: React.RefObject<HTMLInputElement>;
|
||||
pathDropdownRef: React.RefObject<HTMLDivElement>;
|
||||
pathSuggestions: { path: string; type: "folder" | "history" }[];
|
||||
setEditingPathValue: (value: string) => void;
|
||||
setShowPathSuggestions: (value: boolean) => void;
|
||||
setPathSuggestionIndex: (value: number) => void;
|
||||
handlePathBlur: () => void;
|
||||
handlePathKeyDown: (e: React.KeyboardEvent) => void;
|
||||
handlePathDoubleClick: () => void;
|
||||
handlePathSubmit: (pathOverride?: string) => void;
|
||||
}
|
||||
|
||||
export const useSftpPanePath = ({
|
||||
connection,
|
||||
files,
|
||||
showHiddenFiles,
|
||||
onNavigateTo,
|
||||
}: UseSftpPanePathParams): UseSftpPanePathResult => {
|
||||
const [isEditingPath, setIsEditingPath] = useState(false);
|
||||
const [editingPathValue, setEditingPathValue] = useState("");
|
||||
const [showPathSuggestions, setShowPathSuggestions] = useState(false);
|
||||
const [pathSuggestionIndex, setPathSuggestionIndex] = useState(-1);
|
||||
const pathInputRef = useRef<HTMLInputElement>(null);
|
||||
const pathDropdownRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const pathSuggestions = useMemo(() => {
|
||||
if (!isEditingPath || !connection) return [];
|
||||
const currentValue = editingPathValue.trim().toLowerCase();
|
||||
const suggestions: { path: string; type: "folder" | "history" }[] = [];
|
||||
|
||||
const folders = filterHiddenFiles(files, showHiddenFiles).filter(
|
||||
(f) => isNavigableDirectory(f) && f.name !== "..",
|
||||
);
|
||||
folders.forEach((f) => {
|
||||
const fullPath =
|
||||
connection.currentPath === "/"
|
||||
? `/${f.name}`
|
||||
: `${connection.currentPath}/${f.name}`;
|
||||
if (
|
||||
!currentValue ||
|
||||
fullPath.toLowerCase().includes(currentValue) ||
|
||||
f.name.toLowerCase().includes(currentValue)
|
||||
) {
|
||||
suggestions.push({ path: fullPath, type: "folder" });
|
||||
}
|
||||
});
|
||||
|
||||
const quickPaths = ["/home", "/var", "/etc", "/tmp", "/usr", "/opt", "/root"];
|
||||
quickPaths.forEach((qp) => {
|
||||
if (!currentValue || qp.toLowerCase().includes(currentValue)) {
|
||||
if (!suggestions.some((s) => s.path === qp)) {
|
||||
suggestions.push({ path: qp, type: "history" });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return suggestions.slice(0, 8);
|
||||
}, [connection, editingPathValue, files, isEditingPath, showHiddenFiles]);
|
||||
|
||||
const handlePathDoubleClick = () => {
|
||||
if (!connection) return;
|
||||
setEditingPathValue(connection.currentPath);
|
||||
setIsEditingPath(true);
|
||||
setShowPathSuggestions(true);
|
||||
setPathSuggestionIndex(-1);
|
||||
setTimeout(() => pathInputRef.current?.select(), 0);
|
||||
};
|
||||
|
||||
const handlePathSubmit = useCallback((pathOverride?: string) => {
|
||||
// Only treat //host/share as UNC when this pane is already on a Windows-style path.
|
||||
const acceptForwardSlashUnc = !!(
|
||||
connection && isWindowsPath(connection.currentPath)
|
||||
);
|
||||
const newPath = normalizeSftpNavigationPath(
|
||||
pathOverride ?? editingPathValue,
|
||||
{ acceptForwardSlashUnc },
|
||||
);
|
||||
setIsEditingPath(false);
|
||||
setShowPathSuggestions(false);
|
||||
setPathSuggestionIndex(-1);
|
||||
if (connection && newPath !== connection.currentPath) {
|
||||
onNavigateTo(newPath);
|
||||
}
|
||||
}, [connection, editingPathValue, onNavigateTo]);
|
||||
|
||||
const handlePathKeyDown = (e: React.KeyboardEvent) => {
|
||||
if (showPathSuggestions && pathSuggestions.length > 0) {
|
||||
if (e.key === "ArrowDown") {
|
||||
e.preventDefault();
|
||||
setPathSuggestionIndex((prev) =>
|
||||
prev < pathSuggestions.length - 1 ? prev + 1 : 0,
|
||||
);
|
||||
return;
|
||||
} else if (e.key === "ArrowUp") {
|
||||
e.preventDefault();
|
||||
setPathSuggestionIndex((prev) =>
|
||||
prev > 0 ? prev - 1 : pathSuggestions.length - 1,
|
||||
);
|
||||
return;
|
||||
} else if (e.key === "Tab" && pathSuggestionIndex >= 0) {
|
||||
e.preventDefault();
|
||||
setEditingPathValue(pathSuggestions[pathSuggestionIndex].path);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (e.key === "Enter") {
|
||||
if (pathSuggestionIndex >= 0 && pathSuggestions[pathSuggestionIndex]) {
|
||||
handlePathSubmit(pathSuggestions[pathSuggestionIndex].path);
|
||||
} else {
|
||||
handlePathSubmit();
|
||||
}
|
||||
} else if (e.key === "Escape") {
|
||||
setIsEditingPath(false);
|
||||
setShowPathSuggestions(false);
|
||||
setPathSuggestionIndex(-1);
|
||||
}
|
||||
};
|
||||
|
||||
const handlePathBlur = useCallback(() => {
|
||||
setTimeout(() => {
|
||||
if (!pathDropdownRef.current?.contains(document.activeElement)) {
|
||||
handlePathSubmit();
|
||||
}
|
||||
}, 150);
|
||||
}, [handlePathSubmit]);
|
||||
|
||||
return {
|
||||
isEditingPath,
|
||||
editingPathValue,
|
||||
showPathSuggestions,
|
||||
pathSuggestionIndex,
|
||||
pathInputRef,
|
||||
pathDropdownRef,
|
||||
pathSuggestions,
|
||||
setEditingPathValue,
|
||||
setShowPathSuggestions,
|
||||
setPathSuggestionIndex,
|
||||
handlePathBlur,
|
||||
handlePathKeyDown,
|
||||
handlePathDoubleClick,
|
||||
handlePathSubmit,
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user