mirror of
https://github.com/pacnpal/Roo-Code.git
synced 2025-12-20 12:21:13 -05:00
Focus textarea when webview becomes visible; add getApiMetrics
This commit is contained in:
@@ -41,6 +41,17 @@ export class SidebarProvider implements vscode.WebviewViewProvider {
|
||||
// and executes code based on the message that is recieved
|
||||
this.setWebviewMessageListener(webviewView.webview)
|
||||
|
||||
// Listen for when the panel becomes visible
|
||||
// https://github.com/microsoft/vscode-discussions/discussions/840
|
||||
webviewView.onDidChangeVisibility((e: any) => {
|
||||
if (e.visible) {
|
||||
// Your view is visible
|
||||
this.postMessageToWebview({ type: "action", action: "didBecomeVisible"})
|
||||
} else {
|
||||
// Your view is hidden
|
||||
}
|
||||
})
|
||||
|
||||
// if the extension is starting a new session, clear previous task state
|
||||
this.resetTask()
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
export interface ExtensionMessage {
|
||||
type: "action" | "state"
|
||||
text?: string
|
||||
action?: "plusButtonTapped" | "settingsButtonTapped"
|
||||
action?: "plusButtonTapped" | "settingsButtonTapped" | "didBecomeVisible"
|
||||
state?: { didOpenOnce: boolean, apiKey?: string, maxRequestsPerTask?: number, claudeMessages: ClaudeMessage[] }
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,8 @@ const App: React.FC = () => {
|
||||
|
||||
useEffect(() => {
|
||||
vscode.postMessage({ type: "webviewDidLaunch" })
|
||||
window.addEventListener("message", (e: MessageEvent) => {
|
||||
|
||||
const handleMessage = (e: MessageEvent) => {
|
||||
const message: ExtensionMessage = e.data
|
||||
// switch message.type
|
||||
switch (message.type) {
|
||||
@@ -50,7 +51,13 @@ const App: React.FC = () => {
|
||||
}
|
||||
break
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
window.addEventListener("message", handleMessage)
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("message", handleMessage)
|
||||
}
|
||||
}, [])
|
||||
|
||||
// dummy data for messages
|
||||
@@ -136,6 +143,78 @@ const App: React.FC = () => {
|
||||
ts: generateRandomTimestamp(baseDate, 29),
|
||||
},
|
||||
{ type: "say", say: "text", text: "Final message", ts: generateRandomTimestamp(baseDate, 30) },
|
||||
{
|
||||
type: "say",
|
||||
say: "text",
|
||||
text: "Starting API requests",
|
||||
ts: Date.now(),
|
||||
},
|
||||
{
|
||||
type: "say",
|
||||
say: "api_req_started",
|
||||
text: JSON.stringify({
|
||||
request: "GET /api/data1",
|
||||
}),
|
||||
ts: Date.now() + 1000,
|
||||
},
|
||||
{
|
||||
type: "say",
|
||||
say: "api_req_finished",
|
||||
text: JSON.stringify({
|
||||
tokensIn: 10,
|
||||
tokensOut: 20,
|
||||
cost: 0.002,
|
||||
}),
|
||||
ts: Date.now() + 2000,
|
||||
},
|
||||
{
|
||||
type: "say",
|
||||
say: "text",
|
||||
text: "Processing data...",
|
||||
ts: Date.now() + 3000,
|
||||
},
|
||||
{
|
||||
type: "say",
|
||||
say: "api_req_started",
|
||||
text: JSON.stringify({
|
||||
request: "POST /api/update1",
|
||||
}),
|
||||
ts: Date.now() + 4000,
|
||||
},
|
||||
{
|
||||
type: "say",
|
||||
say: "api_req_finished",
|
||||
text: JSON.stringify({
|
||||
tokensIn: 15,
|
||||
tokensOut: 25,
|
||||
cost: 0.003,
|
||||
}),
|
||||
ts: Date.now() + 5000,
|
||||
},
|
||||
{
|
||||
type: "say",
|
||||
say: "text",
|
||||
text: "More processing...",
|
||||
ts: Date.now() + 6000,
|
||||
},
|
||||
{
|
||||
type: "say",
|
||||
say: "api_req_started",
|
||||
text: JSON.stringify({
|
||||
request: "GET /api/data2",
|
||||
}),
|
||||
ts: Date.now() + 7000,
|
||||
},
|
||||
{
|
||||
type: "say",
|
||||
say: "api_req_finished",
|
||||
text: JSON.stringify({
|
||||
tokensIn: 5,
|
||||
tokensOut: 15,
|
||||
cost: 0.001,
|
||||
}),
|
||||
ts: Date.now() + 8000,
|
||||
},
|
||||
]
|
||||
|
||||
return (
|
||||
|
||||
@@ -8,6 +8,7 @@ import ChatRow from "./ChatRow"
|
||||
import { combineCommandSequences } from "../utilities/combineCommandSequences"
|
||||
import { combineApiRequests } from "../utilities/combineApiRequests"
|
||||
import TaskHeader from "./TaskHeader"
|
||||
import { getApiMetrics } from "../utilities/getApiMetrics"
|
||||
|
||||
interface ChatViewProps {
|
||||
messages: ClaudeMessage[]
|
||||
@@ -16,6 +17,8 @@ interface ChatViewProps {
|
||||
const ChatView = ({ messages }: ChatViewProps) => {
|
||||
const task = messages.shift()
|
||||
const modifiedMessages = useMemo(() => combineApiRequests(combineCommandSequences(messages)), [messages])
|
||||
// has to be after api_req_finished are all reduced into api_req_started messages
|
||||
const apiMetrics = useMemo(() => getApiMetrics(modifiedMessages), [modifiedMessages])
|
||||
|
||||
const [inputValue, setInputValue] = useState("")
|
||||
const messagesEndRef = useRef<HTMLDivElement>(null)
|
||||
@@ -110,7 +113,30 @@ const ChatView = ({ messages }: ChatViewProps) => {
|
||||
useEffect(() => {
|
||||
if (textAreaRef.current && !textAreaHeight) {
|
||||
setTextAreaHeight(textAreaRef.current.offsetHeight)
|
||||
textAreaRef.current.focus()
|
||||
//textAreaRef.current.focus()
|
||||
}
|
||||
|
||||
const handleMessage = (e: MessageEvent) => {
|
||||
const message: ExtensionMessage = e.data
|
||||
switch (message.type) {
|
||||
case "action":
|
||||
switch (message.action!) {
|
||||
case "didBecomeVisible":
|
||||
textAreaRef.current?.focus()
|
||||
break
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener("message", handleMessage)
|
||||
|
||||
const timer = setTimeout(() => {
|
||||
textAreaRef.current?.focus()
|
||||
}, 20)
|
||||
return () => {
|
||||
clearTimeout(timer)
|
||||
window.removeEventListener("message", handleMessage)
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [])
|
||||
@@ -129,9 +155,9 @@ const ChatView = ({ messages }: ChatViewProps) => {
|
||||
}}>
|
||||
<TaskHeader
|
||||
taskText={task?.text || ""}
|
||||
tokensIn={1000}
|
||||
tokensOut={1500}
|
||||
totalCost={0.0025}
|
||||
tokensIn={apiMetrics.totalTokensIn}
|
||||
tokensOut={apiMetrics.totalTokensOut}
|
||||
totalCost={apiMetrics.totalCost}
|
||||
/>
|
||||
<div
|
||||
className="scrollable"
|
||||
@@ -177,6 +203,7 @@ const ChatView = ({ messages }: ChatViewProps) => {
|
||||
onHeightChange={() => scrollToBottom(true)}
|
||||
placeholder="Type a message..."
|
||||
maxRows={10}
|
||||
autoFocus={true}
|
||||
style={{
|
||||
width: "100%",
|
||||
boxSizing: "border-box",
|
||||
|
||||
55
webview-ui/src/utilities/getApiMetrics.ts
Normal file
55
webview-ui/src/utilities/getApiMetrics.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { ClaudeMessage } from "@shared/ExtensionMessage"
|
||||
|
||||
interface ApiMetrics {
|
||||
totalTokensIn: number
|
||||
totalTokensOut: number
|
||||
totalCost: number
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates API metrics from an array of ClaudeMessages.
|
||||
*
|
||||
* This function processes 'api_req_started' messages that have been combined with their
|
||||
* corresponding 'api_req_finished' messages by the combineApiRequests function.
|
||||
* It extracts and sums up the tokensIn, tokensOut, and cost from these messages.
|
||||
*
|
||||
* @param messages - An array of ClaudeMessage objects to process.
|
||||
* @returns An ApiMetrics object containing totalTokensIn, totalTokensOut, and totalCost.
|
||||
*
|
||||
* @example
|
||||
* const messages = [
|
||||
* { type: "say", say: "api_req_started", text: '{"request":"GET /api/data","tokensIn":10,"tokensOut":20,"cost":0.005}', ts: 1000 }
|
||||
* ];
|
||||
* const { totalTokensIn, totalTokensOut, totalCost } = getApiMetrics(messages);
|
||||
* // Result: { totalTokensIn: 10, totalTokensOut: 20, totalCost: 0.005 }
|
||||
*/
|
||||
export function getApiMetrics(messages: ClaudeMessage[]): ApiMetrics {
|
||||
const result: ApiMetrics = {
|
||||
totalTokensIn: 0,
|
||||
totalTokensOut: 0,
|
||||
totalCost: 0,
|
||||
}
|
||||
|
||||
messages.forEach((message) => {
|
||||
if (message.type === "say" && message.say === "api_req_started" && message.text) {
|
||||
try {
|
||||
const parsedData = JSON.parse(message.text)
|
||||
const { tokensIn, tokensOut, cost } = parsedData
|
||||
|
||||
if (typeof tokensIn === "number") {
|
||||
result.totalTokensIn += tokensIn
|
||||
}
|
||||
if (typeof tokensOut === "number") {
|
||||
result.totalTokensOut += tokensOut
|
||||
}
|
||||
if (typeof cost === "number") {
|
||||
result.totalCost += cost
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error parsing JSON:", error)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
Reference in New Issue
Block a user