import { useState } from "react"; import { Icon } from "./icon"; import type { GitMount } from "../api/config_profiles"; interface GitMountEditorProps { mounts: GitMount[]; onChange: (mounts: GitMount[]) => void; } export const GitMountEditor = ({ mounts, onChange }: GitMountEditorProps) => { const [editingIndex, setEditingIndex] = useState(null); const [newMount, setNewMount] = useState({ remote_url: "", source_path: ".", target_path: "", branch: "", }); const handleAdd = (mount: GitMount) => { onChange([...mounts, mount]); setNewMount({ remote_url: "", source_path: ".", target_path: "", branch: "" }); }; const handleUpdate = (index: number, updated: GitMount) => { const updatedMounts = [...mounts]; updatedMounts[index] = updated; onChange(updatedMounts); setEditingIndex(null); }; const handleRemove = (index: number) => { onChange(mounts.filter((_, i) => i !== index)); }; const validatePath = (path: string, isTarget: boolean): string | null => { if (!path) return isTarget ? "Target path is required" : null; if (path.includes("..")) return "Path cannot contain .."; if (!isTarget && path.startsWith("/")) return "Source path must be relative"; return null; }; const validateUrl = (url: string): string | null => { if (!url) return "Git URL is required"; if (!url.startsWith("http://") && !url.startsWith("https://") && !url.startsWith("git@") && !url.startsWith("ssh://")) { return "Must be a valid git URL (https://, git@, or ssh://)"; } return null; }; return (

Git Mounts

{mounts.length > 0 && (
{mounts.map((mount, index) => (
{editingIndex === index ? ( handleUpdate(index, updated)} onCancel={() => setEditingIndex(null)} validatePath={validatePath} validateUrl={validateUrl} /> ) : (
{mount.remote_url} {mount.source_path || "."} → {mount.target_path} {mount.branch && ( @{mount.branch} )}
)}
))}
)}
Add Git Mount
setNewMount({ remote_url: "", source_path: ".", target_path: "", branch: "" })} validatePath={validatePath} validateUrl={validateUrl} isNew />
); }; interface GitMountFormProps { mount: GitMount; onSave: (mount: GitMount) => void; onCancel: () => void; validatePath: (path: string, isTarget: boolean) => string | null; validateUrl: (url: string) => string | null; isNew?: boolean; } const GitMountForm = ({ mount, onSave, onCancel, validatePath, validateUrl, isNew }: GitMountFormProps) => { const [form, setForm] = useState({ ...mount }); const [errors, setErrors] = useState>({}); const handleChange = (field: keyof GitMount, value: string) => { setForm((prev) => ({ ...prev, [field]: value })); if (errors[field]) { setErrors((prev) => { const next = { ...prev }; delete next[field]; return next; }); } }; const handleSubmit = () => { const newErrors: Record = {}; const urlError = validateUrl(form.remote_url); if (urlError) newErrors.remote_url = urlError; const sourceError = validatePath(form.source_path || ".", false); if (sourceError) newErrors.source_path = sourceError; const targetError = validatePath(form.target_path, true); if (targetError) newErrors.target_path = targetError; if (Object.keys(newErrors).length > 0) { setErrors(newErrors); return; } onSave(form); if (isNew) { setForm({ remote_url: "", source_path: ".", target_path: "", branch: "" }); } }; return (
handleChange("remote_url", e.target.value)} placeholder="https://github.com/user/repo.git" className={errors.remote_url ? "error" : ""} /> Repository URL (HTTPS or SSH) {errors.remote_url && {errors.remote_url}}
handleChange("source_path", e.target.value)} placeholder="e.g., . or configs/*.json" className={errors.source_path ? "error" : ""} /> Relative path in repo (supports glob patterns) {errors.source_path && {errors.source_path}}
handleChange("target_path", e.target.value)} placeholder="e.g., /app/config" className={errors.target_path ? "error" : ""} /> Use absolute path (e.g. /app/config). Relative paths need working_directory set in tool config. {errors.target_path && {errors.target_path}}
handleChange("branch", e.target.value)} placeholder="e.g., main or v1.0" /> Branch or tag to checkout
); };