mirror of
https://github.com/pacnpal/Roo-Code.git
synced 2025-12-20 04:11:10 -05:00
Add Announcement component to update users on new features
This commit is contained in:
@@ -16,12 +16,14 @@ The best way to solve this is to make your webview stateless. Use message passin
|
||||
*/
|
||||
|
||||
const App: React.FC = () => {
|
||||
const [didHydrateState, setDidHydrateState] = useState(false)
|
||||
const [showSettings, setShowSettings] = useState(false)
|
||||
const [showWelcome, setShowWelcome] = useState<boolean>(false)
|
||||
const [apiKey, setApiKey] = useState<string>("")
|
||||
const [maxRequestsPerTask, setMaxRequestsPerTask] = useState<string>("")
|
||||
const [vscodeThemeName, setVscodeThemeName] = useState<string | undefined>(undefined)
|
||||
const [claudeMessages, setClaudeMessages] = useState<ClaudeMessage[]>([])
|
||||
const [showAnnouncement, setShowAnnouncement] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
vscode.postMessage({ type: "webviewDidLaunch" })
|
||||
@@ -31,14 +33,19 @@ const App: React.FC = () => {
|
||||
const message: ExtensionMessage = e.data
|
||||
switch (message.type) {
|
||||
case "state":
|
||||
const shouldShowWelcome = !message.state!.didOpenOnce || !message.state!.apiKey
|
||||
setShowWelcome(shouldShowWelcome)
|
||||
setShowWelcome(!message.state!.apiKey)
|
||||
setApiKey(message.state!.apiKey || "")
|
||||
setMaxRequestsPerTask(
|
||||
message.state!.maxRequestsPerTask !== undefined ? message.state!.maxRequestsPerTask.toString() : ""
|
||||
)
|
||||
setVscodeThemeName(message.state!.themeName)
|
||||
setClaudeMessages(message.state!.claudeMessages)
|
||||
// don't update showAnnouncement to false if shouldShowAnnouncement is false
|
||||
if (message.state!.shouldShowAnnouncement) {
|
||||
setShowAnnouncement(true)
|
||||
vscode.postMessage({ type: "didShowAnnouncement" })
|
||||
}
|
||||
setDidHydrateState(true)
|
||||
break
|
||||
case "action":
|
||||
switch (message.action!) {
|
||||
@@ -56,6 +63,10 @@ const App: React.FC = () => {
|
||||
|
||||
useEvent("message", handleMessage)
|
||||
|
||||
if (!didHydrateState) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{showWelcome ? (
|
||||
@@ -72,7 +83,13 @@ const App: React.FC = () => {
|
||||
/>
|
||||
)}
|
||||
{/* Do not conditionally load ChatView, it's expensive and there's state we don't want to lose (user input, disableInput, askResponse promise, etc.) */}
|
||||
<ChatView messages={claudeMessages} isHidden={showSettings} vscodeThemeName={vscodeThemeName} />
|
||||
<ChatView
|
||||
messages={claudeMessages}
|
||||
isHidden={showSettings}
|
||||
vscodeThemeName={vscodeThemeName}
|
||||
showAnnouncement={showAnnouncement}
|
||||
hideAnnouncement={() => setShowAnnouncement(false)}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
|
||||
54
webview-ui/src/components/Announcement.tsx
Normal file
54
webview-ui/src/components/Announcement.tsx
Normal file
@@ -0,0 +1,54 @@
|
||||
import { VSCodeButton, VSCodeLink } from "@vscode/webview-ui-toolkit/react"
|
||||
|
||||
interface AnnouncementProps {
|
||||
hideAnnouncement: () => void
|
||||
}
|
||||
/*
|
||||
You must update the latestAnnouncementId in ClaudeDevProvider for new announcements to show to users. This new id will be compared with whats in state for the 'last announcement shown', and if it's different then the announcement will render. As soon as an announcement is shown, the id will be updated in state. This ensures that announcements are not shown more than once, even if the user doesn't close it themselves.
|
||||
*/
|
||||
const Announcement = ({ hideAnnouncement }: AnnouncementProps) => {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
backgroundColor: "var(--vscode-editor-inactiveSelectionBackground)",
|
||||
borderRadius: "3px",
|
||||
padding: "12px 16px",
|
||||
margin: "5px 15px 5px 15px",
|
||||
position: "relative",
|
||||
}}>
|
||||
<VSCodeButton
|
||||
appearance="icon"
|
||||
onClick={hideAnnouncement}
|
||||
style={{ position: "absolute", top: "8px", right: "8px" }}>
|
||||
<span className="codicon codicon-close"></span>
|
||||
</VSCodeButton>
|
||||
<h3 style={{ margin: "0 0 8px" }}>🎉{" "}New in v1.0.0</h3>
|
||||
<ul style={{ margin: "0 0 8px", paddingLeft: "20px" }}>
|
||||
<li>
|
||||
Open in the editor (using{" "}
|
||||
<span
|
||||
className="codicon codicon-link-external"
|
||||
style={{ display: "inline", fontSize: "12.5px", verticalAlign: "text-bottom" }}></span>{" "}
|
||||
or <code>Claude Dev: Open In New Tab</code> in command palette) to see how Claude updates your
|
||||
workspace more clearly
|
||||
</li>
|
||||
<li>Provide feedback to tool use like terminal commands and file edits</li>
|
||||
<li>
|
||||
Updated max output tokens to 8192 so less lazy coding (<code>{"// rest of code here..."}</code>)
|
||||
</li>
|
||||
<li>Added ability to retry failed API requests (helpful for rate limits)</li>
|
||||
<li>
|
||||
Quality of life improvements like markdown rendering, memory optimizations, better theme support
|
||||
</li>
|
||||
</ul>
|
||||
<p style={{ margin: "0" }}>
|
||||
Subscribe to my new YouTube to see how to get the most out of Claude Dev!{" "}
|
||||
<VSCodeLink href="https://youtube.com/@saoudrizwan" style={{ display: "inline" }}>
|
||||
https://youtube.com/@saoudrizwan
|
||||
</VSCodeLink>
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Announcement
|
||||
@@ -97,7 +97,7 @@ const ChatRow: React.FC<ChatRowProps> = ({
|
||||
}
|
||||
}
|
||||
|
||||
const convertToMarkdown = (markdown: string = "") => {
|
||||
const renderMarkdown = (markdown: string = "") => {
|
||||
// react-markdown lets us customize elements, so here we're using their example of replacing code blocks with SyntaxHighlighter. However when there are no language matches (` or ``` without a language specifier) then we default to a normal code element for inline code. Code blocks without a language specifier shouldn't be a common occurrence as we prompt Claude to always use a language specifier.
|
||||
return (
|
||||
<Markdown
|
||||
@@ -107,7 +107,14 @@ const ChatRow: React.FC<ChatRowProps> = ({
|
||||
const { style, ...rest } = props
|
||||
return <p style={{ ...style, margin: 0, marginTop: 0, marginBottom: 0 }} {...rest} />
|
||||
},
|
||||
//p: "span",
|
||||
ol(props) {
|
||||
const { style, ...rest } = props
|
||||
return <ol style={{ ...style, padding: "0 0 0 20px", margin: "10px 0" }} {...rest} />
|
||||
},
|
||||
ul(props) {
|
||||
const { style, ...rest } = props
|
||||
return <ul style={{ ...style, padding: "0 0 0 20px", margin: "10px 0" }} {...rest} />
|
||||
},
|
||||
// https://github.com/remarkjs/react-markdown?tab=readme-ov-file#use-custom-components-syntax-highlight
|
||||
code(props) {
|
||||
const { children, className, node, ...rest } = props
|
||||
@@ -205,7 +212,7 @@ const ChatRow: React.FC<ChatRowProps> = ({
|
||||
case "api_req_finished":
|
||||
return null // we should never see this message type
|
||||
case "text":
|
||||
return <div>{convertToMarkdown(message.text)}</div>
|
||||
return <div>{renderMarkdown(message.text)}</div>
|
||||
case "user_feedback":
|
||||
return (
|
||||
<div
|
||||
@@ -240,7 +247,7 @@ const ChatRow: React.FC<ChatRowProps> = ({
|
||||
{title}
|
||||
</div>
|
||||
<div style={{ color: "var(--vscode-testing-iconPassed)" }}>
|
||||
{convertToMarkdown(message.text)}
|
||||
{renderMarkdown(message.text)}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
@@ -253,7 +260,7 @@ const ChatRow: React.FC<ChatRowProps> = ({
|
||||
{title}
|
||||
</div>
|
||||
)}
|
||||
<div>{convertToMarkdown(message.text)}</div>
|
||||
<div>{renderMarkdown(message.text)}</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -405,7 +412,7 @@ const ChatRow: React.FC<ChatRowProps> = ({
|
||||
{title}
|
||||
</div>
|
||||
<div style={{ color: "var(--vscode-testing-iconPassed)" }}>
|
||||
{convertToMarkdown(message.text)}
|
||||
{renderMarkdown(message.text)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
@@ -421,7 +428,7 @@ const ChatRow: React.FC<ChatRowProps> = ({
|
||||
{title}
|
||||
</div>
|
||||
)}
|
||||
<div>{convertToMarkdown(message.text)}</div>
|
||||
<div>{renderMarkdown(message.text)}</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -12,14 +12,17 @@ import { vscode } from "../utilities/vscode"
|
||||
import ChatRow from "./ChatRow"
|
||||
import TaskHeader from "./TaskHeader"
|
||||
import { Virtuoso, type VirtuosoHandle } from "react-virtuoso"
|
||||
import Announcement from "./Announcement"
|
||||
|
||||
interface ChatViewProps {
|
||||
messages: ClaudeMessage[]
|
||||
isHidden: boolean
|
||||
vscodeThemeName?: string
|
||||
showAnnouncement: boolean
|
||||
hideAnnouncement: () => void
|
||||
}
|
||||
// maybe instead of storing state in App, just make chatview always show so dont conditionally load/unload? need to make sure messages are persisted (i remember seeing something about how webviews can be frozen in docs)
|
||||
const ChatView = ({ messages, isHidden, vscodeThemeName }: ChatViewProps) => {
|
||||
const ChatView = ({ messages, isHidden, vscodeThemeName, showAnnouncement, hideAnnouncement }: ChatViewProps) => {
|
||||
//const task = messages.length > 0 ? (messages[0].say === "task" ? messages[0] : 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])
|
||||
@@ -337,20 +340,24 @@ const ChatView = ({ messages, isHidden, vscodeThemeName }: ChatViewProps) => {
|
||||
isHidden={isHidden}
|
||||
/>
|
||||
) : (
|
||||
<div style={{ padding: "0 25px" }}>
|
||||
<h2>What can I do for you?</h2>
|
||||
<p>
|
||||
Thanks to{" "}
|
||||
<VSCodeLink
|
||||
href="https://www-cdn.anthropic.com/fed9cc193a14b84131812372d8d5857f8f304c52/Model_Card_Claude_3_Addendum.pdf"
|
||||
style={{ display: "inline" }}>
|
||||
Claude 3.5 Sonnet's agentic coding capabilities,
|
||||
</VSCodeLink>{" "}
|
||||
I can handle complex software development tasks step-by-step. With tools that let me read &
|
||||
write files, create entire projects from scratch, and execute terminal commands (after you grant
|
||||
permission), I can assist you in ways that go beyond simple code completion or tech support.
|
||||
</p>
|
||||
</div>
|
||||
<>
|
||||
{showAnnouncement && <Announcement hideAnnouncement={hideAnnouncement} />}
|
||||
<div style={{ padding: "0 20px" }}>
|
||||
<h2>What can I do for you?</h2>
|
||||
<p>
|
||||
Thanks to{" "}
|
||||
<VSCodeLink
|
||||
href="https://www-cdn.anthropic.com/fed9cc193a14b84131812372d8d5857f8f304c52/Model_Card_Claude_3_Addendum.pdf"
|
||||
style={{ display: "inline" }}>
|
||||
Claude 3.5 Sonnet's agentic coding capabilities,
|
||||
</VSCodeLink>{" "}
|
||||
I can handle complex software development tasks step-by-step. With tools that let me read &
|
||||
write files, create entire projects from scratch, and execute terminal commands (after you
|
||||
grant permission), I can assist you in ways that go beyond simple code completion or tech
|
||||
support.
|
||||
</p>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<Virtuoso
|
||||
ref={virtuosoRef}
|
||||
|
||||
@@ -12,12 +12,6 @@ const WelcomeView: React.FC<WelcomeViewProps> = ({ apiKey, setApiKey }) => {
|
||||
|
||||
const disableLetsGoButton = apiKeyErrorMessage != null
|
||||
|
||||
const handleApiKeyChange = (event: any) => {
|
||||
const input = event.target.value
|
||||
setApiKey(input)
|
||||
validateApiKey(input)
|
||||
}
|
||||
|
||||
const validateApiKey = (value: string) => {
|
||||
if (value.trim() === "") {
|
||||
setApiKeyErrorMessage("API Key cannot be empty")
|
||||
@@ -32,10 +26,10 @@ const WelcomeView: React.FC<WelcomeViewProps> = ({ apiKey, setApiKey }) => {
|
||||
|
||||
useEffect(() => {
|
||||
validateApiKey(apiKey)
|
||||
}, [])
|
||||
}, [apiKey])
|
||||
|
||||
return (
|
||||
<div style={{ position: "fixed", top: 0, left: 0, right: 0, bottom: 0, padding: "0 15px" }}>
|
||||
<div style={{ position: "fixed", top: 0, left: 0, right: 0, bottom: 0, padding: "0 20px" }}>
|
||||
<h2>Hi, I'm Claude Dev</h2>
|
||||
<p>
|
||||
I can do all kinds of tasks thanks to the latest breakthroughs in Claude Sonnet 3.5's agentic coding
|
||||
@@ -48,8 +42,8 @@ const WelcomeView: React.FC<WelcomeViewProps> = ({ apiKey, setApiKey }) => {
|
||||
<ol style={{ paddingLeft: "15px" }}>
|
||||
<li>
|
||||
Go to{" "}
|
||||
<VSCodeLink href="https://console.anthropic.com/" style={{ display: "inline" }}>
|
||||
https://console.anthropic.com/
|
||||
<VSCodeLink href="https://console.anthropic.com" style={{ display: "inline" }}>
|
||||
https://console.anthropic.com
|
||||
</VSCodeLink>
|
||||
</li>
|
||||
<li>You may need to buy some credits (although Anthropic is offering $5 free credit for new users)</li>
|
||||
@@ -63,7 +57,7 @@ const WelcomeView: React.FC<WelcomeViewProps> = ({ apiKey, setApiKey }) => {
|
||||
style={{ flexGrow: 1, marginRight: "10px" }}
|
||||
placeholder="Enter API Key..."
|
||||
value={apiKey}
|
||||
onInput={handleApiKeyChange}
|
||||
onInput={(e: any) => setApiKey(e.target.value)}
|
||||
/>
|
||||
<VSCodeButton onClick={handleSubmit} disabled={disableLetsGoButton}>
|
||||
Submit
|
||||
|
||||
Reference in New Issue
Block a user