/** * @file FilesPage.tsx * @description `/files` — the file manager (`FileBrowser`) as a page of its * own, reached from the sidebar's **Files** entry. The location is kept in the * URL (`?home=&path=` or `?lane=&path=`), so Back and a shared * link land in the same folder. With neither it opens the workspace last * shown (`agenthub.workspace.selected`) — reached from a workspace, Files is * about THAT workspace — else the user's own HOME (their Linux home under S3), * else the first workspace. * @author Nguyễn Ngọc Trí Vĩ */ import { useEffect, useState } from "react"; import { useSearchParams } from "react-router-dom"; import { useTranslation } from "react-i18next"; import { api, isHomePlace, type FsPlace } from "../lib/api"; import { FileBrowser } from "../components/lanes/FileExplorer"; function lastWorkspace(): number | null { try { const v = Number(localStorage.getItem("agenthub.workspace.selected")); return Number.isInteger(v) && v > 0 ? v : null; } catch { return null; } } type Start = { lane: number } | { home: string }; export function FilesPage() { const { t } = useTranslation(["lanes"]); const [params, setParams] = useSearchParams(); const askedLane = Number(params.get("lane")); const askedHome = params.get("home"); // The URL wins on every render: a place picked in the browser (another // user's home) lands in the URL, and a frozen start would send the next // folder opened there back to the first place. const asked: Start | null = askedHome ? { home: askedHome } : Number.isInteger(askedLane) && askedLane > 0 ? { lane: askedLane } : null; const [fallback, setStart] = useState(null); const start = asked ?? fallback; const [none, setNone] = useState(false); useEffect(() => { if (start) return; void (async () => { // The workspace last shown first: Files reached from a workspace opens // THAT workspace (from PhuNT → AC_SHOP it opened your own home, empty // but for hidden files); the home is the fallback. const { lanes } = await api.lanes.list().catch(() => ({ lanes: [] })); const last = lastWorkspace(); const shown = lanes.find((l) => l.id === last); if (shown) return setStart({ lane: shown.id }); const homes = await api.files.homes().catch(() => ({ homes: [] })); if (homes.homes.some((h) => h.id === "me")) return setStart({ home: "me" }); if (lanes[0]) setStart({ lane: lanes[0].id }); else setNone(true); })(); }, [start]); if (none) return

{t("fx.noWorkspace")}

; if (!start) return null; return ( // Cancels Layout's page padding, like the Workspace session view.
{ const next = new URLSearchParams( isHomePlace(place) ? { home: place.home } : { lane: String(typeof place === "number" ? place : place.lane) } ); if (path) next.set("path", path); setParams(next, { replace: false }); }} />
); }