import { ref, onBeforeUnmount, type Ref } from "vue"; import { invoke } from "@tauri-apps/api/core"; import { listen, type UnlistenFn } from "@tauri-apps/api/event"; export interface FileEntry { name: string; path: string; size: number; isDir: boolean; permissions: string; modTime: string; } export interface UseSftpReturn { currentPath: Ref; entries: Ref; isLoading: Ref; followTerminal: Ref; navigateTo: (path: string) => Promise; goUp: () => Promise; refresh: () => Promise; } /** * Composable that manages SFTP file browsing state. * Calls the Rust SFTP commands via Tauri invoke. */ export function useSftp(sessionId: string): UseSftpReturn { const currentPath = ref("/"); const entries = ref([]); const isLoading = ref(false); const followTerminal = ref(true); // Holds the unlisten function returned by listen() — called on cleanup. let unlistenCwd: UnlistenFn | null = null; async function listDirectory(path: string): Promise { try { const result = await invoke("sftp_list", { sessionId, path }); return result ?? []; } catch (err) { console.error("SFTP list error:", err); return []; } } async function navigateTo(path: string): Promise { isLoading.value = true; try { currentPath.value = path; entries.value = await listDirectory(path); } finally { isLoading.value = false; } } async function goUp(): Promise { const parts = currentPath.value.split("/").filter(Boolean); if (parts.length <= 1) { await navigateTo("/"); return; } parts.pop(); await navigateTo("/" + parts.join("/")); } async function refresh(): Promise { await navigateTo(currentPath.value); } // Listen for CWD changes from the Rust backend (OSC 7 tracking). // listen() returns Promise — store it for cleanup. listen(`ssh:cwd:${sessionId}`, (event) => { if (!followTerminal.value) return; const newPath = event.payload; if (newPath && newPath !== currentPath.value) { navigateTo(newPath); } }).then((unlisten) => { unlistenCwd = unlisten; }); onBeforeUnmount(() => { if (unlistenCwd) unlistenCwd(); }); // Load home directory on init navigateTo("/home"); return { currentPath, entries, isLoading, followTerminal, navigateTo, goUp, refresh, }; }