mirror of
https://github.com/pacnpal/Roo-Code.git
synced 2025-12-20 12:21:13 -05:00
Add option to choose different models
This commit is contained in:
@@ -1,19 +1,18 @@
|
||||
import React, { useEffect, useState, useCallback } from "react"
|
||||
import React, { useCallback, useEffect, useMemo, useState } from "react"
|
||||
import { useEvent } from "react-use"
|
||||
import { ApiConfiguration } from "../../src/shared/api"
|
||||
import { ClaudeMessage, ExtensionMessage } from "../../src/shared/ExtensionMessage"
|
||||
import "./App.css"
|
||||
import { normalizeApiConfiguration } from "./components/ApiOptions"
|
||||
import ChatView from "./components/ChatView"
|
||||
import SettingsView from "./components/SettingsView"
|
||||
import { ClaudeMessage, ExtensionMessage } from "@shared/ExtensionMessage"
|
||||
import WelcomeView from "./components/WelcomeView"
|
||||
import { vscode } from "./utils/vscode"
|
||||
import { useEvent } from "react-use"
|
||||
import { ApiConfiguration } from "@shared/api"
|
||||
|
||||
/*
|
||||
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 = () => {
|
||||
@@ -70,6 +69,10 @@ const App: React.FC = () => {
|
||||
|
||||
useEvent("message", handleMessage)
|
||||
|
||||
const { selectedModelInfo } = useMemo(() => {
|
||||
return normalizeApiConfiguration(apiConfiguration)
|
||||
}, [apiConfiguration])
|
||||
|
||||
if (!didHydrateState) {
|
||||
return null
|
||||
}
|
||||
@@ -97,6 +100,7 @@ const App: React.FC = () => {
|
||||
isHidden={showSettings}
|
||||
vscodeThemeName={vscodeThemeName}
|
||||
showAnnouncement={showAnnouncement}
|
||||
selectedModelSupportsImages={selectedModelInfo.supportsImages}
|
||||
hideAnnouncement={() => setShowAnnouncement(false)}
|
||||
/>
|
||||
</>
|
||||
|
||||
@@ -1,34 +1,78 @@
|
||||
import { ApiConfiguration } from "@shared/api"
|
||||
import { VSCodeDropdown, VSCodeLink, VSCodeOption, VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
|
||||
import React from "react"
|
||||
import React, { useMemo } from "react"
|
||||
import {
|
||||
ApiConfiguration,
|
||||
ApiModelId,
|
||||
ModelInfo,
|
||||
anthropicDefaultModelId,
|
||||
anthropicModels,
|
||||
bedrockDefaultModelId,
|
||||
bedrockModels,
|
||||
openRouterDefaultModelId,
|
||||
openRouterModels,
|
||||
} from "../../../src/shared/api"
|
||||
|
||||
interface ApiOptionsProps {
|
||||
showModelOptions: boolean
|
||||
apiConfiguration?: ApiConfiguration
|
||||
setApiConfiguration: React.Dispatch<React.SetStateAction<ApiConfiguration | undefined>>
|
||||
}
|
||||
|
||||
const ApiOptions: React.FC<ApiOptionsProps> = ({ apiConfiguration, setApiConfiguration }) => {
|
||||
const ApiOptions: React.FC<ApiOptionsProps> = ({ showModelOptions, apiConfiguration, setApiConfiguration }) => {
|
||||
const handleInputChange = (field: keyof ApiConfiguration) => (event: any) => {
|
||||
setApiConfiguration((prev) => ({ ...prev, [field]: event.target.value }))
|
||||
}
|
||||
|
||||
const { selectedProvider, selectedModelId, selectedModelInfo } = useMemo(() => {
|
||||
return normalizeApiConfiguration(apiConfiguration)
|
||||
}, [apiConfiguration])
|
||||
|
||||
/*
|
||||
VSCodeDropdown has an open bug where dynamically rendered options don't auto select the provided value prop. You can see this for yourself by comparing it with normal select/option elements, which work as expected.
|
||||
https://github.com/microsoft/vscode-webview-ui-toolkit/issues/433
|
||||
|
||||
In our case, when the user switches between providers, we recalculate the selectedModelId depending on the provider, the default model for that provider, and a modelId that the user may have selected. Unfortunately, the VSCodeDropdown component wouldn't select this calculated value, and would default to the first "Select a model..." option instead, which makes it seem like the model was cleared out when it wasn't.
|
||||
|
||||
As a workaround, we create separate instances of the dropdown for each provider, and then conditionally render the one that matches the current provider.
|
||||
*/
|
||||
const createDropdown = (models: Record<string, ModelInfo>) => {
|
||||
return (
|
||||
<VSCodeDropdown
|
||||
id="model-id"
|
||||
value={selectedModelId}
|
||||
onChange={handleInputChange("apiModelId")}
|
||||
style={{ width: "100%" }}>
|
||||
<VSCodeOption value="">Select a model...</VSCodeOption>
|
||||
{Object.keys(models).map((modelId) => (
|
||||
<VSCodeOption
|
||||
key={modelId}
|
||||
value={modelId}
|
||||
style={{
|
||||
whiteSpace: "normal",
|
||||
wordWrap: "break-word",
|
||||
maxWidth: "100%",
|
||||
}}>
|
||||
{modelId}
|
||||
</VSCodeOption>
|
||||
))}
|
||||
</VSCodeDropdown>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={{ display: "flex", flexDirection: "column", gap: 5 }}>
|
||||
<div className="dropdown-container">
|
||||
<label htmlFor="api-provider">
|
||||
<span style={{ fontWeight: 500 }}>API Provider</span>
|
||||
</label>
|
||||
<VSCodeDropdown
|
||||
id="api-provider"
|
||||
value={apiConfiguration?.apiProvider || "anthropic"}
|
||||
onChange={handleInputChange("apiProvider")}>
|
||||
<VSCodeDropdown id="api-provider" value={selectedProvider} onChange={handleInputChange("apiProvider")}>
|
||||
<VSCodeOption value="anthropic">Anthropic</VSCodeOption>
|
||||
<VSCodeOption value="openrouter">OpenRouter</VSCodeOption>
|
||||
<VSCodeOption value="bedrock">AWS Bedrock</VSCodeOption>
|
||||
</VSCodeDropdown>
|
||||
</div>
|
||||
|
||||
{apiConfiguration?.apiProvider === "anthropic" && (
|
||||
{selectedProvider === "anthropic" && (
|
||||
<div>
|
||||
<VSCodeTextField
|
||||
value={apiConfiguration?.apiKey || ""}
|
||||
@@ -51,7 +95,7 @@ const ApiOptions: React.FC<ApiOptionsProps> = ({ apiConfiguration, setApiConfigu
|
||||
</div>
|
||||
)}
|
||||
|
||||
{apiConfiguration?.apiProvider === "openrouter" && (
|
||||
{selectedProvider === "openrouter" && (
|
||||
<div>
|
||||
<VSCodeTextField
|
||||
value={apiConfiguration?.openRouterApiKey || ""}
|
||||
@@ -74,7 +118,7 @@ const ApiOptions: React.FC<ApiOptionsProps> = ({ apiConfiguration, setApiConfigu
|
||||
</div>
|
||||
)}
|
||||
|
||||
{apiConfiguration?.apiProvider === "bedrock" && (
|
||||
{selectedProvider === "bedrock" && (
|
||||
<div style={{ display: "flex", flexDirection: "column", gap: 5 }}>
|
||||
<VSCodeTextField
|
||||
value={apiConfiguration?.awsAccessKey || ""}
|
||||
@@ -100,9 +144,9 @@ const ApiOptions: React.FC<ApiOptionsProps> = ({ apiConfiguration, setApiConfigu
|
||||
style={{ width: "100%" }}
|
||||
onChange={handleInputChange("awsRegion")}>
|
||||
<VSCodeOption value="">Select a region...</VSCodeOption>
|
||||
{/* Currently Claude 3.5 Sonnet is only available in us-east-1 */}
|
||||
{/* The user will have to choose a region that supports the model they use, but this shouldn't be a problem since they'd have to request access for it in that region in the first place. */}
|
||||
<VSCodeOption value="us-east-1">US East (N. Virginia)</VSCodeOption>
|
||||
{/* <VSCodeOption value="us-east-2">US East (Ohio)</VSCodeOption>
|
||||
<VSCodeOption value="us-east-2">US East (Ohio)</VSCodeOption>
|
||||
<VSCodeOption value="us-west-1">US West (N. California)</VSCodeOption>
|
||||
<VSCodeOption value="us-west-2">US West (Oregon)</VSCodeOption>
|
||||
<VSCodeOption value="af-south-1">Africa (Cape Town)</VSCodeOption>
|
||||
@@ -120,7 +164,7 @@ const ApiOptions: React.FC<ApiOptionsProps> = ({ apiConfiguration, setApiConfigu
|
||||
<VSCodeOption value="eu-west-3">Europe (Paris)</VSCodeOption>
|
||||
<VSCodeOption value="eu-north-1">Europe (Stockholm)</VSCodeOption>
|
||||
<VSCodeOption value="me-south-1">Middle East (Bahrain)</VSCodeOption>
|
||||
<VSCodeOption value="sa-east-1">South America (São Paulo)</VSCodeOption> */}
|
||||
<VSCodeOption value="sa-east-1">South America (São Paulo)</VSCodeOption>
|
||||
</VSCodeDropdown>
|
||||
</div>
|
||||
<p
|
||||
@@ -138,8 +182,91 @@ const ApiOptions: React.FC<ApiOptionsProps> = ({ apiConfiguration, setApiConfigu
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{showModelOptions && (
|
||||
<>
|
||||
<div className="dropdown-container">
|
||||
<label htmlFor="model-id">
|
||||
<span style={{ fontWeight: 500 }}>Model</span>
|
||||
</label>
|
||||
{selectedProvider === "anthropic" && createDropdown(anthropicModels)}
|
||||
{selectedProvider === "openrouter" && createDropdown(openRouterModels)}
|
||||
{selectedProvider === "bedrock" && createDropdown(bedrockModels)}
|
||||
</div>
|
||||
|
||||
<ModelInfoView modelInfo={selectedModelInfo} />
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const ModelInfoView = ({ modelInfo }: { modelInfo: ModelInfo }) => {
|
||||
const formatPrice = (price: number) => {
|
||||
return new Intl.NumberFormat("en-US", {
|
||||
style: "currency",
|
||||
currency: "USD",
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
}).format(price)
|
||||
}
|
||||
|
||||
return (
|
||||
<p style={{ fontSize: "12px", marginTop: "2px", color: "var(--vscode-descriptionForeground)" }}>
|
||||
<span
|
||||
style={{
|
||||
fontWeight: 500,
|
||||
color: modelInfo.supportsImages
|
||||
? "var(--vscode-testing-iconPassed)"
|
||||
: "var(--vscode-errorForeground)",
|
||||
}}>
|
||||
<i
|
||||
className={`codicon codicon-${modelInfo.supportsImages ? "check" : "x"}`}
|
||||
style={{
|
||||
marginRight: 4,
|
||||
marginBottom: modelInfo.supportsImages ? 1 : -1,
|
||||
fontSize: modelInfo.supportsImages ? 11 : 13,
|
||||
fontWeight: 700,
|
||||
display: "inline-block",
|
||||
verticalAlign: "bottom",
|
||||
}}></i>
|
||||
{modelInfo.supportsImages ? "Supports images" : "Does not support images"}
|
||||
</span>
|
||||
<br />
|
||||
<span style={{ fontWeight: 500 }}>Max output:</span> {modelInfo.maxTokens.toLocaleString()} tokens
|
||||
<br />
|
||||
<span style={{ fontWeight: 500 }}>Input price:</span> {formatPrice(modelInfo.inputPrice)} per million tokens
|
||||
<br />
|
||||
<span style={{ fontWeight: 500 }}>Output price:</span> {formatPrice(modelInfo.outputPrice)} per million
|
||||
tokens
|
||||
</p>
|
||||
)
|
||||
}
|
||||
|
||||
export function normalizeApiConfiguration(apiConfiguration?: ApiConfiguration) {
|
||||
const provider = apiConfiguration?.apiProvider || "anthropic"
|
||||
const modelId = apiConfiguration?.apiModelId
|
||||
|
||||
const getProviderData = (models: Record<string, ModelInfo>, defaultId: ApiModelId) => {
|
||||
let selectedModelId: ApiModelId
|
||||
let selectedModelInfo: ModelInfo
|
||||
if (modelId && modelId in models) {
|
||||
selectedModelId = modelId
|
||||
selectedModelInfo = models[modelId]
|
||||
} else {
|
||||
selectedModelId = defaultId
|
||||
selectedModelInfo = models[defaultId]
|
||||
}
|
||||
return { selectedProvider: provider, selectedModelId, selectedModelInfo }
|
||||
}
|
||||
switch (provider) {
|
||||
case "anthropic":
|
||||
return getProviderData(anthropicModels, anthropicDefaultModelId)
|
||||
case "openrouter":
|
||||
return getProviderData(openRouterModels, openRouterDefaultModelId)
|
||||
case "bedrock":
|
||||
return getProviderData(bedrockModels, bedrockDefaultModelId)
|
||||
}
|
||||
}
|
||||
|
||||
export default ApiOptions
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { ClaudeAsk, ClaudeMessage, ClaudeSay, ClaudeSayTool } from "@shared/ExtensionMessage"
|
||||
import { VSCodeBadge, VSCodeButton, VSCodeProgressRing } from "@vscode/webview-ui-toolkit/react"
|
||||
import React from "react"
|
||||
import Markdown from "react-markdown"
|
||||
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"
|
||||
import { ClaudeAsk, ClaudeMessage, ClaudeSay, ClaudeSayTool } from "../../../src/shared/ExtensionMessage"
|
||||
import { COMMAND_OUTPUT_STRING } from "../utils/combineCommandSequences"
|
||||
import { SyntaxHighlighterStyle } from "../utils/getSyntaxHighlighterStyleFromTheme"
|
||||
import CodeBlock from "./CodeBlock/CodeBlock"
|
||||
import Markdown from "react-markdown"
|
||||
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"
|
||||
import Thumbnails from "./Thumbnails"
|
||||
|
||||
interface ChatRowProps {
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import { ClaudeAsk, ClaudeMessage, ExtensionMessage } from "@shared/ExtensionMessage"
|
||||
import { VSCodeButton, VSCodeLink } from "@vscode/webview-ui-toolkit/react"
|
||||
import { KeyboardEvent, useCallback, useEffect, useMemo, useRef, useState } from "react"
|
||||
import vsDarkPlus from "react-syntax-highlighter/dist/esm/styles/prism/vsc-dark-plus"
|
||||
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 { combineApiRequests } from "../utils/combineApiRequests"
|
||||
import { combineCommandSequences } from "../utils/combineCommandSequences"
|
||||
import { getApiMetrics } from "../utils/getApiMetrics"
|
||||
import { getSyntaxHighlighterStyleFromTheme } from "../utils/getSyntaxHighlighterStyleFromTheme"
|
||||
import { vscode } from "../utils/vscode"
|
||||
import Announcement from "./Announcement"
|
||||
import ChatRow from "./ChatRow"
|
||||
import TaskHeader from "./TaskHeader"
|
||||
import { Virtuoso, type VirtuosoHandle } from "react-virtuoso"
|
||||
import Announcement from "./Announcement"
|
||||
import Thumbnails from "./Thumbnails"
|
||||
|
||||
interface ChatViewProps {
|
||||
@@ -21,6 +21,7 @@ interface ChatViewProps {
|
||||
isHidden: boolean
|
||||
vscodeThemeName?: string
|
||||
showAnnouncement: boolean
|
||||
selectedModelSupportsImages: boolean
|
||||
hideAnnouncement: () => void
|
||||
}
|
||||
|
||||
@@ -32,6 +33,7 @@ const ChatView = ({
|
||||
isHidden,
|
||||
vscodeThemeName,
|
||||
showAnnouncement,
|
||||
selectedModelSupportsImages,
|
||||
hideAnnouncement,
|
||||
}: ChatViewProps) => {
|
||||
//const task = messages.length > 0 ? (messages[0].say === "task" ? messages[0] : undefined) : undefined
|
||||
@@ -278,6 +280,11 @@ const ChatView = ({
|
||||
}
|
||||
|
||||
const handlePaste = async (e: React.ClipboardEvent) => {
|
||||
if (shouldDisableImages) {
|
||||
e.preventDefault()
|
||||
return
|
||||
}
|
||||
|
||||
const items = e.clipboardData.items
|
||||
const acceptedTypes = ["png", "jpeg", "webp"] // supported by anthropic and openrouter (jpg is just a file extension but the image will be recognized as jpeg)
|
||||
const imageItems = Array.from(items).filter((item) => {
|
||||
@@ -412,6 +419,12 @@ const ChatView = ({
|
||||
return [text, false]
|
||||
}, [task, messages])
|
||||
|
||||
const shouldDisableImages =
|
||||
!selectedModelSupportsImages ||
|
||||
textAreaDisabled ||
|
||||
selectedImages.length >= MAX_IMAGES_PER_MESSAGE ||
|
||||
isInputPipingToStdin
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
@@ -590,9 +603,7 @@ const ChatView = ({
|
||||
height: "calc(100% - 20px)", // Full height minus top and bottom padding
|
||||
}}>
|
||||
<VSCodeButton
|
||||
disabled={
|
||||
textAreaDisabled || selectedImages.length >= MAX_IMAGES_PER_MESSAGE || isInputPipingToStdin
|
||||
}
|
||||
disabled={shouldDisableImages}
|
||||
appearance="icon"
|
||||
aria-label="Attach Images"
|
||||
onClick={selectImages}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ApiConfiguration } from "@shared/api"
|
||||
import { VSCodeButton, VSCodeDivider, VSCodeLink, VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
|
||||
import { VSCodeButton, VSCodeLink, VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
|
||||
import React, { useEffect, useState } from "react"
|
||||
import { ApiConfiguration } from "../../../src/shared/api"
|
||||
import { validateApiConfiguration, validateMaxRequestsPerTask } from "../utils/validate"
|
||||
import { vscode } from "../utils/vscode"
|
||||
import ApiOptions from "./ApiOptions"
|
||||
@@ -60,78 +60,92 @@ const SettingsView = ({
|
||||
*/
|
||||
|
||||
return (
|
||||
<div style={{ margin: "0 auto", paddingTop: "10px" }}>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
marginBottom: "17px",
|
||||
}}>
|
||||
<h3 style={{ color: "var(--vscode-foreground)", margin: 0 }}>Settings</h3>
|
||||
<VSCodeButton onClick={handleSubmit}>Done</VSCodeButton>
|
||||
</div>
|
||||
|
||||
<div style={{ marginBottom: 5 }}>
|
||||
<ApiOptions apiConfiguration={apiConfiguration} setApiConfiguration={setApiConfiguration} />
|
||||
{apiErrorMessage && (
|
||||
<p
|
||||
style={{
|
||||
margin: "-5px 0 12px 0",
|
||||
fontSize: "12px",
|
||||
color: "var(--vscode-errorForeground)",
|
||||
}}>
|
||||
{apiErrorMessage}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div style={{ marginBottom: "20px" }}>
|
||||
<VSCodeTextField
|
||||
value={maxRequestsPerTask}
|
||||
style={{ width: "100%" }}
|
||||
placeholder="20"
|
||||
onInput={(e: any) => setMaxRequestsPerTask(e.target?.value)}>
|
||||
<span style={{ fontWeight: "500" }}>Maximum # Requests Per Task</span>
|
||||
</VSCodeTextField>
|
||||
<p
|
||||
<div
|
||||
style={{
|
||||
position: "fixed",
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
padding: "10px 18px 18px 20px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
overflow: "hidden",
|
||||
}}>
|
||||
<div style={{ flexGrow: 1, overflow: "auto" }}>
|
||||
<div
|
||||
style={{
|
||||
fontSize: "12px",
|
||||
marginTop: "5px",
|
||||
color: "var(--vscode-descriptionForeground)",
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
marginBottom: "17px",
|
||||
}}>
|
||||
If Claude Dev reaches this limit, it will pause and ask for your permission before making additional
|
||||
requests.
|
||||
</p>
|
||||
{maxRequestsErrorMessage && (
|
||||
<h3 style={{ color: "var(--vscode-foreground)", margin: 0 }}>Settings</h3>
|
||||
<VSCodeButton onClick={handleSubmit}>Done</VSCodeButton>
|
||||
</div>
|
||||
|
||||
<div style={{ marginBottom: 5 }}>
|
||||
<ApiOptions
|
||||
apiConfiguration={apiConfiguration}
|
||||
setApiConfiguration={setApiConfiguration}
|
||||
showModelOptions={true}
|
||||
/>
|
||||
{apiErrorMessage && (
|
||||
<p
|
||||
style={{
|
||||
margin: "-5px 0 12px 0",
|
||||
fontSize: "12px",
|
||||
color: "var(--vscode-errorForeground)",
|
||||
}}>
|
||||
{apiErrorMessage}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div style={{ marginBottom: "20px" }}>
|
||||
<VSCodeTextField
|
||||
value={maxRequestsPerTask}
|
||||
style={{ width: "100%" }}
|
||||
placeholder="20"
|
||||
onInput={(e: any) => setMaxRequestsPerTask(e.target?.value)}>
|
||||
<span style={{ fontWeight: "500" }}>Maximum # Requests Per Task</span>
|
||||
</VSCodeTextField>
|
||||
<p
|
||||
style={{
|
||||
fontSize: "12px",
|
||||
marginTop: "5px",
|
||||
color: "var(--vscode-errorForeground)",
|
||||
color: "var(--vscode-descriptionForeground)",
|
||||
}}>
|
||||
{maxRequestsErrorMessage}
|
||||
If Claude Dev reaches this limit, it will pause and ask for your permission before making
|
||||
additional requests.
|
||||
</p>
|
||||
)}
|
||||
{maxRequestsErrorMessage && (
|
||||
<p
|
||||
style={{
|
||||
fontSize: "12px",
|
||||
marginTop: "5px",
|
||||
color: "var(--vscode-errorForeground)",
|
||||
}}>
|
||||
{maxRequestsErrorMessage}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<VSCodeDivider />
|
||||
|
||||
<div
|
||||
style={{
|
||||
marginTop: "20px",
|
||||
textAlign: "center",
|
||||
color: "var(--vscode-descriptionForeground)",
|
||||
fontSize: "12px",
|
||||
lineHeight: "1.2",
|
||||
}}>
|
||||
<p style={{ wordWrap: "break-word" }}>
|
||||
<p style={{ wordWrap: "break-word", margin: 0, padding: 0 }}>
|
||||
If you have any questions or feedback, feel free to open an issue at{" "}
|
||||
<VSCodeLink href="https://github.com/saoudrizwan/claude-dev" style={{ display: "inline" }}>
|
||||
https://github.com/saoudrizwan/claude-dev
|
||||
</VSCodeLink>
|
||||
</p>
|
||||
<p style={{ fontStyle: "italic" }}>v{version}</p>
|
||||
<p style={{ fontStyle: "italic", margin: "10px 0 0 0", padding: 0 }}>v{version}</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -2,8 +2,8 @@ import { VSCodeButton } from "@vscode/webview-ui-toolkit/react"
|
||||
import React, { useEffect, useRef, useState } from "react"
|
||||
import TextTruncate from "react-text-truncate"
|
||||
import { useWindowSize } from "react-use"
|
||||
import { ClaudeMessage } from "../../../src/shared/ExtensionMessage"
|
||||
import { vscode } from "../utils/vscode"
|
||||
import { ClaudeMessage } from "@shared/ExtensionMessage"
|
||||
import Thumbnails from "./Thumbnails"
|
||||
|
||||
interface TaskHeaderProps {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ApiConfiguration } from "@shared/api"
|
||||
import { ApiConfiguration } from "../../../src/shared/api"
|
||||
import { VSCodeButton, VSCodeLink } from "@vscode/webview-ui-toolkit/react"
|
||||
import React, { useEffect, useState } from "react"
|
||||
import { validateApiConfiguration } from "../utils/validate"
|
||||
@@ -40,7 +40,11 @@ const WelcomeView: React.FC<WelcomeViewProps> = ({ apiConfiguration, setApiConfi
|
||||
<b>To get started, this extension needs an API key for Claude 3.5 Sonnet:</b>
|
||||
|
||||
<div style={{ marginTop: "15px" }}>
|
||||
<ApiOptions apiConfiguration={apiConfiguration} setApiConfiguration={setApiConfiguration} />
|
||||
<ApiOptions
|
||||
apiConfiguration={apiConfiguration}
|
||||
setApiConfiguration={setApiConfiguration}
|
||||
showModelOptions={false}
|
||||
/>
|
||||
<VSCodeButton onClick={handleSubmit} disabled={disableLetsGoButton} style={{ marginTop: "3px" }}>
|
||||
Let's go!
|
||||
</VSCodeButton>
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import { ClaudeMessage } from "@shared/ExtensionMessage"
|
||||
import { ClaudeMessage } from "../../../src/shared/ExtensionMessage"
|
||||
|
||||
/**
|
||||
* Combines API request start and finish messages in an array of ClaudeMessages.
|
||||
*
|
||||
*
|
||||
* This function looks for pairs of 'api_req_started' and 'api_req_finished' messages.
|
||||
* When it finds a pair, it combines them into a single 'api_req_combined' message.
|
||||
* The JSON data in the text fields of both messages are merged.
|
||||
*
|
||||
*
|
||||
* @param messages - An array of ClaudeMessage objects to process.
|
||||
* @returns A new array of ClaudeMessage objects with API requests combined.
|
||||
*
|
||||
*
|
||||
* @example
|
||||
* const messages = [
|
||||
* { type: "say", say: "api_req_started", text: '{"request":"GET /api/data"}', ts: 1000 },
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ClaudeMessage } from "@shared/ExtensionMessage"
|
||||
import { ClaudeMessage } from "../../../src/shared/ExtensionMessage"
|
||||
|
||||
/**
|
||||
* Combines sequences of command and command_output messages in an array of ClaudeMessages.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ClaudeMessage } from "@shared/ExtensionMessage"
|
||||
import { ClaudeMessage } from "../../../src/shared/ExtensionMessage"
|
||||
|
||||
interface ApiMetrics {
|
||||
totalTokensIn: number
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ClaudeMessage } from "@shared/ExtensionMessage";
|
||||
import { ClaudeMessage } from "../../../src/shared/ExtensionMessage"
|
||||
|
||||
export const mockMessages: ClaudeMessage[] = [
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ApiConfiguration } from "@shared/api"
|
||||
import { ApiConfiguration } from "../../../src/shared/api"
|
||||
|
||||
export function validateApiConfiguration(apiConfiguration?: ApiConfiguration): string | undefined {
|
||||
if (apiConfiguration) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { WebviewMessage } from "@shared/WebviewMessage"
|
||||
import { WebviewMessage } from "../../../src/shared/WebviewMessage"
|
||||
import type { WebviewApi } from "vscode-webview"
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user