App.tsx 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. import { useCallback, useEffect, useRef, useState } from "react"
  2. import { useEvent } from "react-use"
  3. import { ExtensionMessage } from "../../src/shared/ExtensionMessage"
  4. import { vscode } from "./utils/vscode"
  5. import { ExtensionStateContextProvider, useExtensionState } from "./context/ExtensionStateContext"
  6. import ChatView from "./components/chat/ChatView"
  7. import HistoryView from "./components/history/HistoryView"
  8. import SettingsView, { SettingsViewRef } from "./components/settings/SettingsView"
  9. import WelcomeView from "./components/welcome/WelcomeView"
  10. import McpView from "./components/mcp/McpView"
  11. import PromptsView from "./components/prompts/PromptsView"
  12. type Tab = "settings" | "history" | "mcp" | "prompts" | "chat"
  13. const tabsByMessageAction: Partial<Record<NonNullable<ExtensionMessage["action"]>, Tab>> = {
  14. chatButtonClicked: "chat",
  15. settingsButtonClicked: "settings",
  16. promptsButtonClicked: "prompts",
  17. mcpButtonClicked: "mcp",
  18. historyButtonClicked: "history",
  19. }
  20. const App = () => {
  21. const { didHydrateState, showWelcome, shouldShowAnnouncement } = useExtensionState()
  22. const [showAnnouncement, setShowAnnouncement] = useState(false)
  23. const [tab, setTab] = useState<Tab>("chat")
  24. const settingsRef = useRef<SettingsViewRef>(null)
  25. const switchTab = useCallback((newTab: Tab) => {
  26. if (settingsRef.current?.checkUnsaveChanges) {
  27. settingsRef.current.checkUnsaveChanges(() => setTab(newTab))
  28. } else {
  29. setTab(newTab)
  30. }
  31. }, [])
  32. const onMessage = useCallback(
  33. (e: MessageEvent) => {
  34. const message: ExtensionMessage = e.data
  35. if (message.type === "action" && message.action) {
  36. const newTab = tabsByMessageAction[message.action]
  37. if (newTab) {
  38. switchTab(newTab)
  39. }
  40. }
  41. },
  42. [switchTab],
  43. )
  44. useEvent("message", onMessage)
  45. useEffect(() => {
  46. if (shouldShowAnnouncement) {
  47. setShowAnnouncement(true)
  48. vscode.postMessage({ type: "didShowAnnouncement" })
  49. }
  50. }, [shouldShowAnnouncement])
  51. if (!didHydrateState) {
  52. return null
  53. }
  54. // Do not conditionally load ChatView, it's expensive and there's state we
  55. // don't want to lose (user input, disableInput, askResponse promise, etc.)
  56. return showWelcome ? (
  57. <WelcomeView />
  58. ) : (
  59. <>
  60. {tab === "settings" && <SettingsView ref={settingsRef} onDone={() => setTab("chat")} />}
  61. {tab === "history" && <HistoryView onDone={() => switchTab("chat")} />}
  62. {tab === "mcp" && <McpView onDone={() => switchTab("chat")} />}
  63. {tab === "prompts" && <PromptsView onDone={() => switchTab("chat")} />}
  64. <ChatView
  65. isHidden={tab !== "chat"}
  66. showAnnouncement={showAnnouncement}
  67. hideAnnouncement={() => setShowAnnouncement(false)}
  68. showHistoryView={() => switchTab("history")}
  69. />
  70. </>
  71. )
  72. }
  73. const AppWithProviders = () => (
  74. <ExtensionStateContextProvider>
  75. <App />
  76. </ExtensionStateContextProvider>
  77. )
  78. export default AppWithProviders