Merge main

This commit is contained in:
cte
2025-01-30 21:15:34 -08:00
parent 4aff01240b
commit 7282e02de3
12 changed files with 172 additions and 201 deletions

View File

@@ -1,40 +0,0 @@
{
"root": true,
"extends": [
"eslint:recommended",
"plugin:react/recommended",
"plugin:@typescript-eslint/recommended",
"plugin:react-hooks/recommended"
],
"parser": "@typescript-eslint/parser",
"plugins": ["react", "@typescript-eslint", "react-hooks"],
"rules": {
"react/react-in-jsx-scope": "off",
"@typescript-eslint/explicit-module-boundary-types": "off",
"@typescript-eslint/no-explicit-any": "warn",
"react/display-name": "warn",
"no-case-declarations": "warn",
"react/no-unescaped-entities": "warn",
"react/jsx-key": "warn",
"no-extra-semi": "warn",
"@typescript-eslint/no-var-requires": "warn",
"@typescript-eslint/no-unused-vars": [
"warn",
{
"argsIgnorePattern": "^_",
"varsIgnorePattern": "^_",
"caughtErrorsIgnorePattern": "^_"
}
]
},
"settings": {
"react": {
"version": "detect"
}
},
"env": {
"browser": true,
"es2021": true,
"node": true
}
}

View File

@@ -89,7 +89,7 @@ export const ChatRowContent = ({
}
}, [isLast, message.say])
const [cost, apiReqCancelReason, apiReqStreamingFailedMessage] = useMemo(() => {
if (message.text != null && message.say === "api_req_started") {
if (message.text && message.say === "api_req_started") {
const info: ClineApiReqInfo = JSON.parse(message.text)
return [info.cost, info.cancelReason, info.streamingFailedMessage]
}
@@ -183,26 +183,26 @@ export const ChatRowContent = ({
</div>
)
return [
apiReqCancelReason != null ? (
apiReqCancelReason !== null ? (
apiReqCancelReason === "user_cancelled" ? (
getIconSpan("error", cancelledColor)
) : (
getIconSpan("error", errorColor)
)
) : cost != null ? (
) : cost !== null ? (
getIconSpan("check", successColor)
) : apiRequestFailedMessage ? (
getIconSpan("error", errorColor)
) : (
<ProgressIndicator />
),
apiReqCancelReason != null ? (
apiReqCancelReason !== null ? (
apiReqCancelReason === "user_cancelled" ? (
<span style={{ color: normalColor, fontWeight: "bold" }}>API Request Cancelled</span>
) : (
<span style={{ color: errorColor, fontWeight: "bold" }}>API Streaming Failed</span>
)
) : cost != null ? (
) : cost !== null ? (
<span style={{ color: normalColor, fontWeight: "bold" }}>API Request</span>
) : apiRequestFailedMessage ? (
<span style={{ color: errorColor, fontWeight: "bold" }}>API Request Failed</span>
@@ -510,7 +510,7 @@ export const ChatRowContent = ({
style={{
...headerStyle,
marginBottom:
(cost == null && apiRequestFailedMessage) || apiReqStreamingFailedMessage
(cost === null && apiRequestFailedMessage) || apiReqStreamingFailedMessage
? 10
: 0,
justifyContent: "space-between",
@@ -524,13 +524,13 @@ export const ChatRowContent = ({
<div style={{ display: "flex", alignItems: "center", gap: "10px", flexGrow: 1 }}>
{icon}
{title}
<VSCodeBadge style={{ opacity: cost != null && cost > 0 ? 1 : 0 }}>
<VSCodeBadge style={{ opacity: cost ? 1 : 0 }}>
${Number(cost || 0)?.toFixed(4)}
</VSCodeBadge>
</div>
<span className={`codicon codicon-chevron-${isExpanded ? "up" : "down"}`}></span>
</div>
{((cost == null && apiRequestFailedMessage) || apiReqStreamingFailedMessage) && (
{((cost === null && apiRequestFailedMessage) || apiReqStreamingFailedMessage) && (
<>
<p style={{ ...pStyle, color: "var(--vscode-errorForeground)" }}>
{apiRequestFailedMessage || apiReqStreamingFailedMessage}

View File

@@ -275,7 +275,7 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie
return true
} else {
const lastApiReqStarted = findLast(modifiedMessages, (message) => message.say === "api_req_started")
if (lastApiReqStarted && lastApiReqStarted.text != null && lastApiReqStarted.say === "api_req_started") {
if (lastApiReqStarted && lastApiReqStarted.text && lastApiReqStarted.say === "api_req_started") {
const cost = JSON.parse(lastApiReqStarted.text).cost
if (cost === undefined) {
// api request has not finished yet
@@ -337,56 +337,96 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie
/*
This logic depends on the useEffect[messages] above to set clineAsk, after which buttons are shown and we then send an askResponse to the extension.
*/
const handlePrimaryButtonClick = useCallback(() => {
switch (clineAsk) {
case "api_req_failed":
case "command":
case "command_output":
case "tool":
case "browser_action_launch":
case "use_mcp_server":
case "resume_task":
case "mistake_limit_reached":
vscode.postMessage({ type: "askResponse", askResponse: "yesButtonClicked" })
break
case "completion_result":
case "resume_completed_task":
// extension waiting for feedback. but we can just present a new task button
startNewTask()
break
}
setTextAreaDisabled(true)
setClineAsk(undefined)
setEnableButtons(false)
disableAutoScrollRef.current = false
}, [clineAsk, startNewTask])
const handlePrimaryButtonClick = useCallback(
(text?: string, images?: string[]) => {
const trimmedInput = text?.trim()
switch (clineAsk) {
case "api_req_failed":
case "command":
case "command_output":
case "tool":
case "browser_action_launch":
case "use_mcp_server":
case "resume_task":
case "mistake_limit_reached":
// Only send text/images if they exist
if (trimmedInput || (images && images.length > 0)) {
vscode.postMessage({
type: "askResponse",
askResponse: "yesButtonClicked",
text: trimmedInput,
images: images,
})
} else {
vscode.postMessage({
type: "askResponse",
askResponse: "yesButtonClicked",
})
}
// Clear input state after sending
setInputValue("")
setSelectedImages([])
break
case "completion_result":
case "resume_completed_task":
// extension waiting for feedback. but we can just present a new task button
startNewTask()
break
}
setTextAreaDisabled(true)
setClineAsk(undefined)
setEnableButtons(false)
disableAutoScrollRef.current = false
},
[clineAsk, startNewTask],
)
const handleSecondaryButtonClick = useCallback(() => {
if (isStreaming) {
vscode.postMessage({ type: "cancelTask" })
setDidClickCancel(true)
return
}
const handleSecondaryButtonClick = useCallback(
(text?: string, images?: string[]) => {
const trimmedInput = text?.trim()
if (isStreaming) {
vscode.postMessage({ type: "cancelTask" })
setDidClickCancel(true)
return
}
switch (clineAsk) {
case "api_req_failed":
case "mistake_limit_reached":
case "resume_task":
startNewTask()
break
case "command":
case "tool":
case "browser_action_launch":
case "use_mcp_server":
// responds to the API with a "This operation failed" and lets it try again
vscode.postMessage({ type: "askResponse", askResponse: "noButtonClicked" })
break
}
setTextAreaDisabled(true)
setClineAsk(undefined)
setEnableButtons(false)
disableAutoScrollRef.current = false
}, [clineAsk, startNewTask, isStreaming])
switch (clineAsk) {
case "api_req_failed":
case "mistake_limit_reached":
case "resume_task":
startNewTask()
break
case "command":
case "tool":
case "browser_action_launch":
case "use_mcp_server":
// Only send text/images if they exist
if (trimmedInput || (images && images.length > 0)) {
vscode.postMessage({
type: "askResponse",
askResponse: "noButtonClicked",
text: trimmedInput,
images: images,
})
} else {
// responds to the API with a "This operation failed" and lets it try again
vscode.postMessage({
type: "askResponse",
askResponse: "noButtonClicked",
})
}
// Clear input state after sending
setInputValue("")
setSelectedImages([])
break
}
setTextAreaDisabled(true)
setClineAsk(undefined)
setEnableButtons(false)
disableAutoScrollRef.current = false
},
[clineAsk, startNewTask, isStreaming],
)
const handleTaskCloseButtonClick = useCallback(() => {
startNewTask()
@@ -430,10 +470,10 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie
handleSendMessage(message.text ?? "", message.images ?? [])
break
case "primaryButtonClick":
handlePrimaryButtonClick()
handlePrimaryButtonClick(message.text ?? "", message.images ?? [])
break
case "secondaryButtonClick":
handleSecondaryButtonClick()
handleSecondaryButtonClick(message.text ?? "", message.images ?? [])
break
}
}
@@ -660,9 +700,9 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie
if (message.say === "api_req_started") {
// get last api_req_started in currentGroup to check if it's cancelled. If it is then this api req is not part of the current browser session
const lastApiReqStarted = [...currentGroup].reverse().find((m) => m.say === "api_req_started")
if (lastApiReqStarted?.text != null) {
if (lastApiReqStarted?.text) {
const info = JSON.parse(lastApiReqStarted.text)
const isCancelled = info.cancelReason != null
const isCancelled = info.cancelReason !== null
if (isCancelled) {
endBrowserSession()
result.push(message)
@@ -1038,7 +1078,7 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie
flex: secondaryButtonText ? 1 : 2,
marginRight: secondaryButtonText ? "6px" : "0",
}}
onClick={handlePrimaryButtonClick}>
onClick={(e) => handlePrimaryButtonClick(inputValue, selectedImages)}>
{primaryButtonText}
</VSCodeButton>
)}
@@ -1050,7 +1090,7 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie
flex: isStreaming ? 2 : 1,
marginLeft: isStreaming ? 0 : "6px",
}}
onClick={handleSecondaryButtonClick}>
onClick={(e) => handleSecondaryButtonClick(inputValue, selectedImages)}>
{isStreaming ? "Cancel" : secondaryButtonText}
</VSCodeButton>
)}

View File

@@ -10,7 +10,7 @@ const WelcomeView = () => {
const [apiErrorMessage, setApiErrorMessage] = useState<string | undefined>(undefined)
const disableLetsGoButton = apiErrorMessage != null
const disableLetsGoButton = !!apiErrorMessage
const handleSubmit = () => {
vscode.postMessage({ type: "apiConfiguration", apiConfiguration })