Refine instructions tab: remove header/border, draggable panel, move Advanced below

- Remove "Instructions Bundle" header and border wrapper
- Move "Advanced" collapsible section below the file browser / editor grid
- Make the file browser column width draggable (180px–500px range)
- Advanced section now uses a wider 3-column grid layout

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
dotta 2026-03-18 07:09:10 -05:00
parent c7d31346e0
commit 5252568825

View file

@ -1509,6 +1509,8 @@ function PromptsTab({
} | null>(null); } | null>(null);
const [newFilePath, setNewFilePath] = useState(""); const [newFilePath, setNewFilePath] = useState("");
const [expandedDirs, setExpandedDirs] = useState<Set<string>>(new Set()); const [expandedDirs, setExpandedDirs] = useState<Set<string>>(new Set());
const [filePanelWidth, setFilePanelWidth] = useState(260);
const containerRef = useRef<HTMLDivElement>(null);
const [awaitingRefresh, setAwaitingRefresh] = useState(false); const [awaitingRefresh, setAwaitingRefresh] = useState(false);
const lastFileVersionRef = useRef<string | null>(null); const lastFileVersionRef = useRef<string | null>(null);
@ -1716,6 +1718,27 @@ function PromptsTab({
} : null); } : null);
}, [bundle, isDirty, onCancelActionChange]); }, [bundle, isDirty, onCancelActionChange]);
const handleSeparatorDrag = useCallback((event: React.MouseEvent) => {
event.preventDefault();
const startX = event.clientX;
const startWidth = filePanelWidth;
const onMouseMove = (moveEvent: MouseEvent) => {
const delta = moveEvent.clientX - startX;
const next = Math.max(180, Math.min(500, startWidth + delta));
setFilePanelWidth(next);
};
const onMouseUp = () => {
document.removeEventListener("mousemove", onMouseMove);
document.removeEventListener("mouseup", onMouseUp);
document.body.style.cursor = "";
document.body.style.userSelect = "";
};
document.addEventListener("mousemove", onMouseMove);
document.addEventListener("mouseup", onMouseUp);
document.body.style.cursor = "col-resize";
document.body.style.userSelect = "none";
}, [filePanelWidth]);
if (!isLocal) { if (!isLocal) {
return ( return (
<div className="max-w-3xl"> <div className="max-w-3xl">
@ -1732,29 +1755,18 @@ function PromptsTab({
return ( return (
<div className="max-w-6xl space-y-4"> <div className="max-w-6xl space-y-4">
<div className="border border-border rounded-lg p-4 space-y-4"> {(bundle?.warnings ?? []).length > 0 && (
<div className="flex items-start justify-between gap-4"> <div className="space-y-2">
<div>
<h3 className="text-sm font-medium">Instructions Bundle</h3>
<p className="mt-1 text-sm text-muted-foreground">
Configure your agent's behavior with instructions
</p>
</div>
<div className="text-xs text-muted-foreground">
{bundle?.files.length ?? 0} files
</div>
</div>
{(bundle?.warnings ?? []).map((warning) => ( {(bundle?.warnings ?? []).map((warning) => (
<div key={warning} className="rounded-md border border-sky-500/25 bg-sky-500/10 px-3 py-2 text-xs text-sky-100"> <div key={warning} className="rounded-md border border-sky-500/25 bg-sky-500/10 px-3 py-2 text-xs text-sky-100">
{warning} {warning}
</div> </div>
))} ))}
</div> </div>
)}
<div className="grid gap-4 lg:grid-cols-[260px_minmax(0,1fr)]"> <div ref={containerRef} className="flex gap-0">
<div className="border border-border rounded-lg p-3 space-y-3"> <div className="border border-border rounded-lg p-3 space-y-3 shrink-0" style={{ width: filePanelWidth }}>
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<h4 className="text-sm font-medium">Files</h4> <h4 className="text-sm font-medium">Files</h4>
<Button <Button
@ -1822,9 +1834,76 @@ function PromptsTab({
); );
}} }}
/> />
</div>
{/* Draggable separator */}
<div
className="w-1 shrink-0 cursor-col-resize hover:bg-border active:bg-primary/50 rounded transition-colors mx-1"
onMouseDown={handleSeparatorDrag}
/>
<div className="border border-border rounded-lg p-4 space-y-3 min-w-0 flex-1">
<div className="flex items-center justify-between gap-3">
<div>
<h4 className="text-sm font-medium font-mono">{selectedOrEntryFile}</h4>
<p className="text-xs text-muted-foreground">
{selectedFileExists
? selectedFileSummary?.deprecated
? "Deprecated virtual file"
: `${selectedFileDetail?.language ?? "text"} file`
: "New file in this bundle"}
</p>
</div>
{selectedFileExists && !selectedFileSummary?.deprecated && selectedOrEntryFile !== currentEntryFile && (
<Button
type="button"
size="sm"
variant="outline"
onClick={() => {
if (confirm(`Delete ${selectedOrEntryFile}?`)) {
deleteFile.mutate(selectedOrEntryFile, {
onSuccess: () => {
setSelectedFile(currentEntryFile);
setDraft(null);
},
});
}
}}
disabled={deleteFile.isPending}
>
Delete
</Button>
)}
</div>
{selectedFileExists && fileLoading && !selectedFileDetail ? (
<PromptEditorSkeleton />
) : isMarkdown(selectedOrEntryFile) ? (
<MarkdownEditor
key={selectedOrEntryFile}
value={displayValue}
onChange={(value) => setDraft(value ?? "")}
placeholder="# Agent instructions"
contentClassName="min-h-[420px] text-sm font-mono"
imageUploadHandler={async (file) => {
const namespace = `agents/${agent.id}/instructions/${selectedOrEntryFile.replaceAll("/", "-")}`;
const asset = await uploadMarkdownImage.mutateAsync({ file, namespace });
return asset.contentPath;
}}
/>
) : (
<textarea
value={displayValue}
onChange={(event) => setDraft(event.target.value)}
className="min-h-[420px] w-full rounded-md border border-border bg-transparent px-3 py-2 font-mono text-sm outline-none"
placeholder="File contents"
/>
)}
</div>
</div>
<Collapsible> <Collapsible>
<CollapsibleTrigger className="flex items-center gap-1 text-xs text-muted-foreground hover:text-foreground transition-colors group w-full"> <CollapsibleTrigger className="flex items-center gap-1 text-xs text-muted-foreground hover:text-foreground transition-colors group">
<ChevronRight className="h-3 w-3 transition-transform group-data-[state=open]:rotate-90" /> <ChevronRight className="h-3 w-3 transition-transform group-data-[state=open]:rotate-90" />
Advanced Advanced
</CollapsibleTrigger> </CollapsibleTrigger>
@ -1869,7 +1948,7 @@ function PromptsTab({
</Button> </Button>
</div> </div>
</label> </label>
<div className="grid gap-3 grid-cols-2"> <div className="grid gap-3 sm:grid-cols-3">
<label className="space-y-1"> <label className="space-y-1">
<span className="text-xs font-medium text-muted-foreground flex items-center gap-1"> <span className="text-xs font-medium text-muted-foreground flex items-center gap-1">
Root path Root path
@ -1944,67 +2023,6 @@ function PromptsTab({
</CollapsibleContent> </CollapsibleContent>
</Collapsible> </Collapsible>
</div> </div>
<div className="border border-border rounded-lg p-4 space-y-3">
<div className="flex items-center justify-between gap-3">
<div>
<h4 className="text-sm font-medium font-mono">{selectedOrEntryFile}</h4>
<p className="text-xs text-muted-foreground">
{selectedFileExists
? selectedFileSummary?.deprecated
? "Deprecated virtual file"
: `${selectedFileDetail?.language ?? "text"} file`
: "New file in this bundle"}
</p>
</div>
{selectedFileExists && !selectedFileSummary?.deprecated && selectedOrEntryFile !== currentEntryFile && (
<Button
type="button"
size="sm"
variant="outline"
onClick={() => {
if (confirm(`Delete ${selectedOrEntryFile}?`)) {
deleteFile.mutate(selectedOrEntryFile, {
onSuccess: () => {
setSelectedFile(currentEntryFile);
setDraft(null);
},
});
}
}}
disabled={deleteFile.isPending}
>
Delete
</Button>
)}
</div>
{selectedFileExists && fileLoading && !selectedFileDetail ? (
<PromptEditorSkeleton />
) : isMarkdown(selectedOrEntryFile) ? (
<MarkdownEditor
key={selectedOrEntryFile}
value={displayValue}
onChange={(value) => setDraft(value ?? "")}
placeholder="# Agent instructions"
contentClassName="min-h-[420px] text-sm font-mono"
imageUploadHandler={async (file) => {
const namespace = `agents/${agent.id}/instructions/${selectedOrEntryFile.replaceAll("/", "-")}`;
const asset = await uploadMarkdownImage.mutateAsync({ file, namespace });
return asset.contentPath;
}}
/>
) : (
<textarea
value={displayValue}
onChange={(event) => setDraft(event.target.value)}
className="min-h-[420px] w-full rounded-md border border-border bg-transparent px-3 py-2 font-mono text-sm outline-none"
placeholder="File contents"
/>
)}
</div>
</div>
</div>
); );
} }