mirror of
https://github.com/pacnpal/Roo-Code.git
synced 2025-12-20 04:11:10 -05:00
Chat modes
This commit is contained in:
@@ -14,6 +14,7 @@ import ContextMenu from "./ContextMenu"
|
||||
import Thumbnails from "../common/Thumbnails"
|
||||
import { vscode } from "../../utils/vscode"
|
||||
import { WebviewMessage } from "../../../../src/shared/WebviewMessage"
|
||||
import { Mode } from "../../../../src/core/prompts/types"
|
||||
|
||||
interface ChatTextAreaProps {
|
||||
inputValue: string
|
||||
@@ -26,6 +27,8 @@ interface ChatTextAreaProps {
|
||||
onSelectImages: () => void
|
||||
shouldDisableImages: boolean
|
||||
onHeightChange?: (height: number) => void
|
||||
mode: Mode
|
||||
setMode: (value: Mode) => void
|
||||
}
|
||||
|
||||
const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
|
||||
@@ -41,6 +44,8 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
|
||||
onSelectImages,
|
||||
shouldDisableImages,
|
||||
onHeightChange,
|
||||
mode,
|
||||
setMode,
|
||||
},
|
||||
ref,
|
||||
) => {
|
||||
@@ -668,56 +673,98 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{(listApiConfigMeta || []).length > 1 && (
|
||||
<div
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
left: 25,
|
||||
bottom: 19,
|
||||
zIndex: 3,
|
||||
display: "flex",
|
||||
gap: 8,
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<select
|
||||
value={mode}
|
||||
disabled={textAreaDisabled}
|
||||
onChange={(e) => {
|
||||
const newMode = e.target.value as Mode;
|
||||
setMode(newMode);
|
||||
vscode.postMessage({
|
||||
type: "mode",
|
||||
text: newMode
|
||||
});
|
||||
}}
|
||||
style={{
|
||||
position: "absolute",
|
||||
left: 25,
|
||||
bottom: 14,
|
||||
zIndex: 2
|
||||
fontSize: "11px",
|
||||
cursor: textAreaDisabled ? "not-allowed" : "pointer",
|
||||
backgroundColor: "transparent",
|
||||
border: "none",
|
||||
color: "var(--vscode-input-foreground)",
|
||||
opacity: textAreaDisabled ? 0.5 : 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"
|
||||
}}>
|
||||
<option value="code" style={{
|
||||
backgroundColor: "var(--vscode-dropdown-background)",
|
||||
color: "var(--vscode-dropdown-foreground)"
|
||||
}}>Code</option>
|
||||
<option value="architect" style={{
|
||||
backgroundColor: "var(--vscode-dropdown-background)",
|
||||
color: "var(--vscode-dropdown-foreground)"
|
||||
}}>Architect</option>
|
||||
<option value="ask" style={{
|
||||
backgroundColor: "var(--vscode-dropdown-background)",
|
||||
color: "var(--vscode-dropdown-foreground)"
|
||||
}}>Ask</option>
|
||||
</select>
|
||||
<select
|
||||
value={currentApiConfigName}
|
||||
disabled={textAreaDisabled}
|
||||
onChange={(e) => vscode.postMessage({
|
||||
type: "loadApiConfiguration",
|
||||
text: e.target.value
|
||||
})}
|
||||
style={{
|
||||
fontSize: "11px",
|
||||
cursor: textAreaDisabled ? "not-allowed" : "pointer",
|
||||
backgroundColor: "transparent",
|
||||
border: "none",
|
||||
color: "var(--vscode-input-foreground)",
|
||||
opacity: textAreaDisabled ? 0.5 : 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"
|
||||
}}
|
||||
>
|
||||
<select
|
||||
value={currentApiConfigName}
|
||||
disabled={textAreaDisabled}
|
||||
onChange={(e) => vscode.postMessage({
|
||||
type: "loadApiConfiguration",
|
||||
text: e.target.value
|
||||
})}
|
||||
style={{
|
||||
fontSize: "11px",
|
||||
cursor: textAreaDisabled ? "not-allowed" : "pointer",
|
||||
backgroundColor: "transparent",
|
||||
border: "none",
|
||||
color: "var(--vscode-input-foreground)",
|
||||
opacity: textAreaDisabled ? 0.5 : 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" }}>
|
||||
{(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: 12, display: "flex", alignItems: "center", height: 31, bottom: 8, zIndex: 3, padding: "0 8px", justifyContent: "flex-end", backgroundColor: "var(--vscode-input-background)", }}>
|
||||
<span style={{ display: "flex", alignItems: "center", gap: 12 }}>
|
||||
{apiConfiguration?.apiProvider === "openrouter" && (
|
||||
<div style={{ display: "flex", alignItems: "center" }}>
|
||||
|
||||
@@ -38,7 +38,7 @@ interface ChatViewProps {
|
||||
export const MAX_IMAGES_PER_MESSAGE = 20 // Anthropic limits to 20 images
|
||||
|
||||
const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryView }: ChatViewProps) => {
|
||||
const { version, clineMessages: messages, taskHistory, apiConfiguration, mcpServers, alwaysAllowBrowser, alwaysAllowReadOnly, alwaysAllowWrite, alwaysAllowExecute, alwaysAllowMcp, allowedCommands, writeDelayMs } = useExtensionState()
|
||||
const { version, clineMessages: messages, taskHistory, apiConfiguration, mcpServers, alwaysAllowBrowser, alwaysAllowReadOnly, alwaysAllowWrite, alwaysAllowExecute, alwaysAllowMcp, allowedCommands, writeDelayMs, mode, setMode } = useExtensionState()
|
||||
|
||||
//const task = messages.length > 0 ? (messages[0].say === "task" ? messages[0] : undefined) : undefined) : undefined
|
||||
const task = useMemo(() => messages.at(0), [messages]) // leaving this less safe version here since if the first message is not a task, then the extension is in a bad state and needs to be debugged (see Cline.abort)
|
||||
@@ -297,11 +297,13 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie
|
||||
// there is no other case that a textfield should be enabled
|
||||
}
|
||||
}
|
||||
// Only reset message-specific state, preserving mode
|
||||
setInputValue("")
|
||||
setTextAreaDisabled(true)
|
||||
setSelectedImages([])
|
||||
setClineAsk(undefined)
|
||||
setEnableButtons(false)
|
||||
// Do not reset mode here as it should persist
|
||||
// setPrimaryButtonText(undefined)
|
||||
// setSecondaryButtonText(undefined)
|
||||
disableAutoScrollRef.current = false
|
||||
@@ -338,8 +340,6 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie
|
||||
setTextAreaDisabled(true)
|
||||
setClineAsk(undefined)
|
||||
setEnableButtons(false)
|
||||
// setPrimaryButtonText(undefined)
|
||||
// setSecondaryButtonText(undefined)
|
||||
disableAutoScrollRef.current = false
|
||||
}, [clineAsk, startNewTask])
|
||||
|
||||
@@ -367,8 +367,6 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie
|
||||
setTextAreaDisabled(true)
|
||||
setClineAsk(undefined)
|
||||
setEnableButtons(false)
|
||||
// setPrimaryButtonText(undefined)
|
||||
// setSecondaryButtonText(undefined)
|
||||
disableAutoScrollRef.current = false
|
||||
}, [clineAsk, startNewTask, isStreaming])
|
||||
|
||||
@@ -779,9 +777,12 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie
|
||||
useEvent("wheel", handleWheel, window, { passive: true }) // passive improves scrolling performance
|
||||
|
||||
const placeholderText = useMemo(() => {
|
||||
const text = task ? "Type a message...\n(@ to add context, hold shift to drag in images)" : "Type your task here...\n(@ to add context, hold shift to drag in images)"
|
||||
return text
|
||||
}, [task])
|
||||
const baseText = task ? "Type a message..." : "Type your task here..."
|
||||
const contextText = "(@ to add context"
|
||||
const imageText = shouldDisableImages ? "" : ", hold shift to drag in images"
|
||||
const helpText = imageText ? `\n${contextText}${imageText})` : `\n${contextText})`
|
||||
return baseText + helpText
|
||||
}, [task, shouldDisableImages])
|
||||
|
||||
const itemContent = useCallback(
|
||||
(index: number, messageOrGroup: ClineMessage | ClineMessage[]) => {
|
||||
@@ -983,6 +984,8 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie
|
||||
scrollToBottomAuto()
|
||||
}
|
||||
}}
|
||||
mode={mode}
|
||||
setMode={setMode}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -263,6 +263,7 @@ describe('ChatView - Auto Approval Tests', () => {
|
||||
// First hydrate state with initial task
|
||||
mockPostMessage({
|
||||
alwaysAllowWrite: true,
|
||||
writeDelayMs: 0,
|
||||
clineMessages: [
|
||||
{
|
||||
type: 'say',
|
||||
@@ -276,6 +277,7 @@ describe('ChatView - Auto Approval Tests', () => {
|
||||
// Then send the write tool ask message
|
||||
mockPostMessage({
|
||||
alwaysAllowWrite: true,
|
||||
writeDelayMs: 0,
|
||||
clineMessages: [
|
||||
{
|
||||
type: 'say',
|
||||
|
||||
@@ -6,6 +6,7 @@ import { vscode } from "../../utils/vscode"
|
||||
import ApiOptions from "./ApiOptions"
|
||||
import McpEnabledToggle from "../mcp/McpEnabledToggle"
|
||||
import ApiConfigManager from "./ApiConfigManager"
|
||||
import { Mode } from "../../../../src/shared/modes"
|
||||
|
||||
const IS_DEV = false // FIXME: use flags when packaging
|
||||
|
||||
@@ -58,6 +59,8 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {
|
||||
setRequestDelaySeconds,
|
||||
currentApiConfigName,
|
||||
listApiConfigMeta,
|
||||
mode,
|
||||
setMode,
|
||||
} = useExtensionState()
|
||||
const [apiErrorMessage, setApiErrorMessage] = useState<string | undefined>(undefined)
|
||||
const [modelIdErrorMessage, setModelIdErrorMessage] = useState<string | undefined>(undefined)
|
||||
@@ -99,7 +102,7 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {
|
||||
text: currentApiConfigName,
|
||||
apiConfiguration
|
||||
})
|
||||
|
||||
vscode.postMessage({ type: "mode", text: mode })
|
||||
onDone()
|
||||
}
|
||||
}
|
||||
@@ -203,6 +206,37 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {
|
||||
<div style={{ marginBottom: 15 }}>
|
||||
<h3 style={{ color: "var(--vscode-foreground)", margin: 0, marginBottom: 15 }}>Agent Settings</h3>
|
||||
|
||||
<div style={{ marginBottom: 15 }}>
|
||||
<label style={{ fontWeight: "500", display: "block", marginBottom: 5 }}>Agent Mode</label>
|
||||
<select
|
||||
value={mode}
|
||||
onChange={(e) => {
|
||||
const value = e.target.value as Mode
|
||||
setMode(value)
|
||||
vscode.postMessage({ type: "mode", text: value })
|
||||
}}
|
||||
style={{
|
||||
width: "100%",
|
||||
padding: "4px 8px",
|
||||
backgroundColor: "var(--vscode-input-background)",
|
||||
color: "var(--vscode-input-foreground)",
|
||||
border: "1px solid var(--vscode-input-border)",
|
||||
borderRadius: "2px",
|
||||
height: "28px"
|
||||
}}>
|
||||
<option value="code">Code</option>
|
||||
<option value="architect">Architect</option>
|
||||
<option value="ask">Ask</option>
|
||||
</select>
|
||||
<p style={{
|
||||
fontSize: "12px",
|
||||
marginTop: "5px",
|
||||
color: "var(--vscode-descriptionForeground)",
|
||||
}}>
|
||||
Select the mode that best fits your needs. Code mode focuses on implementation details, Architect mode on high-level design, and Ask mode on asking questions about the codebase.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<label style={{ fontWeight: "500", display: "block", marginBottom: 5 }}>Preferred Language</label>
|
||||
<select
|
||||
value={preferredLanguage}
|
||||
|
||||
@@ -16,6 +16,8 @@ import { McpServer } from "../../../src/shared/mcp"
|
||||
import {
|
||||
checkExistKey
|
||||
} from "../../../src/shared/checkExistApiConfig"
|
||||
import { Mode } from "../../../src/core/prompts/types"
|
||||
import { codeMode } from "../../../src/shared/modes"
|
||||
|
||||
export interface ExtensionStateContextType extends ExtensionState {
|
||||
didHydrateState: boolean
|
||||
@@ -56,6 +58,8 @@ export interface ExtensionStateContextType extends ExtensionState {
|
||||
setCurrentApiConfigName: (value: string) => void
|
||||
setListApiConfigMeta: (value: ApiConfigMeta[]) => void
|
||||
onUpdateApiConfig: (apiConfig: ApiConfiguration) => void
|
||||
mode: Mode
|
||||
setMode: (value: Mode) => void
|
||||
}
|
||||
|
||||
export const ExtensionStateContext = createContext<ExtensionStateContextType | undefined>(undefined)
|
||||
@@ -81,6 +85,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
|
||||
requestDelaySeconds: 5,
|
||||
currentApiConfigName: 'default',
|
||||
listApiConfigMeta: [],
|
||||
mode: codeMode,
|
||||
})
|
||||
const [didHydrateState, setDidHydrateState] = useState(false)
|
||||
const [showWelcome, setShowWelcome] = useState(false)
|
||||
@@ -111,7 +116,10 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
|
||||
const message: ExtensionMessage = event.data
|
||||
switch (message.type) {
|
||||
case "state": {
|
||||
setState(message.state!)
|
||||
setState(prevState => ({
|
||||
...prevState,
|
||||
...message.state!
|
||||
}))
|
||||
const config = message.state?.apiConfiguration
|
||||
const hasKey = checkExistKey(config)
|
||||
setShowWelcome(!hasKey)
|
||||
@@ -220,7 +228,8 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
|
||||
setRequestDelaySeconds: (value) => setState((prevState) => ({ ...prevState, requestDelaySeconds: value })),
|
||||
setCurrentApiConfigName: (value) => setState((prevState) => ({ ...prevState, currentApiConfigName: value })),
|
||||
setListApiConfigMeta,
|
||||
onUpdateApiConfig
|
||||
onUpdateApiConfig,
|
||||
setMode: (value: Mode) => setState((prevState) => ({ ...prevState, mode: value })),
|
||||
}
|
||||
|
||||
return <ExtensionStateContext.Provider value={contextValue}>{children}</ExtensionStateContext.Provider>
|
||||
|
||||
Reference in New Issue
Block a user