Cody Web: Update Cody Web to 0.3.6 [React version] (#64254)

Closes
https://linear.app/sourcegraph/issue/SRCH-720/new-chat-button-in-side-panel-view
Closes
https://linear.app/sourcegraph/issue/SRCH-808/chat-history-in-side-panel-view

This PR does a few things 
- Updates Cody Web to 0.3.6 (this includes improvements around mentions
UI, web url mention support, etc)
- Adds "create new chat" button to the sidebar cody chat UI
- Adds history chats UI to the sidebar cody chat UI (note that in GA we
will rely on the new Tabs UI, this history UI is just a temporal
solution for the Sourcegraph Aug release, in sep GA release it will be
improved)

## Test plan
- General manual checks on Cody Web.
- Check that you can create a new chat from the sidebar chat UI
- Check that you can select chats from the history panel from the
sidebar chat UI

<!-- REQUIRED; info at
https://docs-legacy.sourcegraph.com/dev/background-information/testing_principles
-->

## Changelog

<!-- OPTIONAL; info at
https://www.notion.so/sourcegraph/Writing-a-changelog-entry-dd997f411d524caabf0d8d38a24a878c
-->
This commit is contained in:
Vova Kulikov 2024-08-02 18:04:52 -03:00 committed by GitHub
parent c643c224ab
commit 4caad25380
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 165 additions and 20 deletions

View File

@ -25,6 +25,7 @@ export interface ChatExportResult {
interface ChatHistoryListProps {
chats: ChatExportResult[]
isSelectedChat: (chat: ChatExportResult) => boolean
withCreationButton?: boolean
className?: string
onChatSelect: (chat: ChatExportResult) => void
onChatDelete: (chat: ChatExportResult) => void
@ -32,7 +33,15 @@ interface ChatHistoryListProps {
}
export const ChatHistoryList: FC<ChatHistoryListProps> = props => {
const { chats, isSelectedChat, className, onChatSelect, onChatDelete, onChatCreate } = props
const {
chats,
isSelectedChat,
withCreationButton = true,
className,
onChatSelect,
onChatDelete,
onChatCreate,
} = props
const sortedChats = useMemo(() => {
try {
@ -65,12 +74,14 @@ export const ChatHistoryList: FC<ChatHistoryListProps> = props => {
onDelete={() => onChatDelete(chat)}
/>
))}
<footer className={styles.footer}>
<Button variant="primary" onClick={() => onChatCreate()} className="w-100">
Start new chat
<Icon aria-label="Add chat" svgPath={mdiPlus} />
</Button>
</footer>
{withCreationButton && (
<footer className={styles.footer}>
<Button variant="primary" onClick={() => onChatCreate()} className="w-100">
Start new chat
<Icon aria-label="Add chat" svgPath={mdiPlus} />
</Button>
</footer>
)}
</ul>
)
}

View File

@ -14,4 +14,22 @@
font-size: 0.875rem;
background-color: var(--input-bg);
border-bottom: 1px solid var(--border-color-2);
&--main {
display: flex;
align-items: center;
flex-shrink: 0;
gap: 0.5rem;
}
&--actions {
display: flex;
align-items: center;
gap: 0.5rem;
}
&--logo {
display: flex;
align-items: center;
}
}

View File

@ -1,10 +1,11 @@
import { Suspense, type FC } from 'react'
import { Suspense, type FC, useRef, useCallback, useState } from 'react'
import { mdiClose } from '@mdi/js'
import { mdiClose, mdiPlus, mdiArrowLeft, mdiHistory } from '@mdi/js'
import { CodyLogo } from '@sourcegraph/cody-ui'
import type { CodyWebChatContextClient } from '@sourcegraph/cody-web'
import { lazyComponent } from '@sourcegraph/shared/src/util/lazyComponent'
import { Alert, Button, H4, Icon, LoadingSpinner, ProductStatusBadge } from '@sourcegraph/wildcard'
import { Alert, Button, H4, Icon, LoadingSpinner, ProductStatusBadge, Tooltip } from '@sourcegraph/wildcard'
import styles from './NewCodySidebar.module.scss'
@ -25,16 +26,67 @@ interface NewCodySidebarProps {
export const NewCodySidebar: FC<NewCodySidebarProps> = props => {
const { repository, filePath, isAuthorized, onClose } = props
const [chatMode, setChatMode] = useState<'chat' | 'history'>('chat')
const codyClientRef = useRef<CodyWebChatContextClient>()
const handleShowHistory = (): void => {
setChatMode('history')
}
const handleShowChat = (): void => {
setChatMode('chat')
}
const handleCreateNewChat = async (): Promise<void> => {
if (codyClientRef.current) {
await codyClientRef.current.createNewChat()
setChatMode('chat')
}
}
const handleSelectChat = (): void => {
setChatMode('chat')
}
const handleClientCreation = useCallback((client: CodyWebChatContextClient): void => {
codyClientRef.current = client
}, [])
return (
<div className={styles.root}>
<div className={styles.header}>
<div className="d-flex flex-shrink-0 align-items-center">
<div className={styles.headerActions}>
{chatMode === 'history' && (
<Tooltip content="Go back to chat">
<Button variant="icon" aria-label="Create new chat" onClick={handleShowChat}>
<Icon aria-hidden={true} svgPath={mdiArrowLeft} />
</Button>
</Tooltip>
)}
{chatMode === 'chat' && (
<Tooltip content="Show chat history">
<Button variant="icon" aria-label="Show chat history" onClick={handleShowHistory}>
<Icon aria-hidden={true} svgPath={mdiHistory} />
</Button>
</Tooltip>
)}
<Tooltip content="Start a new chat">
<Button variant="icon" aria-label="Create new chat" onClick={handleCreateNewChat}>
<Icon aria-hidden={true} svgPath={mdiPlus} />
</Button>
</Tooltip>
</div>
<span className={styles.headerLogo}>
<CodyLogo />
Cody
<div className="ml-2">
<ProductStatusBadge status="beta" />
</div>
</div>
</span>
<Button variant="icon" aria-label="Close" onClick={onClose}>
<Icon aria-hidden={true} svgPath={mdiClose} />
</Button>
@ -48,7 +100,13 @@ export const NewCodySidebar: FC<NewCodySidebarProps> = props => {
</div>
}
>
<LazyCodySidebarWebChat filePath={filePath} repository={repository} />
<LazyCodySidebarWebChat
mode={chatMode}
filePath={filePath}
repository={repository}
onChatSelect={handleSelectChat}
onClientCreated={handleClientCreation}
/>
</Suspense>
)}

View File

@ -0,0 +1,9 @@
.chat-history {
padding: 0.5rem;
overflow: auto;
height: 100%;
}
.hidden {
display: none;
}

View File

@ -1,13 +1,23 @@
import { type FC, memo, useCallback, useMemo } from 'react'
import classNames from 'classnames'
import { useLocation } from 'react-router-dom'
import { CodyWebChatProvider, type InitialContext } from '@sourcegraph/cody-web'
import {
CodyWebChatProvider,
type InitialContext,
type CodyWebChatContextClient,
CodyWebHistory,
} from '@sourcegraph/cody-web'
import { SourcegraphURL } from '@sourcegraph/common'
import { useLocalStorage } from '@sourcegraph/wildcard'
import { Text, useLocalStorage } from '@sourcegraph/wildcard'
import { getTelemetrySourceClient } from '../../../telemetry'
import { ChatHistoryList } from '../../chat/new-chat/components/chat-history-list/ChatHistoryList'
import { ChatUi } from '../../chat/new-chat/components/chat-ui/ChatUi'
import { Skeleton } from '../../chat/new-chat/components/skeleton/Skeleton'
import styles from './NewCodySidebarWebChat.module.scss'
interface Repository {
id: string
@ -17,10 +27,13 @@ interface Repository {
interface NewCodySidebarWebChatProps {
filePath?: string
repository: Repository
mode: 'chat' | 'history'
onChatSelect?: () => void
onClientCreated?: (client: CodyWebChatContextClient) => void
}
export const NewCodySidebarWebChat: FC<NewCodySidebarWebChatProps> = memo(function CodyWebChat(props) {
const { filePath, repository } = props
const { filePath, repository, onChatSelect, onClientCreated, mode } = props
const location = useLocation()
const [contextToChatIds, setContextToChatIds] = useLocalStorage<Record<string, string>>(
@ -64,8 +77,40 @@ export const NewCodySidebarWebChat: FC<NewCodySidebarWebChatProps> = memo(functi
customHeaders={window.context.xhrHeaders}
telemetryClientName={getTelemetrySourceClient()}
onNewChatCreated={handleNewChatCreated}
onClientCreated={onClientCreated}
>
<ChatUi />
<ChatUi className={classNames({ [styles.hidden]: mode !== 'chat' })} />
<div className={classNames(styles.chatHistory, { [styles.hidden]: mode !== 'history' })}>
<CodyWebHistory>
{history => (
<div>
{history.loading && (
<>
<Skeleton />
<Skeleton />
<Skeleton />
</>
)}
{history.error && <Text>Error: {history.error.message}</Text>}
{!history.loading && !history.error && (
<ChatHistoryList
chats={history.chats}
isSelectedChat={history.isSelectedChat}
withCreationButton={false}
onChatSelect={chat => {
onChatSelect?.()
history.selectChat(chat)
}}
onChatDelete={history.deleteChat}
onChatCreate={history.createNewChat}
/>
)}
</div>
)}
</CodyWebHistory>
</div>
</CodyWebChatProvider>
)
})

View File

@ -309,7 +309,7 @@
"@reach/visually-hidden": "^0.16.0",
"@react-aria/live-announcer": "^3.1.0",
"@sentry/browser": "^7.8.1",
"@sourcegraph/cody-web": "^0.3.4",
"@sourcegraph/cody-web": "^0.3.6",
"@sourcegraph/extension-api-classes": "^1.1.0",
"@stripe/react-stripe-js": "^2.7.0",
"@stripe/stripe-js": "^3.3.0",

View File

@ -146,8 +146,8 @@ importers:
specifier: ^7.8.1
version: 7.8.1
'@sourcegraph/cody-web':
specifier: ^0.3.4
version: 0.3.4
specifier: ^0.3.6
version: 0.3.6
'@sourcegraph/extension-api-classes':
specifier: ^1.1.0
version: 1.1.0(sourcegraph@client+extension-api)
@ -9724,6 +9724,10 @@ packages:
resolution: {integrity: sha512-g9wXZcQTrPBrxE3Pm6A3ip57diIEQAwBEqy16gCoN7DIwI9YYsP47svXxwgJmF6JS0XEA33Ah9k3RbRfoMadKg==}
dev: false
/@sourcegraph/cody-web@0.3.6:
resolution: {integrity: sha512-xFOjcdv7pITMg6AIN3VJxzXCdKk4nszQkuobbsqJeJwIA4sRljU+Zv6qLstzY5kQgCFu8KoYs9aAofAg3KFD0A==}
dev: false
/@sourcegraph/eslint-config@0.37.1(@testing-library/dom@8.13.0)(eslint@8.57.0)(typescript@5.4.2):
resolution: {integrity: sha512-3G0d3OUgifaADfd27Bdk/kSpECj21BfQ6srbYMY/HVWOv/N8AVuFWNwUMT4Y4slt026RXO5XcwoZhfnaskr5hQ==}
dependencies: