import { useEffect, useState } from 'react'; import * as api from '../api'; import { FolderGlyph } from './icons'; const join = (a: string, b: string) => (a ? `${a}/${b}` : b); const ROOT = '.'; const isRoot = (p: string) => p === '' || p === ROOT; // Folder names are typed freely; just strip separators and traversal. const cleanName = (s: string) => s.replace(/[/\\]/g, '').replace(/\.\./g, '').trim(); // Inline "+ new folder" row shown at the end of every level, so a fresh // subfolder anywhere in the tree is one click + type away. function NewFolderRow({ base, depth, onPick }: { base: string; depth: number; onPick: (p: string) => void; }) { const [adding, setAdding] = useState(false); const [name, setName] = useState(''); const commit = () => { const n = cleanName(name); if (n) onPick(join(base, n)); setAdding(false); setName(''); }; if (!adding) { return ( ); } return (
setName(e.target.value)} onBlur={commit} onKeyDown={(e) => { if (e.key === 'Enter') commit(); if (e.key === 'Escape') { setAdding(false); setName(''); } }} />
); } // One level of the workspace folder tree: click a name to pick it as the // location, click the caret to expand its subfolders. function Level({ path, depth, value, onPick }: { path: string; depth: number; value: string; onPick: (p: string) => void; }) { const [folders, setFolders] = useState(null); const [open, setOpen] = useState>(new Set()); useEffect(() => { let alive = true; api.listFolders(path).then((r) => { if (alive) setFolders(r.folders); }).catch(() => { if (alive) setFolders([]); }); return () => { alive = false; }; }, [path]); if (!folders) return
; return ( <> {folders.map((f) => { const p = join(path, f); const expanded = open.has(f); return (
{expanded && }
); })} ); } /** * Workspace location picker. `value` is a workspace-relative path; '.' is the * workspace root. Picking a not-yet-existing folder is fine: the server * creates it when the agent is created. */ export default function FolderPicker({ value, onChange }: { value: string; onChange: (p: string) => void; }) { const [open, setOpen] = useState(false); const root = isRoot(value); return (
{open && (
{ onChange(p); }} />
)}
); }