Files
openworker/surfaces/gui/src/components/FolderGate.tsx
T
Rohit C PrasadandDevika 2b45018ffa OpenWorker: initial import
Imported from andrewyng/aisuite@1b4bbf303e
(contents of its platform/ directory, hoisted to the repo root).
Development history prior to this commit lives in that repository.

Co-authored-by: Devika <devikaverma11@gmail.com>
2026-07-21 11:09:41 -07:00

92 lines
3.2 KiB
TypeScript

import { useEffect, useState } from "react";
import { getRecentWorkspaces, openWorkspace, type RecentWorkspace } from "../api";
import { chooseFolder } from "../tauri";
// The mandatory workspace picker for project-scoped personas. Deliberately no
// "switch persona" escape hatch: if a persona needs a folder, the choice here is
// pick one or cancel — offering Chat as an exit undermined the persona the user
// just chose (owner call, 2026-07-03).
interface Props {
onChoose: (path: string, branch?: string | null) => void;
onCancel?: () => void; // present when changing folder mid-session
create?: boolean; // "New project" mode: create the folder if missing
}
export function FolderGate({ onChoose, onCancel, create }: Props) {
const [recents, setRecents] = useState<RecentWorkspace[]>([]);
const [path, setPath] = useState("");
const [error, setError] = useState("");
useEffect(() => {
getRecentWorkspaces().then(setRecents).catch(() => {});
}, []);
const open = async (p: string, doCreate = false) => {
setError("");
const res = await openWorkspace(p.trim(), doCreate);
if (res.ok) onChoose(res.path, res.git_branch);
else setError(res.error || "could not open that folder");
};
const browse = async () => {
const picked = await chooseFolder();
if (picked) {
setPath(picked);
open(picked, create); // a picked folder already exists; create flag is harmless
}
};
return (
<div className="gate-overlay">
<div className="gate">
<div className="gate-mark"></div>
<h2>{create ? "New project" : "Choose a project folder"}</h2>
<p className="gate-sub">
{create
? "Pick a folder or enter a path. If the path doesn't exist, it will be created."
: "This coworker needs a workspace to read, edit, and run in."}
</p>
<div className="gate-input">
<input
placeholder="/path/to/your/project"
value={path}
onChange={(e) => setPath(e.target.value)}
onKeyDown={(e) => e.key === "Enter" && open(path, create)}
autoFocus
/>
<button className="btn" onClick={browse} title="Pick a folder">
Browse
</button>
<button className="btn primary" onClick={() => open(path, create)} disabled={!path.trim()}>
{create ? "Create" : "Open"}
</button>
</div>
{error && <div className="gate-error">{error}</div>}
{recents.length > 0 && (
<>
<div className="gate-label">Recent</div>
<div className="gate-recents">
{recents.map((w) => (
<div className="gate-recent" key={w.path} onClick={() => open(w.path)} title={w.path}>
<span className="folder">📁 {w.name}</span>
<span className="dim">{w.path}</span>
</div>
))}
</div>
</>
)}
{onCancel && (
<div className="gate-foot">
<button className="btn gate-cancel" onClick={onCancel}>
Cancel
</button>
</div>
)}
</div>
</div>
);
}