Cody: Add some recipes buttons to the welcome message (#54277)

This adds three recipes to the welcome chat message in VScode to help
users get started. Fixes issue #54232 .

## Test plan

Open Cody. A new chat now has buttons. Clicking the buttons starts those
recipes.

---------

Co-authored-by: Beatrix <beatrix@sourcegraph.com>
This commit is contained in:
Dominic Cooney 2023-06-29 21:33:18 +09:00 committed by GitHub
parent 9e78e1c4cd
commit a7a1db2bb6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 73 additions and 3 deletions

View File

@ -3,9 +3,16 @@ import { Message } from '../../sourcegraph-api'
import { TranscriptJSON } from '.'
export interface ChatButton {
label: string
action: string
onClick: (action: string) => void
}
export interface ChatMessage extends Message {
displayText?: string
contextFiles?: ContextFile[]
buttons?: ChatButton[]
}
export interface InteractionMessage extends Message {

View File

@ -3,7 +3,7 @@ import React, { useCallback, useMemo, useState } from 'react'
import classNames from 'classnames'
import { ChatContextStatus } from '@sourcegraph/cody-shared/src/chat/context'
import { ChatMessage } from '@sourcegraph/cody-shared/src/chat/transcript/messages'
import { ChatButton, ChatMessage } from '@sourcegraph/cody-shared/src/chat/transcript/messages'
import { isDefined } from '@sourcegraph/common'
import { FileLinkProps } from './chat/ContextFiles'
@ -32,6 +32,7 @@ interface ChatProps extends ChatClassNames {
fileLinkComponent: React.FunctionComponent<FileLinkProps>
helpMarkdown?: string
afterMarkdown?: string
gettingStartedButtons?: ChatButton[]
className?: string
EditButtonContainer?: React.FunctionComponent<EditButtonProps>
editButtonOnSubmit?: (text: string) => void
@ -46,6 +47,7 @@ interface ChatProps extends ChatClassNames {
abortMessageInProgressComponent?: React.FunctionComponent<{ onAbortMessageInProgress: () => void }>
onAbortMessageInProgress?: () => void
isCodyEnabled: boolean
ChatButtonComponent?: React.FunctionComponent<ChatButtonProps>
}
interface ChatClassNames extends TranscriptItemClassNames {
@ -54,6 +56,12 @@ interface ChatClassNames extends TranscriptItemClassNames {
chatInputClassName?: string
}
export interface ChatButtonProps {
label: string
action: string
onClick: (action: string) => void
}
export interface ChatUITextAreaProps {
className: string
rows: number
@ -113,6 +121,7 @@ export const Chat: React.FunctionComponent<ChatProps> = ({
fileLinkComponent,
helpMarkdown,
afterMarkdown,
gettingStartedButtons,
className,
codeBlocksCopyButtonClassName,
codeBlocksInsertButtonClassName,
@ -138,6 +147,7 @@ export const Chat: React.FunctionComponent<ChatProps> = ({
abortMessageInProgressComponent: AbortMessageInProgressButton,
onAbortMessageInProgress = () => {},
isCodyEnabled,
ChatButtonComponent,
}) => {
const [inputRows, setInputRows] = useState(5)
const [historyIndex, setHistoryIndex] = useState(inputHistory.length)
@ -230,10 +240,11 @@ export const Chat: React.FunctionComponent<ChatProps> = ({
{
speaker: 'assistant',
displayText: welcomeText({ helpMarkdown, afterMarkdown }),
buttons: gettingStartedButtons,
},
...transcript,
],
[helpMarkdown, afterMarkdown, transcript]
[helpMarkdown, afterMarkdown, gettingStartedButtons, transcript]
)
return (
@ -268,6 +279,7 @@ export const Chat: React.FunctionComponent<ChatProps> = ({
copyButtonOnSubmit={copyButtonOnSubmit}
submitButtonComponent={SubmitButton}
chatInputClassName={chatInputClassName}
ChatButtonComponent={ChatButtonComponent}
/>
)}

View File

@ -5,6 +5,7 @@ import classNames from 'classnames'
import { ChatMessage } from '@sourcegraph/cody-shared/src/chat/transcript/messages'
import {
ChatButtonProps,
ChatUITextAreaProps,
EditButtonProps,
FeedbackButtonsProps,
@ -32,6 +33,7 @@ export const Transcript: React.FunctionComponent<
feedbackButtonsOnSubmit?: (text: string) => void
copyButtonOnSubmit?: CopyButtonProps['copyButtonOnSubmit']
submitButtonComponent?: React.FunctionComponent<ChatUISubmitButtonProps>
ChatButtonComponent?: React.FunctionComponent<ChatButtonProps>
} & TranscriptItemClassNames
> = React.memo(function TranscriptContent({
transcript,
@ -54,6 +56,7 @@ export const Transcript: React.FunctionComponent<
copyButtonOnSubmit,
submitButtonComponent,
chatInputClassName,
ChatButtonComponent,
}) {
const transcriptContainerRef = useRef<HTMLDivElement>(null)
useEffect(() => {
@ -116,6 +119,7 @@ export const Transcript: React.FunctionComponent<
showFeedbackButtons={index > 0 && transcript.length - index === 1}
submitButtonComponent={submitButtonComponent}
chatInputClassName={chatInputClassName}
ChatButtonComponent={ChatButtonComponent}
/>
)
)}
@ -136,6 +140,7 @@ export const Transcript: React.FunctionComponent<
copyButtonOnSubmit={copyButtonOnSubmit}
submitButtonComponent={submitButtonComponent}
chatInputClassName={chatInputClassName}
ChatButtonComponent={ChatButtonComponent}
/>
)}
</div>

View File

@ -10,6 +10,7 @@ import {
FeedbackButtonsProps,
CopyButtonProps,
ChatUISubmitButtonProps,
ChatButtonProps,
} from '../Chat'
import { BlinkingCursor } from './BlinkingCursor'
@ -52,6 +53,7 @@ export const TranscriptItem: React.FunctionComponent<
submitButtonComponent?: React.FunctionComponent<ChatUISubmitButtonProps>
abortMessageInProgressComponent?: React.FunctionComponent<{ onAbortMessageInProgress: () => void }>
onAbortMessageInProgress?: () => void
ChatButtonComponent?: React.FunctionComponent<ChatButtonProps>
} & TranscriptItemClassNames
> = React.memo(function TranscriptItemContent({
message,
@ -75,6 +77,7 @@ export const TranscriptItem: React.FunctionComponent<
copyButtonOnSubmit,
submitButtonComponent: SubmitButton,
chatInputClassName,
ChatButtonComponent,
}) {
const [formInput, setFormInput] = useState<string>(message.displayText ?? '')
const textarea =
@ -168,6 +171,9 @@ export const TranscriptItem: React.FunctionComponent<
<BlinkingCursor />
) : null}
</div>
{message.buttons?.length && ChatButtonComponent && (
<div className={styles.actions}>{message.buttons.map(ChatButtonComponent)}</div>
)}
{showFeedbackButtons &&
FeedbackButtonsContainer &&
feedbackButtonsOnSubmit &&

View File

@ -299,6 +299,18 @@ export class ChatViewProvider implements vscode.WebviewViewProvider, vscode.Disp
}
break
}
case 'chat-button': {
switch (message.action) {
case 'explain-code-high-level':
case 'find-code-smells':
case 'generate-unit-test':
void this.executeRecipe(message.action)
break
default:
break
}
break
}
default:
this.sendErrorToWebview('Invalid request type from Webview')
}

View File

@ -24,6 +24,7 @@ export type WebviewMessage =
| { command: 'insert'; text: string }
| { command: 'auth'; type: 'signin' | 'signout' | 'support' | 'app' | 'callback'; endpoint?: string }
| { command: 'abort' }
| { command: 'chat-button'; action: string }
/**
* A message sent from the extension host to the webview.

View File

@ -97,6 +97,11 @@ body[data-vscode-theme-kind='vscode-high-contrast'] .transcript-item:not(.human-
color: var(--vscode-problemsWarningIcon-foreground);
}
.chat-button {
margin-top: 0.5rem;
padding: 0.25rem;
}
.feedback-buttons {
display: flex;
flex-direction: row;

View File

@ -6,6 +6,7 @@ import classNames from 'classnames'
import { ChatContextStatus } from '@sourcegraph/cody-shared/src/chat/context'
import { ChatMessage } from '@sourcegraph/cody-shared/src/chat/transcript/messages'
import {
ChatButtonProps,
Chat as ChatUI,
ChatUISubmitButtonProps,
ChatUISuggestionButtonProps,
@ -89,6 +90,13 @@ export const Chat: React.FunctionComponent<React.PropsWithChildren<ChatboxProps>
[vscodeAPI]
)
const onChatButtonClick = useCallback(
(which: string) => {
vscodeAPI.postMessage({ command: 'chat-button', action: which })
},
[vscodeAPI]
)
return (
<ChatUI
messageInProgress={messageInProgress}
@ -128,7 +136,15 @@ export const Chat: React.FunctionComponent<React.PropsWithChildren<ChatboxProps>
// down here to render cody is disabled on the instance nicely.
isCodyEnabled={true}
codyNotEnabledNotice={undefined}
helpMarkdown="See [Getting Started](command:cody.welcome) for help and tips."
helpMarkdown="See [Getting Started](command:cody.welcome) for help and tips.
To get started, select some code and run one of Cody's recipes:"
gettingStartedButtons={[
{ label: 'Explain code (high level)', action: 'explain-code-high-level', onClick: onChatButtonClick },
{ label: 'Smell code', action: 'find-code-smells', onClick: onChatButtonClick },
{ label: 'Generate a unit test', action: 'generate-unit-test', onClick: onChatButtonClick },
]}
ChatButtonComponent={ChatButton}
/>
)
}
@ -149,6 +165,12 @@ const AbortMessageInProgress: React.FunctionComponent<AbortMessageInProgressProp
</div>
)
const ChatButton: React.FunctionComponent<ChatButtonProps> = ({ label, action, onClick }) => (
<VSCodeButton type="button" onClick={() => onClick(action)} className={styles.chatButton}>
{label}
</VSCodeButton>
)
const TextArea: React.FunctionComponent<ChatUITextAreaProps> = ({
className,
rows,