Skip to content

Commit fbc9216

Browse files
committed
rebase onto branch with changes
1 parent 99258e4 commit fbc9216

20 files changed

+765
-536
lines changed

.changeset/quiet-boxes-smoke.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"roo-cline": minor
3+
---
4+
5+
Redesign chat window, update start screen, fix chat disabling.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@
7777
{
7878
"command": "roo-cline.mcpButtonClicked",
7979
"title": "MCP Servers",
80-
"icon": "$(server)"
80+
"icon": "$(extensions)"
8181
},
8282
{
8383
"command": "roo-cline.promptsButtonClicked",

src/core/webview/__tests__/ClineProvider.test.ts

Lines changed: 2 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,8 @@ import { ClineProvider } from "../ClineProvider"
77
import { ExtensionMessage, ExtensionState } from "../../../shared/ExtensionMessage"
88
import { GlobalStateKey, SecretKey } from "../../../shared/globalState"
99
import { setSoundEnabled } from "../../../utils/sound"
10-
import { defaultModeSlug } from "../../../shared/modes"
1110
import { experimentDefault } from "../../../shared/experiments"
12-
import { Cline } from "../../Cline"
11+
import { defaultExtensionState } from "../../../shared/ExtensionMessage"
1312

1413
// Mock setup must come before imports
1514
jest.mock("../../prompts/sections/custom-instructions")
@@ -417,11 +416,7 @@ describe("ClineProvider", () => {
417416
await provider.resolveWebviewView(mockWebviewView)
418417

419418
const mockState: ExtensionState = {
420-
version: "1.0.0",
421-
preferredLanguage: "English",
422-
clineMessages: [],
423-
taskHistory: [],
424-
shouldShowAnnouncement: false,
419+
...defaultExtensionState,
425420
apiConfiguration: {
426421
apiProvider: "openrouter",
427422
},
@@ -432,24 +427,10 @@ describe("ClineProvider", () => {
432427
alwaysAllowBrowser: false,
433428
alwaysAllowMcp: false,
434429
uriScheme: "vscode",
435-
soundEnabled: false,
436-
diffEnabled: false,
437430
enableCheckpoints: false,
438-
checkpointStorage: "task",
439-
writeDelayMs: 1000,
440-
browserViewportSize: "900x600",
441431
fuzzyMatchThreshold: 1.0,
442-
mcpEnabled: true,
443432
enableMcpServerCreation: false,
444-
requestDelaySeconds: 5,
445-
rateLimitSeconds: 0,
446-
mode: defaultModeSlug,
447433
customModes: [],
448-
experiments: experimentDefault,
449-
maxOpenTabsContext: 20,
450-
browserToolEnabled: true,
451-
telemetrySetting: "unset",
452-
showRooIgnoredFiles: true,
453434
}
454435

455436
const message: ExtensionMessage = {

src/shared/ExtensionMessage.ts

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@ import { ApiConfiguration, ApiProvider, ModelInfo } from "./api"
22
import { HistoryItem } from "./HistoryItem"
33
import { McpServer } from "./mcp"
44
import { GitCommit } from "../utils/git"
5-
import { Mode, CustomModePrompts, ModeConfig } from "./modes"
5+
import { Mode, CustomModePrompts, ModeConfig, defaultModeSlug, defaultPrompts } from "./modes"
66
import { CustomSupportPrompts } from "./support-prompt"
7-
import { ExperimentId } from "./experiments"
7+
import { experimentDefault, ExperimentId } from "./experiments"
8+
import { TERMINAL_OUTPUT_LIMIT } from "./terminal"
9+
import { ClineMessage, ClineAsk, ClineSay } from "../exports/roo-code"
810
import { CheckpointStorage } from "./checkpoints"
911
import { TelemetrySetting } from "./TelemetrySetting"
10-
import { ClineMessage, ClineAsk, ClineSay } from "../exports/roo-code"
1112

1213
export interface LanguageModelChatSelector {
1314
vendor?: string
@@ -97,6 +98,11 @@ export interface ApiConfigMeta {
9798
}
9899

99100
export interface ExtensionState {
101+
glamaModels?: Record<string, ModelInfo>
102+
requestyModels?: Record<string, ModelInfo>
103+
openRouterModels?: Record<string, ModelInfo>
104+
unboundModels?: Record<string, ModelInfo>
105+
openAiModels?: string[]
100106
version: string
101107
clineMessages: ClineMessage[]
102108
taskHistory: HistoryItem[]
@@ -231,8 +237,46 @@ export interface HumanRelayCancelMessage {
231237
}
232238

233239
export type ClineApiReqCancelReason = "streaming_failed" | "user_cancelled"
234-
235240
export type ToolProgressStatus = {
236241
icon?: string
237242
text?: string
238243
}
244+
245+
export const defaultExtensionState: ExtensionState = {
246+
version: "",
247+
clineMessages: [],
248+
taskHistory: [],
249+
shouldShowAnnouncement: false,
250+
allowedCommands: [],
251+
soundEnabled: false,
252+
soundVolume: 0.5,
253+
diffEnabled: false,
254+
enableCheckpoints: true,
255+
checkpointStorage: "task",
256+
fuzzyMatchThreshold: 1.0,
257+
preferredLanguage: "English",
258+
enableCustomModeCreation: true,
259+
writeDelayMs: 1000,
260+
browserViewportSize: "900x600",
261+
screenshotQuality: 75,
262+
terminalOutputLimit: TERMINAL_OUTPUT_LIMIT,
263+
mcpEnabled: true,
264+
enableMcpServerCreation: true,
265+
alwaysApproveResubmit: false,
266+
requestDelaySeconds: 5,
267+
rateLimitSeconds: 0, // Minimum time between successive requests (0 = disabled)
268+
currentApiConfigName: "default",
269+
listApiConfigMeta: [],
270+
mode: defaultModeSlug,
271+
customModePrompts: defaultPrompts,
272+
customSupportPrompts: {},
273+
experiments: experimentDefault,
274+
enhancementApiConfigId: "",
275+
autoApprovalEnabled: false,
276+
customModes: [],
277+
maxOpenTabsContext: 20,
278+
cwd: "",
279+
browserToolEnabled: true,
280+
telemetrySetting: "unset",
281+
showRooIgnoredFiles: true, // Default to showing .rooignore'd files with lock symbol (current behavior)
282+
}
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
import React, { forwardRef, useEffect, useState } from "react"
2+
import ChatTextAreaLayout from "./ChatTextAreaLayout"
3+
import ChatTextAreaInput from "./ChatTextAreaInput"
4+
import ChatTextAreaSelections from "./ChatTextAreaSelections"
5+
import ChatTextAreaActions from "./ChatTextAreaActions"
6+
import { useExtensionState } from "@/context/ExtensionStateContext"
7+
import { ContextMenuOptionType } from "@/utils/context-mentions"
8+
import { Mode } from "../../../../../src/shared/modes"
9+
10+
interface ChatTextAreaProps {
11+
inputValue: string
12+
setInputValue: (value: string) => void
13+
textAreaDisabled: boolean
14+
placeholderText: string
15+
selectedImages: string[]
16+
setSelectedImages: React.Dispatch<React.SetStateAction<string[]>>
17+
onSend: () => void
18+
onSelectImages: () => void
19+
shouldDisableImages: boolean
20+
onHeightChange?: (height: number) => void
21+
mode: Mode
22+
setMode: (value: Mode) => void
23+
modeShortcutText: string
24+
}
25+
26+
const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
27+
(
28+
{
29+
inputValue,
30+
setInputValue,
31+
textAreaDisabled,
32+
placeholderText,
33+
selectedImages,
34+
setSelectedImages,
35+
onSend,
36+
onSelectImages,
37+
shouldDisableImages,
38+
onHeightChange,
39+
mode,
40+
setMode,
41+
modeShortcutText,
42+
},
43+
ref,
44+
) => {
45+
const { filePaths, openedTabs, currentApiConfigName, listApiConfigMeta, customModes, cwd } = useExtensionState()
46+
const [gitCommits, setGitCommits] = useState<any[]>([])
47+
const [isEnhancingPrompt, setIsEnhancingPrompt] = useState(false)
48+
const [showDropdown, setShowDropdown] = useState(false)
49+
50+
// Close dropdown when clicking outside
51+
useEffect(() => {
52+
const handleClickOutside = (event: MouseEvent) => {
53+
if (showDropdown) {
54+
setShowDropdown(false)
55+
}
56+
}
57+
document.addEventListener("mousedown", handleClickOutside)
58+
return () => document.removeEventListener("mousedown", handleClickOutside)
59+
}, [showDropdown])
60+
61+
// Handle enhanced prompt response
62+
useEffect(() => {
63+
const messageHandler = (event: MessageEvent) => {
64+
const message = event.data
65+
if (message.type === "enhancedPrompt") {
66+
if (message.text) {
67+
setInputValue(message.text)
68+
}
69+
setIsEnhancingPrompt(false)
70+
} else if (message.type === "commitSearchResults") {
71+
const commits = message.commits.map((commit: any) => ({
72+
type: ContextMenuOptionType.Git,
73+
value: commit.hash,
74+
label: commit.subject,
75+
description: `${commit.shortHash} by ${commit.author} on ${commit.date}`,
76+
icon: "$(git-commit)",
77+
}))
78+
setGitCommits(commits)
79+
}
80+
}
81+
window.addEventListener("message", messageHandler)
82+
return () => window.removeEventListener("message", messageHandler)
83+
}, [setInputValue, setIsEnhancingPrompt])
84+
85+
return (
86+
<ChatTextAreaLayout>
87+
{{
88+
input: (
89+
<ChatTextAreaInput
90+
ref={ref}
91+
inputValue={inputValue}
92+
setInputValue={setInputValue}
93+
textAreaDisabled={textAreaDisabled}
94+
placeholderText={placeholderText}
95+
selectedImages={selectedImages}
96+
setSelectedImages={setSelectedImages}
97+
onSend={onSend}
98+
onHeightChange={onHeightChange}
99+
setMode={setMode}
100+
customModes={customModes}
101+
filePaths={filePaths}
102+
openedTabs={openedTabs}
103+
gitCommits={gitCommits}
104+
shouldDisableImages={shouldDisableImages}
105+
cwd={cwd}
106+
/>
107+
),
108+
selections: (
109+
<ChatTextAreaSelections
110+
mode={mode}
111+
setMode={setMode}
112+
currentApiConfigName={currentApiConfigName}
113+
listApiConfigMeta={listApiConfigMeta}
114+
customModes={customModes}
115+
modeShortcutText={modeShortcutText}
116+
/>
117+
),
118+
actions: (
119+
<ChatTextAreaActions
120+
textAreaDisabled={textAreaDisabled}
121+
shouldDisableImages={shouldDisableImages}
122+
isEnhancingPrompt={isEnhancingPrompt}
123+
inputValue={inputValue}
124+
setInputValue={setInputValue}
125+
onSelectImages={onSelectImages}
126+
onSend={onSend}
127+
setIsEnhancingPrompt={setIsEnhancingPrompt}
128+
/>
129+
),
130+
}}
131+
</ChatTextAreaLayout>
132+
)
133+
},
134+
)
135+
136+
export default ChatTextArea
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import React from "react"
2+
import { vscode } from "../../../utils/vscode"
3+
import { cn } from "@/lib/utils"
4+
5+
interface ChatTextAreaActionsProps {
6+
textAreaDisabled: boolean
7+
shouldDisableImages: boolean
8+
isEnhancingPrompt: boolean
9+
inputValue: string
10+
setInputValue: (value: string) => void
11+
onSelectImages: () => void
12+
onSend: () => void
13+
setIsEnhancingPrompt: (value: boolean) => void
14+
}
15+
16+
const baseActionButton = [
17+
"text-base relative inline-flex items-center justify-center",
18+
"bg-transparent border-none outline-none opacity-50",
19+
"p-1.5 rounded-md transition-all duration-200",
20+
"min-w-[28px] min-h-[28px] cursor-pointer",
21+
"text-vscode-foreground",
22+
"hover:bg-[color:color-mix(in_srgb,var(--vscode-button-hoverBackground)_40%,transparent)]",
23+
"hover:opacity-75",
24+
]
25+
26+
const disabledButton = "opacity-35 cursor-not-allowed grayscale-[30%] hover:bg-transparent"
27+
28+
const ChatTextAreaActions: React.FC<ChatTextAreaActionsProps> = ({
29+
textAreaDisabled,
30+
shouldDisableImages,
31+
isEnhancingPrompt,
32+
inputValue,
33+
setInputValue,
34+
onSelectImages,
35+
onSend,
36+
setIsEnhancingPrompt,
37+
}) => {
38+
const handleEnhancePrompt = () => {
39+
if (!textAreaDisabled) {
40+
const trimmedInput = inputValue.trim()
41+
if (trimmedInput) {
42+
setIsEnhancingPrompt(true)
43+
const message = {
44+
type: "enhancePrompt" as const,
45+
text: trimmedInput,
46+
}
47+
vscode.postMessage(message)
48+
} else {
49+
const promptDescription =
50+
"The 'Enhance Prompt' button helps improve your prompt by providing additional context, clarification, or rephrasing. Try typing a prompt in here and clicking the button again to see how it works."
51+
setInputValue(promptDescription)
52+
}
53+
}
54+
}
55+
56+
return (
57+
<div className="flex items-center gap-0.5 flex-wrap justify-end min-w-0">
58+
<div className="flex items-center gap-0.5 shrink-0">
59+
<span
60+
role="button"
61+
aria-label="Enhance prompt with additional context"
62+
title="Enhance prompt with additional context"
63+
data-testid="enhance-prompt-button"
64+
onClick={() => !textAreaDisabled && !isEnhancingPrompt && handleEnhancePrompt()}
65+
className={cn(
66+
"codicon codicon-sparkle",
67+
baseActionButton,
68+
"transition-all duration-300 z-[1] hover:brightness-120",
69+
textAreaDisabled && disabledButton,
70+
isEnhancingPrompt && [
71+
"!bg-transparent !opacity-100",
72+
"after:content-[''] after:absolute after:inset-[-2px]",
73+
"after:rounded-lg after:bg-gradient-to-r",
74+
"after:from-vscode-button-background",
75+
"after:via-vscode-textLink-foreground",
76+
"after:via-vscode-symbolIcon-classForeground",
77+
"after:to-vscode-button-background",
78+
"after:bg-[length:400%_400%] after:opacity-[0.08]",
79+
"after:z-0 after:animate-border-flow",
80+
],
81+
)}
82+
/>
83+
</div>
84+
<span
85+
role="button"
86+
aria-label="Add images to message"
87+
title="Add images to message"
88+
onClick={() => !shouldDisableImages && onSelectImages()}
89+
className={cn("codicon codicon-device-camera", baseActionButton, shouldDisableImages && disabledButton)}
90+
/>
91+
<span
92+
role="button"
93+
aria-label="Send message"
94+
title="Send message"
95+
onClick={() => !textAreaDisabled && onSend()}
96+
className={cn("codicon codicon-send", baseActionButton, textAreaDisabled && disabledButton)}
97+
/>
98+
</div>
99+
)
100+
}
101+
102+
export default ChatTextAreaActions

0 commit comments

Comments
 (0)