mirror of
https://github.com/pacnpal/Roo-Code.git
synced 2025-12-20 04:11:10 -05:00
UI cleanup
This commit is contained in:
@@ -44,9 +44,21 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
|
||||
},
|
||||
ref,
|
||||
) => {
|
||||
const { filePaths, apiConfiguration } = useExtensionState()
|
||||
const { filePaths, apiConfiguration, currentApiConfigName, listApiConfigMeta } = useExtensionState()
|
||||
const [isTextAreaFocused, setIsTextAreaFocused] = useState(false)
|
||||
const [gitCommits, setGitCommits] = useState<any[]>([])
|
||||
const [showDropdown, setShowDropdown] = useState(false)
|
||||
|
||||
// Close dropdown when clicking outside
|
||||
useEffect(() => {
|
||||
const handleClickOutside = (event: MouseEvent) => {
|
||||
if (showDropdown) {
|
||||
setShowDropdown(false)
|
||||
}
|
||||
}
|
||||
document.addEventListener("mousedown", handleClickOutside)
|
||||
return () => document.removeEventListener("mousedown", handleClickOutside)
|
||||
}, [showDropdown])
|
||||
|
||||
// Handle enhanced prompt response
|
||||
useEffect(() => {
|
||||
@@ -656,6 +668,54 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{(listApiConfigMeta || []).length > 1 && (
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
left: 25,
|
||||
bottom: 14,
|
||||
zIndex: 2
|
||||
}}
|
||||
>
|
||||
<select
|
||||
value={currentApiConfigName}
|
||||
onChange={(e) => vscode.postMessage({
|
||||
type: "loadApiConfiguration",
|
||||
text: e.target.value
|
||||
})}
|
||||
style={{
|
||||
fontSize: "11px",
|
||||
cursor: "pointer",
|
||||
backgroundColor: "transparent",
|
||||
border: "none",
|
||||
color: "var(--vscode-input-foreground)",
|
||||
opacity: 0.6,
|
||||
outline: "none",
|
||||
paddingLeft: 14,
|
||||
WebkitAppearance: "none",
|
||||
MozAppearance: "none",
|
||||
appearance: "none",
|
||||
backgroundImage: "url(\"data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='rgba(255,255,255,0.5)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='6 9 12 15 18 9'%3e%3c/polyline%3e%3c/svg%3e\")",
|
||||
backgroundRepeat: "no-repeat",
|
||||
backgroundPosition: "left 0px center",
|
||||
backgroundSize: "10px"
|
||||
}}
|
||||
>
|
||||
{(listApiConfigMeta || [])?.map((config) => (
|
||||
<option
|
||||
key={config.name}
|
||||
value={config.name}
|
||||
style={{
|
||||
backgroundColor: "var(--vscode-dropdown-background)",
|
||||
color: "var(--vscode-dropdown-foreground)"
|
||||
}}
|
||||
>
|
||||
{config.name}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
)}
|
||||
<div className="button-row" style={{ position: "absolute", right: 20, display: "flex", alignItems: "center", height: 31, bottom: 8, zIndex: 2, justifyContent: "flex-end" }}>
|
||||
<span style={{ display: "flex", alignItems: "center", gap: 12 }}>
|
||||
{apiConfiguration?.apiProvider === "openrouter" && (
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { VSCodeButton, VSCodeDivider, VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
|
||||
import { memo, useState } from "react"
|
||||
import { VSCodeButton, VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
|
||||
import { memo, useEffect, useRef, useState } from "react"
|
||||
import { ApiConfigMeta } from "../../../../src/shared/ExtensionMessage"
|
||||
|
||||
interface ApiConfigManagerProps {
|
||||
@@ -9,7 +9,6 @@ interface ApiConfigManagerProps {
|
||||
onDeleteConfig: (configName: string) => void
|
||||
onRenameConfig: (oldName: string, newName: string) => void
|
||||
onUpsertConfig: (configName: string) => void
|
||||
// setDraftNewConfig: (mode: boolean) => void
|
||||
}
|
||||
|
||||
const ApiConfigManager = ({
|
||||
@@ -19,145 +18,206 @@ const ApiConfigManager = ({
|
||||
onDeleteConfig,
|
||||
onRenameConfig,
|
||||
onUpsertConfig,
|
||||
// setDraftNewConfig,
|
||||
}: ApiConfigManagerProps) => {
|
||||
const [isNewMode, setIsNewMode] = useState(false);
|
||||
const [isRenameMode, setIsRenameMode] = useState(false);
|
||||
const [newConfigName, setNewConfigName] = useState("");
|
||||
const [renamedConfigName, setRenamedConfigName] = useState("");
|
||||
const [editState, setEditState] = useState<'new' | 'rename' | null>(null);
|
||||
const [inputValue, setInputValue] = useState("");
|
||||
const inputRef = useRef<HTMLInputElement>();
|
||||
|
||||
const handleNewConfig = () => {
|
||||
setIsNewMode(true);
|
||||
setNewConfigName("");
|
||||
// setDraftNewConfig(true)
|
||||
};
|
||||
|
||||
const handleSaveNewConfig = () => {
|
||||
if (newConfigName.trim()) {
|
||||
onUpsertConfig(newConfigName.trim());
|
||||
setIsNewMode(false);
|
||||
setNewConfigName("");
|
||||
// setDraftNewConfig(false)
|
||||
// Focus input when entering edit mode
|
||||
useEffect(() => {
|
||||
if (editState) {
|
||||
setTimeout(() => inputRef.current?.focus(), 0);
|
||||
}
|
||||
};
|
||||
}, [editState]);
|
||||
|
||||
const handleCancelNewConfig = () => {
|
||||
setIsNewMode(false);
|
||||
setNewConfigName("");
|
||||
// setDraftNewConfig(false)
|
||||
// Reset edit state when current profile changes
|
||||
useEffect(() => {
|
||||
setEditState(null);
|
||||
setInputValue("");
|
||||
}, [currentApiConfigName]);
|
||||
|
||||
const handleStartNew = () => {
|
||||
setEditState('new');
|
||||
setInputValue("");
|
||||
};
|
||||
|
||||
const handleStartRename = () => {
|
||||
setIsRenameMode(true);
|
||||
setRenamedConfigName(currentApiConfigName || "");
|
||||
setEditState('rename');
|
||||
setInputValue(currentApiConfigName || "");
|
||||
};
|
||||
|
||||
const handleSaveRename = () => {
|
||||
if (renamedConfigName.trim() && currentApiConfigName) {
|
||||
onRenameConfig(currentApiConfigName, renamedConfigName.trim());
|
||||
setIsRenameMode(false);
|
||||
setRenamedConfigName("");
|
||||
const handleCancel = () => {
|
||||
setEditState(null);
|
||||
setInputValue("");
|
||||
};
|
||||
|
||||
const handleSave = () => {
|
||||
const trimmedValue = inputValue.trim();
|
||||
if (!trimmedValue) return;
|
||||
|
||||
if (editState === 'new') {
|
||||
onUpsertConfig(trimmedValue);
|
||||
} else if (editState === 'rename' && currentApiConfigName) {
|
||||
onRenameConfig(currentApiConfigName, trimmedValue);
|
||||
}
|
||||
|
||||
setEditState(null);
|
||||
setInputValue("");
|
||||
};
|
||||
|
||||
const handleCancelRename = () => {
|
||||
setIsRenameMode(false);
|
||||
setRenamedConfigName("");
|
||||
const handleDelete = () => {
|
||||
if (!currentApiConfigName || !listApiConfigMeta || listApiConfigMeta.length <= 1) return;
|
||||
|
||||
// Let the extension handle both deletion and selection
|
||||
onDeleteConfig(currentApiConfigName);
|
||||
};
|
||||
|
||||
const isOnlyProfile = listApiConfigMeta?.length === 1;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<label style={{ fontWeight: "500", display: "block", marginBottom: 5 }}>
|
||||
API Configuration
|
||||
</label>
|
||||
<div style={{ display: "flex", gap: "8px", alignItems: "center" }}>
|
||||
{isNewMode ? (
|
||||
<>
|
||||
<div style={{ marginBottom: 5 }}>
|
||||
<div style={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: "2px"
|
||||
}}>
|
||||
<label htmlFor="config-profile">
|
||||
<span style={{ fontWeight: "500" }}>Configuration Profile</span>
|
||||
</label>
|
||||
|
||||
{editState ? (
|
||||
<div style={{ display: "flex", gap: "4px", alignItems: "center" }}>
|
||||
<VSCodeTextField
|
||||
value={newConfigName}
|
||||
onInput={(e: any) => setNewConfigName(e.target.value)}
|
||||
placeholder="Enter configuration name"
|
||||
ref={inputRef as any}
|
||||
value={inputValue}
|
||||
onInput={(e: any) => setInputValue(e.target.value)}
|
||||
placeholder={editState === 'new' ? "Enter profile name" : "Enter new name"}
|
||||
style={{ flexGrow: 1 }}
|
||||
onKeyDown={(e: any) => {
|
||||
if (e.key === 'Enter' && inputValue.trim()) {
|
||||
handleSave();
|
||||
} else if (e.key === 'Escape') {
|
||||
handleCancel();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<VSCodeButton
|
||||
appearance="secondary"
|
||||
disabled={!newConfigName.trim()}
|
||||
onClick={handleSaveNewConfig}
|
||||
appearance="icon"
|
||||
disabled={!inputValue.trim()}
|
||||
onClick={handleSave}
|
||||
title="Save"
|
||||
style={{
|
||||
padding: 0,
|
||||
margin: 0,
|
||||
height: '28px',
|
||||
width: '28px',
|
||||
minWidth: '28px'
|
||||
}}
|
||||
>
|
||||
<span className="codicon codicon-check" /> Save
|
||||
<span className="codicon codicon-check" />
|
||||
</VSCodeButton>
|
||||
<VSCodeButton
|
||||
appearance="secondary"
|
||||
onClick={handleCancelNewConfig}
|
||||
appearance="icon"
|
||||
onClick={handleCancel}
|
||||
title="Cancel"
|
||||
style={{
|
||||
padding: 0,
|
||||
margin: 0,
|
||||
height: '28px',
|
||||
width: '28px',
|
||||
minWidth: '28px'
|
||||
}}
|
||||
>
|
||||
<span className="codicon codicon-close" /> Cancel
|
||||
<span className="codicon codicon-close" />
|
||||
</VSCodeButton>
|
||||
</>
|
||||
) : isRenameMode ? (
|
||||
<>
|
||||
<VSCodeTextField
|
||||
value={renamedConfigName}
|
||||
onInput={(e: any) => setRenamedConfigName(e.target.value)}
|
||||
placeholder="Enter new name"
|
||||
style={{ flexGrow: 1 }}
|
||||
/>
|
||||
<VSCodeButton
|
||||
appearance="secondary"
|
||||
disabled={!renamedConfigName.trim()}
|
||||
onClick={handleSaveRename}
|
||||
>
|
||||
<span className="codicon codicon-check" /> Save
|
||||
</VSCodeButton>
|
||||
<VSCodeButton
|
||||
appearance="secondary"
|
||||
onClick={handleCancelRename}
|
||||
>
|
||||
<span className="codicon codicon-close" /> Cancel
|
||||
</VSCodeButton>
|
||||
</>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<select
|
||||
value={currentApiConfigName}
|
||||
onChange={(e) => onSelectConfig(e.target.value)}
|
||||
style={{
|
||||
flexGrow: 1,
|
||||
padding: "4px 8px",
|
||||
backgroundColor: "var(--vscode-input-background)",
|
||||
color: "var(--vscode-input-foreground)",
|
||||
border: "1px solid var(--vscode-input-border)",
|
||||
borderRadius: "2px",
|
||||
height: "28px"
|
||||
}}>
|
||||
{listApiConfigMeta?.map((config) => (
|
||||
<option key={config.name} value={config.name}>{config.name} {config.apiProvider ? `(${config.apiProvider})` : ""}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
<VSCodeButton
|
||||
appearance="secondary"
|
||||
onClick={handleNewConfig}
|
||||
>
|
||||
<span className="codicon codicon-add" /> New
|
||||
</VSCodeButton>
|
||||
<VSCodeButton
|
||||
appearance="secondary"
|
||||
disabled={!currentApiConfigName}
|
||||
onClick={handleStartRename}
|
||||
>
|
||||
<span className="codicon codicon-edit" /> Rename
|
||||
</VSCodeButton>
|
||||
<VSCodeButton
|
||||
appearance="secondary"
|
||||
disabled={!currentApiConfigName}
|
||||
onClick={() => onDeleteConfig(currentApiConfigName!)}
|
||||
>
|
||||
<span className="codicon codicon-trash" /> Delete
|
||||
</VSCodeButton>
|
||||
<div style={{ display: "flex", gap: "4px", alignItems: "center" }}>
|
||||
<select
|
||||
id="config-profile"
|
||||
value={currentApiConfigName}
|
||||
onChange={(e) => onSelectConfig(e.target.value)}
|
||||
style={{
|
||||
flexGrow: 1,
|
||||
padding: "4px 8px",
|
||||
paddingRight: "24px",
|
||||
backgroundColor: "var(--vscode-dropdown-background)",
|
||||
color: "var(--vscode-dropdown-foreground)",
|
||||
border: "1px solid var(--vscode-dropdown-border)",
|
||||
borderRadius: "2px",
|
||||
height: "28px",
|
||||
cursor: "pointer",
|
||||
outline: "none"
|
||||
}}
|
||||
>
|
||||
{listApiConfigMeta?.map((config) => (
|
||||
<option
|
||||
key={config.name}
|
||||
value={config.name}
|
||||
>
|
||||
{config.name}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
<VSCodeButton
|
||||
appearance="icon"
|
||||
onClick={handleStartNew}
|
||||
title="Add profile"
|
||||
style={{
|
||||
padding: 0,
|
||||
margin: 0,
|
||||
height: '28px',
|
||||
width: '28px',
|
||||
minWidth: '28px'
|
||||
}}
|
||||
>
|
||||
<span className="codicon codicon-add" />
|
||||
</VSCodeButton>
|
||||
{currentApiConfigName && (
|
||||
<>
|
||||
<VSCodeButton
|
||||
appearance="icon"
|
||||
onClick={handleStartRename}
|
||||
title="Rename profile"
|
||||
style={{
|
||||
padding: 0,
|
||||
margin: 0,
|
||||
height: '28px',
|
||||
width: '28px',
|
||||
minWidth: '28px'
|
||||
}}
|
||||
>
|
||||
<span className="codicon codicon-edit" />
|
||||
</VSCodeButton>
|
||||
<VSCodeButton
|
||||
appearance="icon"
|
||||
onClick={handleDelete}
|
||||
title={isOnlyProfile ? "Cannot delete the only profile" : "Delete profile"}
|
||||
disabled={isOnlyProfile}
|
||||
style={{
|
||||
padding: 0,
|
||||
margin: 0,
|
||||
height: '28px',
|
||||
width: '28px',
|
||||
minWidth: '28px'
|
||||
}}
|
||||
>
|
||||
<span className="codicon codicon-trash" />
|
||||
</VSCodeButton>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<p style={{
|
||||
fontSize: "12px",
|
||||
margin: "5px 0 12px",
|
||||
color: "var(--vscode-descriptionForeground)"
|
||||
}}>
|
||||
Save different API configurations to quickly switch between providers and settings
|
||||
</p>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<VSCodeDivider style={{ margin: "15px 0" }} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -164,6 +164,7 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {
|
||||
<div
|
||||
style={{ flexGrow: 1, overflowY: "scroll", paddingRight: 8, display: "flex", flexDirection: "column" }}>
|
||||
<div style={{ marginBottom: 5 }}>
|
||||
<h3 style={{ color: "var(--vscode-foreground)", margin: 0, marginBottom: 15 }}>Provider Settings</h3>
|
||||
<ApiConfigManager
|
||||
currentApiConfigName={currentApiConfigName}
|
||||
listApiConfigMeta={listApiConfigMeta}
|
||||
@@ -193,14 +194,7 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {
|
||||
apiConfiguration
|
||||
})
|
||||
}}
|
||||
// setDraftNewConfig={(mode: boolean) => {
|
||||
// setDraftNewMode(mode)
|
||||
// }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div style={{ marginBottom: 5 }}>
|
||||
<h3 style={{ color: "var(--vscode-foreground)", margin: 0, marginBottom: 15 }}>Provider Settings</h3>
|
||||
<ApiOptions
|
||||
showModelOptions={true}
|
||||
apiErrorMessage={apiErrorMessage}
|
||||
|
||||
Reference in New Issue
Block a user