From 3b5e018a60ae1b8cf7003c9f902944c8850ba471 Mon Sep 17 00:00:00 2001 From: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com> Date: Tue, 27 Aug 2024 04:44:36 -0400 Subject: [PATCH] Refactor extension state into ExtensionStateContext --- webview-ui/src/App.css | 38 ------- webview-ui/src/App.test.tsx | 9 -- webview-ui/src/App.tsx | 98 +++---------------- webview-ui/src/components/ApiOptions.tsx | 24 ++--- webview-ui/src/components/ChatRow.tsx | 2 +- webview-ui/src/components/ChatView.tsx | 46 ++++----- .../components/{CodeBlock => }/CodeBlock.tsx | 4 +- webview-ui/src/components/HistoryPreview.tsx | 6 +- webview-ui/src/components/HistoryView.tsx | 6 +- webview-ui/src/components/KoduPromo.tsx | 8 +- webview-ui/src/components/SettingsView.tsx | 63 +++++------- webview-ui/src/components/WelcomeView.tsx | 26 ++--- .../src/context/ExtensionStateContext.tsx | 71 ++++++++++++++ webview-ui/src/index.css | 12 --- 14 files changed, 155 insertions(+), 258 deletions(-) delete mode 100644 webview-ui/src/App.css delete mode 100644 webview-ui/src/App.test.tsx rename webview-ui/src/components/{CodeBlock => }/CodeBlock.tsx (97%) create mode 100644 webview-ui/src/context/ExtensionStateContext.tsx diff --git a/webview-ui/src/App.css b/webview-ui/src/App.css deleted file mode 100644 index 83b7c5e..0000000 --- a/webview-ui/src/App.css +++ /dev/null @@ -1,38 +0,0 @@ -.App { - text-align: center; -} - -.App-logo { - height: 40vmin; - pointer-events: none; -} - -@media (prefers-reduced-motion: no-preference) { - .App-logo { - animation: App-logo-spin infinite 20s linear; - } -} - -.App-header { - background-color: #282c34; - min-height: 100vh; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - font-size: calc(10px + 2vmin); - color: white; -} - -.App-link { - color: #61dafb; -} - -@keyframes App-logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} diff --git a/webview-ui/src/App.test.tsx b/webview-ui/src/App.test.tsx deleted file mode 100644 index 6354378..0000000 --- a/webview-ui/src/App.test.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import React from "react" -import { render, screen } from "@testing-library/react" -import App from "./App" - -test("renders learn react link", () => { - render() - const linkElement = screen.getByText(/learn react/i) - expect(linkElement).toBeInTheDocument() -}) diff --git a/webview-ui/src/App.tsx b/webview-ui/src/App.tsx index 954bc45..31d8af3 100644 --- a/webview-ui/src/App.tsx +++ b/webview-ui/src/App.tsx @@ -1,51 +1,27 @@ -import React, { useCallback, useEffect, useMemo, useState } from "react" +import { useCallback, useMemo, useState } from "react" import { useEvent } from "react-use" -import { ApiConfiguration } from "../../src/shared/api" -import { ClaudeMessage, ExtensionMessage } from "../../src/shared/ExtensionMessage" -import { HistoryItem } from "../../src/shared/HistoryItem" -import "./App.css" +import { ExtensionMessage } from "../../src/shared/ExtensionMessage" import { normalizeApiConfiguration } from "./components/ApiOptions" import ChatView from "./components/ChatView" import HistoryView from "./components/HistoryView" import SettingsView from "./components/SettingsView" import WelcomeView from "./components/WelcomeView" +import { ExtensionStateContextProvider, useExtensionState } from "./context/ExtensionStateContext" import { vscode } from "./utils/vscode" -/* -The contents of webviews however are created when the webview becomes visible and destroyed when the webview is moved into the background. Any state inside the webview will be lost when the webview is moved to a background tab. - -The best way to solve this is to make your webview stateless. Use message passing to save off the webview's state and then restore the state when the webview becomes visible again. -*/ - -const App: React.FC = () => { - const [didHydrateState, setDidHydrateState] = useState(false) +const AppContent = () => { + const { apiConfiguration } = useExtensionState() const [showSettings, setShowSettings] = useState(false) const [showHistory, setShowHistory] = useState(false) const [showWelcome, setShowWelcome] = useState(false) - const [version, setVersion] = useState("") - const [apiConfiguration, setApiConfiguration] = useState(undefined) - const [maxRequestsPerTask, setMaxRequestsPerTask] = useState("") - const [customInstructions, setCustomInstructions] = useState("") - const [alwaysAllowReadOnly, setAlwaysAllowReadOnly] = useState(false) - const [vscodeThemeName, setVscodeThemeName] = useState(undefined) - const [vscodeUriScheme, setVscodeUriScheme] = useState(undefined) - const [claudeMessages, setClaudeMessages] = useState([]) - const [taskHistory, setTaskHistory] = useState([]) const [showAnnouncement, setShowAnnouncement] = useState(false) - const [koduCredits, setKoduCredits] = useState(undefined) - const [shouldShowKoduPromo, setShouldShowKoduPromo] = useState(true) const [didAuthKoduFromWelcome, setDidAuthKoduFromWelcome] = useState(false) - useEffect(() => { - vscode.postMessage({ type: "webviewDidLaunch" }) - }, []) - const handleMessage = useCallback( (e: MessageEvent) => { const message: ExtensionMessage = e.data switch (message.type) { case "state": - setVersion(message.state!.version) const hasKey = message.state!.apiConfiguration?.apiKey !== undefined || message.state!.apiConfiguration?.openRouterApiKey !== undefined || @@ -55,25 +31,10 @@ const App: React.FC = () => { if (!hasKey) { setDidAuthKoduFromWelcome(false) } - setApiConfiguration(message.state!.apiConfiguration) - setMaxRequestsPerTask( - message.state!.maxRequestsPerTask !== undefined - ? message.state!.maxRequestsPerTask.toString() - : "" - ) - setCustomInstructions(message.state!.customInstructions || "") - setAlwaysAllowReadOnly(message.state!.alwaysAllowReadOnly || false) - setVscodeThemeName(message.state!.themeName) - setVscodeUriScheme(message.state!.uriScheme) - setClaudeMessages(message.state!.claudeMessages) - setTaskHistory(message.state!.taskHistory) - setKoduCredits(message.state!.koduCredits) // don't update showAnnouncement to false if shouldShowAnnouncement is false if (message.state!.shouldShowAnnouncement) { setShowAnnouncement(true) } - setShouldShowKoduPromo(message.state!.shouldShowKoduPromo) - setDidHydrateState(true) break case "action": switch (message.action!) { @@ -89,9 +50,6 @@ const App: React.FC = () => { setShowSettings(false) setShowHistory(false) break - case "koduCreditsFetched": - setKoduCredits(message.state!.koduCredits) - break case "koduAuthenticated": if (!didAuthKoduFromWelcome) { setShowSettings(true) @@ -112,49 +70,21 @@ const App: React.FC = () => { return normalizeApiConfiguration(apiConfiguration) }, [apiConfiguration]) - if (!didHydrateState) { - return null - } - return ( <> {showWelcome ? ( - + ) : ( <> - {showSettings && ( - setShowSettings(false)} - vscodeUriScheme={vscodeUriScheme} - /> - )} - {showHistory && setShowHistory(false)} />} + {showSettings && setShowSettings(false)} />} + {showHistory && setShowHistory(false)} />} {/* Do not conditionally load ChatView, it's expensive and there's state we don't want to lose (user input, disableInput, askResponse promise, etc.) */} { setShowSettings(false) setShowHistory(true) }} isHidden={showSettings || showHistory} - vscodeThemeName={vscodeThemeName} showAnnouncement={showAnnouncement} selectedModelSupportsImages={selectedModelInfo.supportsImages} selectedModelSupportsPromptCache={selectedModelInfo.supportsPromptCache} @@ -162,10 +92,6 @@ const App: React.FC = () => { vscode.postMessage({ type: "didCloseAnnouncement" }) setShowAnnouncement(false) }} - apiConfiguration={apiConfiguration} - vscodeUriScheme={vscodeUriScheme} - shouldShowKoduPromo={shouldShowKoduPromo} - koduCredits={koduCredits} /> )} @@ -173,4 +99,12 @@ const App: React.FC = () => { ) } +const App = () => { + return ( + + + + ) +} + export default App diff --git a/webview-ui/src/components/ApiOptions.tsx b/webview-ui/src/components/ApiOptions.tsx index 5bb3063..c54517d 100644 --- a/webview-ui/src/components/ApiOptions.tsx +++ b/webview-ui/src/components/ApiOptions.tsx @@ -18,29 +18,19 @@ import { ExtensionMessage } from "../../../src/shared/ExtensionMessage" import { getKoduAddCreditsUrl, getKoduHomepageUrl, getKoduSignInUrl } from "../../../src/shared/kodu" import { vscode } from "../utils/vscode" import VSCodeButtonLink from "./VSCodeButtonLink" +import { useExtensionState } from "../context/ExtensionStateContext" interface ApiOptionsProps { showModelOptions: boolean - apiConfiguration?: ApiConfiguration - setApiConfiguration: React.Dispatch> - koduCredits?: number apiErrorMessage?: string - vscodeUriScheme?: string setDidAuthKodu?: React.Dispatch> } -const ApiOptions: React.FC = ({ - showModelOptions, - apiConfiguration, - setApiConfiguration, - koduCredits, - apiErrorMessage, - vscodeUriScheme, - setDidAuthKodu, -}) => { +const ApiOptions: React.FC = ({ showModelOptions, apiErrorMessage, setDidAuthKodu }) => { + const { apiConfiguration, setApiConfiguration, koduCredits, uriScheme } = useExtensionState() const [, setDidFetchKoduCredits] = useState(false) const handleInputChange = (field: keyof ApiConfiguration) => (event: any) => { - setApiConfiguration((prev) => ({ ...prev, [field]: event.target.value })) + setApiConfiguration({ ...apiConfiguration, [field]: event.target.value }) } const { selectedProvider, selectedModelId, selectedModelInfo } = useMemo(() => { @@ -185,7 +175,7 @@ const ApiOptions: React.FC = ({ @@ -194,9 +184,7 @@ const ApiOptions: React.FC = ({ ) : (
- setDidAuthKodu?.(true)}> + setDidAuthKodu?.(true)}> Sign in to Kodu
diff --git a/webview-ui/src/components/ChatRow.tsx b/webview-ui/src/components/ChatRow.tsx index 2ce39d0..aeb09e9 100644 --- a/webview-ui/src/components/ChatRow.tsx +++ b/webview-ui/src/components/ChatRow.tsx @@ -5,7 +5,7 @@ import { Prism as SyntaxHighlighter } from "react-syntax-highlighter" import { ClaudeAsk, ClaudeMessage, ClaudeSay, ClaudeSayTool } from "../../../src/shared/ExtensionMessage" import { COMMAND_OUTPUT_STRING } from "../../../src/shared/combineCommandSequences" import { SyntaxHighlighterStyle } from "../utils/getSyntaxHighlighterStyleFromTheme" -import CodeBlock from "./CodeBlock/CodeBlock" +import CodeBlock from "./CodeBlock" import Thumbnails from "./Thumbnails" import { ApiProvider } from "../../../src/shared/api" diff --git a/webview-ui/src/components/ChatView.tsx b/webview-ui/src/components/ChatView.tsx index cc7c2b6..c8803f0 100644 --- a/webview-ui/src/components/ChatView.tsx +++ b/webview-ui/src/components/ChatView.tsx @@ -4,56 +4,50 @@ import vsDarkPlus from "react-syntax-highlighter/dist/esm/styles/prism/vsc-dark- import DynamicTextArea from "react-textarea-autosize" import { useEvent, useMount } from "react-use" import { Virtuoso, type VirtuosoHandle } from "react-virtuoso" -import { ClaudeAsk, ClaudeMessage, ExtensionMessage } from "../../../src/shared/ExtensionMessage" -import { getApiMetrics } from "../../../src/shared/getApiMetrics" +import { ClaudeAsk, ExtensionMessage } from "../../../src/shared/ExtensionMessage" import { combineApiRequests } from "../../../src/shared/combineApiRequests" import { combineCommandSequences } from "../../../src/shared/combineCommandSequences" +import { getApiMetrics } from "../../../src/shared/getApiMetrics" +import { useExtensionState } from "../context/ExtensionStateContext" import { getSyntaxHighlighterStyleFromTheme } from "../utils/getSyntaxHighlighterStyleFromTheme" import { vscode } from "../utils/vscode" import Announcement from "./Announcement" import ChatRow from "./ChatRow" import HistoryPreview from "./HistoryPreview" +import KoduPromo from "./KoduPromo" import TaskHeader from "./TaskHeader" import Thumbnails from "./Thumbnails" -import { HistoryItem } from "../../../src/shared/HistoryItem" -import { ApiConfiguration } from "../../../src/shared/api" -import KoduPromo from "./KoduPromo" interface ChatViewProps { - version: string - messages: ClaudeMessage[] - taskHistory: HistoryItem[] isHidden: boolean - vscodeThemeName?: string showAnnouncement: boolean selectedModelSupportsImages: boolean selectedModelSupportsPromptCache: boolean hideAnnouncement: () => void showHistoryView: () => void - apiConfiguration?: ApiConfiguration - vscodeUriScheme?: string - shouldShowKoduPromo: boolean - koduCredits?: number } const MAX_IMAGES_PER_MESSAGE = 20 // Anthropic limits to 20 images const ChatView = ({ - version, - messages, - taskHistory, isHidden, - vscodeThemeName, showAnnouncement, selectedModelSupportsImages, selectedModelSupportsPromptCache, hideAnnouncement, showHistoryView, - apiConfiguration, - vscodeUriScheme, - shouldShowKoduPromo, - koduCredits, }: ChatViewProps) => { + const { + version, + claudeMessages: messages, + taskHistory, + themeName: vscodeThemeName, + apiConfiguration, + uriScheme, + shouldShowKoduPromo, + koduCredits, + } = useExtensionState() + //const task = messages.length > 0 ? (messages[0].say === "task" ? messages[0] : undefined) : undefined) : undefined const task = messages.length > 0 ? messages[0] : undefined // leaving this less safe version here since if the first message is not a task, then the extension is in a bad state and needs to be debugged (see ClaudeDev.abort) const modifiedMessages = useMemo(() => combineApiRequests(combineCommandSequences(messages.slice(1))), [messages]) @@ -490,7 +484,7 @@ const ChatView = ({ onClose={handleTaskCloseButtonClick} isHidden={isHidden} koduCredits={koduCredits} - vscodeUriScheme={vscodeUriScheme} + vscodeUriScheme={uriScheme} apiProvider={apiConfiguration?.apiProvider} /> ) : ( @@ -500,11 +494,11 @@ const ChatView = ({ version={version} hideAnnouncement={hideAnnouncement} apiConfiguration={apiConfiguration} - vscodeUriScheme={vscodeUriScheme} + vscodeUriScheme={uriScheme} /> )} {apiConfiguration?.koduApiKey === undefined && !showAnnouncement && shouldShowKoduPromo && ( - + )}
0 ? undefined : 1 }}>

What can I do for you?

@@ -520,9 +514,7 @@ const ChatView = ({ permission), I can assist you in ways that go beyond simple code completion or tech support.

- {taskHistory.length > 0 && ( - - )} + {taskHistory.length > 0 && } )} {task && ( diff --git a/webview-ui/src/components/CodeBlock/CodeBlock.tsx b/webview-ui/src/components/CodeBlock.tsx similarity index 97% rename from webview-ui/src/components/CodeBlock/CodeBlock.tsx rename to webview-ui/src/components/CodeBlock.tsx index a008f0b..6a2f8ef 100644 --- a/webview-ui/src/components/CodeBlock/CodeBlock.tsx +++ b/webview-ui/src/components/CodeBlock.tsx @@ -1,7 +1,7 @@ import { useMemo } from "react" import { Prism as SyntaxHighlighter } from "react-syntax-highlighter" -import { getLanguageFromPath } from "../../utils/getLanguageFromPath" -import { SyntaxHighlighterStyle } from "../../utils/getSyntaxHighlighterStyleFromTheme" +import { getLanguageFromPath } from "../utils/getLanguageFromPath" +import { SyntaxHighlighterStyle } from "../utils/getSyntaxHighlighterStyleFromTheme" /* const vscodeSyntaxStyle: React.CSSProperties = { diff --git a/webview-ui/src/components/HistoryPreview.tsx b/webview-ui/src/components/HistoryPreview.tsx index ee4cb51..3705eb8 100644 --- a/webview-ui/src/components/HistoryPreview.tsx +++ b/webview-ui/src/components/HistoryPreview.tsx @@ -1,13 +1,13 @@ import { VSCodeButton } from "@vscode/webview-ui-toolkit/react" +import { useExtensionState } from "../context/ExtensionStateContext" import { vscode } from "../utils/vscode" -import { HistoryItem } from "../../../src/shared/HistoryItem" type HistoryPreviewProps = { - taskHistory: HistoryItem[] showHistoryView: () => void } -const HistoryPreview = ({ taskHistory, showHistoryView }: HistoryPreviewProps) => { +const HistoryPreview = ({ showHistoryView }: HistoryPreviewProps) => { + const { taskHistory } = useExtensionState() const handleHistorySelect = (id: string) => { vscode.postMessage({ type: "showTaskWithId", text: id }) } diff --git a/webview-ui/src/components/HistoryView.tsx b/webview-ui/src/components/HistoryView.tsx index 8265dac..57ce3b6 100644 --- a/webview-ui/src/components/HistoryView.tsx +++ b/webview-ui/src/components/HistoryView.tsx @@ -1,13 +1,13 @@ import { VSCodeButton } from "@vscode/webview-ui-toolkit/react" +import { useExtensionState } from "../context/ExtensionStateContext" import { vscode } from "../utils/vscode" -import { HistoryItem } from "../../../src/shared/HistoryItem" type HistoryViewProps = { - taskHistory: HistoryItem[] onDone: () => void } -const HistoryView = ({ taskHistory, onDone }: HistoryViewProps) => { +const HistoryView = ({ onDone }: HistoryViewProps) => { + const { taskHistory } = useExtensionState() const handleHistorySelect = (id: string) => { vscode.postMessage({ type: "showTaskWithId", text: id }) } diff --git a/webview-ui/src/components/KoduPromo.tsx b/webview-ui/src/components/KoduPromo.tsx index 8b38dde..81141ca 100644 --- a/webview-ui/src/components/KoduPromo.tsx +++ b/webview-ui/src/components/KoduPromo.tsx @@ -1,13 +1,15 @@ import React from "react" import { getKoduSignInUrl } from "../../../src/shared/kodu" import { vscode } from "../utils/vscode" +import { useExtensionState } from "../context/ExtensionStateContext" interface KoduPromoProps { - vscodeUriScheme?: string style?: React.CSSProperties } -const KoduPromo: React.FC = ({ vscodeUriScheme, style }) => { +const KoduPromo: React.FC = ({ style }) => { + const { uriScheme } = useExtensionState() + function onClose() { vscode.postMessage({ type: "didDismissKoduPromo" }) } @@ -28,7 +30,7 @@ const KoduPromo: React.FC = ({ vscodeUriScheme, style }) => { cursor: "pointer", }}> > - koduCredits?: number - maxRequestsPerTask: string - setMaxRequestsPerTask: React.Dispatch> - customInstructions: string - setCustomInstructions: React.Dispatch> onDone: () => void - alwaysAllowReadOnly: boolean - setAlwaysAllowReadOnly: React.Dispatch> - vscodeUriScheme?: string } -const SettingsView = ({ - version, - apiConfiguration, - setApiConfiguration, - koduCredits, - maxRequestsPerTask, - setMaxRequestsPerTask, - customInstructions, - setCustomInstructions, - onDone, - alwaysAllowReadOnly, - setAlwaysAllowReadOnly, - vscodeUriScheme, -}: SettingsViewProps) => { +const SettingsView = ({ onDone }: SettingsViewProps) => { + const { + apiConfiguration, + version, + maxRequestsPerTask, + customInstructions, + setCustomInstructions, + alwaysAllowReadOnly, + setAlwaysAllowReadOnly, + } = useExtensionState() const [apiErrorMessage, setApiErrorMessage] = useState(undefined) const [maxRequestsErrorMessage, setMaxRequestsErrorMessage] = useState(undefined) + const [maxRequestsPerTaskString, setMaxRequestsPerTaskString] = useState( + maxRequestsPerTask?.toString() || "" + ) const handleSubmit = () => { const apiValidationResult = validateApiConfiguration(apiConfiguration) - const maxRequestsValidationResult = validateMaxRequestsPerTask(maxRequestsPerTask) + const maxRequestsValidationResult = validateMaxRequestsPerTask(maxRequestsPerTaskString) setApiErrorMessage(apiValidationResult) setMaxRequestsErrorMessage(maxRequestsValidationResult) if (!apiValidationResult && !maxRequestsValidationResult) { vscode.postMessage({ type: "apiConfiguration", apiConfiguration }) - vscode.postMessage({ type: "maxRequestsPerTask", text: maxRequestsPerTask }) + vscode.postMessage({ type: "maxRequestsPerTask", text: maxRequestsPerTaskString }) vscode.postMessage({ type: "customInstructions", text: customInstructions }) vscode.postMessage({ type: "alwaysAllowReadOnly", bool: alwaysAllowReadOnly }) onDone() @@ -112,14 +100,7 @@ const SettingsView = ({
- +
@@ -141,13 +122,13 @@ const SettingsView = ({
setCustomInstructions(e.target?.value || "")}> + onInput={(e: any) => setCustomInstructions(e.target?.value ?? "")}> Custom Instructions

setMaxRequestsPerTask(e.target?.value)}> + onInput={(e: any) => setMaxRequestsPerTaskString(e.target?.value ?? "")}> Maximum # Requests Per Task

> - vscodeUriScheme?: string setDidAuthKoduFromWelcome: React.Dispatch> } -const WelcomeView: React.FC = ({ - apiConfiguration, - setApiConfiguration, - vscodeUriScheme, - setDidAuthKoduFromWelcome, -}) => { +const WelcomeView: React.FC = ({ setDidAuthKoduFromWelcome }) => { + const { apiConfiguration, uriScheme } = useExtensionState() + const [apiErrorMessage, setApiErrorMessage] = useState(undefined) const disableLetsGoButton = apiErrorMessage != null @@ -67,20 +61,14 @@ const WelcomeView: React.FC = ({ }}> Explore Claude's capabilities with $20 free credits from{" "} - + Kodu

- + {apiConfiguration?.apiProvider !== "kodu" && ( Let's go! diff --git a/webview-ui/src/context/ExtensionStateContext.tsx b/webview-ui/src/context/ExtensionStateContext.tsx new file mode 100644 index 0000000..2f2882c --- /dev/null +++ b/webview-ui/src/context/ExtensionStateContext.tsx @@ -0,0 +1,71 @@ +import React, { createContext, useCallback, useContext, useEffect, useState } from "react" +import { useEvent } from "react-use" +import { ExtensionMessage, ExtensionState } from "../../../src/shared/ExtensionMessage" +import { ApiConfiguration } from "../../../src/shared/api" +import { vscode } from "../utils/vscode" + +interface ExtensionStateContextType extends ExtensionState { + setApiConfiguration: (config: ApiConfiguration) => void + setMaxRequestsPerTask: (value?: number) => void + setCustomInstructions: (value?: string) => void + setAlwaysAllowReadOnly: (value: boolean) => void + setShowAnnouncement: (value: boolean) => void +} + +const ExtensionStateContext = createContext(undefined) + +export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => { + const [state, setState] = useState({ + version: "", + claudeMessages: [], + taskHistory: [], + shouldShowAnnouncement: false, + shouldShowKoduPromo: false, + }) + const [didHydrateState, setDidHydrateState] = useState(false) + + const handleMessage = useCallback((event: MessageEvent) => { + const message: ExtensionMessage = event.data + if (message.type === "state" && message.state) { + setState(message.state) + setDidHydrateState(true) + } + if (message.type === "action" && message.action) { + switch (message.action) { + case "koduCreditsFetched": + // special case where we only want to update one part of state in case user is in the middle of modifying settings + setState((prevState) => ({ ...prevState, koduCredits: message.state?.koduCredits })) + break + } + } + }, []) + + useEvent("message", handleMessage) + + useEffect(() => { + vscode.postMessage({ type: "webviewDidLaunch" }) + }, []) + + const contextValue: ExtensionStateContextType = { + ...state, + setApiConfiguration: (value) => setState((prevState) => ({ ...prevState, apiConfiguration: value })), + setMaxRequestsPerTask: (value) => setState((prevState) => ({ ...prevState, maxRequestsPerTask: value })), + setCustomInstructions: (value) => setState((prevState) => ({ ...prevState, customInstructions: value })), + setAlwaysAllowReadOnly: (value) => setState((prevState) => ({ ...prevState, alwaysAllowReadOnly: value })), + setShowAnnouncement: (value) => setState((prevState) => ({ ...prevState, shouldShowAnnouncement: value })), + } + + if (!didHydrateState) { + return null + } + + return {children} +} + +export const useExtensionState = () => { + const context = useContext(ExtensionStateContext) + if (context === undefined) { + throw new Error("useExtensionState must be used within an ExtensionStateContextProvider") + } + return context +} diff --git a/webview-ui/src/index.css b/webview-ui/src/index.css index 73572e5..f451bc8 100644 --- a/webview-ui/src/index.css +++ b/webview-ui/src/index.css @@ -1,15 +1,3 @@ -/* body { - margin: 0; - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", - "Droid Sans", "Helvetica Neue", sans-serif; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -code { - font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace; -} */ - textarea:focus { outline: 1.5px solid var(--vscode-focusBorder, #007fd4); }