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:
parent
c7d31346e0
commit
5252568825
1 changed files with 161 additions and 143 deletions
|
|
@ -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>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue