Roo-Code/webview-ui/src/App.tsx
2025-03-07 03:03:44 -05:00

153 lines
4.6 KiB
TypeScript

import { useCallback, useEffect, useRef, useState } from "react"
import { useEvent } from "react-use"
import { ExtensionMessage } from "../../src/shared/ExtensionMessage"
import { ShowHumanRelayDialogMessage } from "../../src/shared/ExtensionMessage"
import { vscode } from "./utils/vscode"
import { telemetryClient } from "./utils/TelemetryClient"
import { ExtensionStateContextProvider, useExtensionState } from "./context/ExtensionStateContext"
import ChatView from "./components/chat/ChatView"
import HistoryView from "./components/history/HistoryView"
import SettingsView, { SettingsViewRef } from "./components/settings/SettingsView"
import WelcomeView from "./components/welcome/WelcomeView"
import McpView from "./components/mcp/McpView"
import PromptsView from "./components/prompts/PromptsView"
import { HumanRelayDialog } from "./components/human-relay/HumanRelayDialog"
type Tab = "settings" | "history" | "mcp" | "prompts" | "chat"
const tabsByMessageAction: Partial<Record<NonNullable<ExtensionMessage["action"]>, Tab>> = {
chatButtonClicked: "chat",
settingsButtonClicked: "settings",
promptsButtonClicked: "prompts",
mcpButtonClicked: "mcp",
historyButtonClicked: "history",
}
const App = () => {
const { didHydrateState, showWelcome, shouldShowAnnouncement, telemetrySetting, telemetryKey, machineId } =
useExtensionState()
const [showAnnouncement, setShowAnnouncement] = useState(false)
const [tab, setTab] = useState<Tab>("chat")
const settingsRef = useRef<SettingsViewRef>(null)
// Human Relay Dialog Status
const [humanRelayDialogState, setHumanRelayDialogState] = useState<{
isOpen: boolean
requestId: string
promptText: string
}>({
isOpen: false,
requestId: "",
promptText: "",
})
const switchTab = useCallback((newTab: Tab) => {
if (settingsRef.current?.checkUnsaveChanges) {
settingsRef.current.checkUnsaveChanges(() => setTab(newTab))
} else {
setTab(newTab)
}
}, [])
const onMessage = useCallback(
(e: MessageEvent) => {
const message: ExtensionMessage = e.data
if (message.type === "action" && message.action) {
const newTab = tabsByMessageAction[message.action]
if (newTab) {
switchTab(newTab)
}
}
const mes: ShowHumanRelayDialogMessage = message as ShowHumanRelayDialogMessage
// Processing displays human relay dialog messages
if (mes.type === "showHumanRelayDialog" && mes.requestId && mes.promptText) {
setHumanRelayDialogState({
isOpen: true,
requestId: mes.requestId,
promptText: mes.promptText,
})
}
},
[switchTab],
)
// Processing Human Relay Dialog Submission
const handleHumanRelaySubmit = (requestId: string, text: string) => {
vscode.postMessage({
type: "humanRelayResponse",
requestId,
text,
})
}
// Handle Human Relay dialog box cancel
const handleHumanRelayCancel = (requestId: string) => {
vscode.postMessage({
type: "humanRelayCancel",
requestId,
})
}
useEvent("message", onMessage)
useEffect(() => {
if (shouldShowAnnouncement) {
setShowAnnouncement(true)
vscode.postMessage({ type: "didShowAnnouncement" })
}
}, [shouldShowAnnouncement])
useEffect(() => {
if (didHydrateState) {
telemetryClient.updateTelemetryState(telemetrySetting, telemetryKey, machineId)
}
}, [telemetrySetting, telemetryKey, machineId, didHydrateState])
// Tell Extension that we are ready to receive messages
useEffect(() => {
vscode.postMessage({ type: "webviewDidLaunch" })
}, [])
if (!didHydrateState) {
return null
}
// Do not conditionally load ChatView, it's expensive and there's state we
// don't want to lose (user input, disableInput, askResponse promise, etc.)
return showWelcome ? (
<WelcomeView />
) : (
<>
{tab === "settings" && <SettingsView ref={settingsRef} onDone={() => setTab("chat")} />}
{tab === "history" && <HistoryView onDone={() => switchTab("chat")} />}
{tab === "mcp" && <McpView onDone={() => switchTab("chat")} />}
{tab === "prompts" && <PromptsView onDone={() => switchTab("chat")} />}
<ChatView
isHidden={tab !== "chat"}
showAnnouncement={showAnnouncement}
hideAnnouncement={() => setShowAnnouncement(false)}
showHistoryView={() => switchTab("history")}
/>
{/* Human Relay Dialog */}
<HumanRelayDialog
isOpen={humanRelayDialogState.isOpen}
requestId={humanRelayDialogState.requestId}
promptText={humanRelayDialogState.promptText}
onClose={() => setHumanRelayDialogState((prev) => ({ ...prev, isOpen: false }))}
onSubmit={handleHumanRelaySubmit}
onCancel={handleHumanRelayCancel}
/>
</>
)
}
const AppWithProviders = () => (
<ExtensionStateContextProvider>
<App />
</ExtensionStateContextProvider>
)
export default AppWithProviders