Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Chat: Display Enhanced Context settings on first chat #3547

Merged
merged 22 commits into from
Mar 26, 2024
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 25 additions & 2 deletions vscode/test/e2e/chat-atFile.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,15 @@ test.extend<ExpectedEvents>({
await page.getByRole('button', { name: 'New Chat', exact: true }).click()

const chatPanelFrame = page.frameLocator('iframe.webview').last().frameLocator('iframe')
const enhancedContextCheckbox = chatPanelFrame.locator('#enhanced-context-checkbox')
await expect(enhancedContextCheckbox).toBeFocused()
await page.keyboard.press('Escape')
await expect(enhancedContextCheckbox).not.toBeVisible()

const chatInput = chatPanelFrame.getByRole('textbox', { name: 'Chat message' })
await chatInput.click()
await chatInput.dblclick()

await chatInput.focus()
await page.keyboard.type('@')
await expect(
chatPanelFrame.getByRole('heading', {
Expand Down Expand Up @@ -199,6 +205,11 @@ test('editing a chat message with @-mention', async ({ page, sidebar }) => {
await sidebarSignin(page, sidebar)
await page.getByRole('button', { name: 'New Chat', exact: true }).click()
const chatPanelFrame = page.frameLocator('iframe.webview').last().frameLocator('iframe')
const enhancedContextCheckbox = chatPanelFrame.locator('#enhanced-context-checkbox')
await expect(enhancedContextCheckbox).toBeFocused()
await page.keyboard.press('Escape')
await expect(enhancedContextCheckbox).not.toBeVisible()

const chatInput = chatPanelFrame.getByRole('textbox', { name: 'Chat message' })

// Send a message with an @-mention.
Expand Down Expand Up @@ -237,8 +248,12 @@ test('pressing Enter with @-mention menu open selects item, does not submit mess
await sidebarSignin(page, sidebar)
await page.getByRole('button', { name: 'New Chat', exact: true }).click()
const chatPanelFrame = page.frameLocator('iframe.webview').last().frameLocator('iframe')
const chatInput = chatPanelFrame.getByRole('textbox', { name: 'Chat message' })
const enhancedContextCheckbox = chatPanelFrame.locator('#enhanced-context-checkbox')
await expect(enhancedContextCheckbox).toBeFocused()
await page.keyboard.press('Escape')
await expect(enhancedContextCheckbox).not.toBeVisible()

const chatInput = chatPanelFrame.getByRole('textbox', { name: 'Chat message' })
await chatInput.fill('Explain @index.htm')
await expect(chatPanelFrame.getByRole('option', { name: 'index.html' })).toBeVisible()
await chatInput.press('Enter')
Expand Down Expand Up @@ -275,6 +290,10 @@ test('@-mention file range', async ({ page, sidebar }) => {
// Open chat.
await page.getByRole('button', { name: 'New Chat', exact: true }).click()
const chatPanelFrame = page.frameLocator('iframe.webview').last().frameLocator('iframe')
const enhancedContextCheckbox = chatPanelFrame.locator('#enhanced-context-checkbox')
await expect(enhancedContextCheckbox).toBeFocused()
await page.keyboard.press('Escape')
await expect(enhancedContextCheckbox).not.toBeVisible()
const chatInput = chatPanelFrame.getByRole('textbox', { name: 'Chat message' })

// Type a file with range.
Expand Down Expand Up @@ -305,6 +324,10 @@ test.extend<ExpectedEvents>({
// Open chat.
await page.getByRole('button', { name: 'New Chat', exact: true }).click()
const chatPanelFrame = page.frameLocator('iframe.webview').last().frameLocator('iframe')
const enhancedContextCheckbox = chatPanelFrame.locator('#enhanced-context-checkbox')
await expect(enhancedContextCheckbox).toBeFocused()
await page.keyboard.press('Escape')
await expect(enhancedContextCheckbox).not.toBeVisible()
const chatInput = chatPanelFrame.getByRole('textbox', { name: 'Chat message' })

// Open the buzz.ts file so that VS Code starts to populate symbols.
Expand Down
5 changes: 1 addition & 4 deletions vscode/test/e2e/context-settings.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,7 @@ test.extend<ExpectedEvents>({

await sidebarSignin(page, sidebar)
const chatFrame = await newChat(page)
const contextSettingsButton = chatFrame.getByTitle('Configure Enhanced Context')
await contextSettingsButton.focus()

await page.keyboard.press('Space')
// Opening the enhanced context settings should focus the checkbox for toggling it.
const enhancedContextCheckbox = chatFrame.locator('#enhanced-context-checkbox')
await expect(enhancedContextCheckbox).toBeFocused()
Expand All @@ -42,6 +39,7 @@ test.extend<ExpectedEvents>({
// Closing the enhanced context settings should close the dialog...
await expect(enhancedContextCheckbox).not.toBeVisible()
// ... and focus the button which re-opens it.
const contextSettingsButton = chatFrame.getByTitle('Configure Enhanced Context')
await expect(contextSettingsButton.and(page.locator(':focus'))).toBeVisible()
})

Expand Down Expand Up @@ -78,7 +76,6 @@ test('enterprise context selector can pick repos', async ({ page, sidebar, serve
const chatFrame = await newChat(page)

// Because there are no repositories in the workspace, none should be selected by default.
await chatFrame.getByTitle('Configure Enhanced Context').click()
await expect(chatFrame.getByText('No repositories selected')).toBeVisible()

// Choosing a repository should open the repository picker.
Expand Down
7 changes: 1 addition & 6 deletions vscode/test/e2e/local-embeddings.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,8 @@ test.extend<helpers.WorkspaceDirectory>({
})('non-git repositories should explain lack of embeddings', async ({ page, sidebar }) => {
await openFile(page, 'main.c')
await sidebarSignin(page, sidebar)
// The Enhanced Context settings is opened on first chat by default
const chatFrame = await newChat(page)
const enhancedContextButton = chatFrame.getByTitle('Configure Enhanced Context')
await enhancedContextButton.click()

// Embeddings is visible at first as cody-engine starts...
await expect(chatFrame.getByText('Embeddings')).toBeVisible()
Expand All @@ -109,8 +108,6 @@ test('git repositories without a remote should explain the issue', async ({ page
await openFile(page, 'main.c')
await sidebarSignin(page, sidebar)
const chatFrame = await newChat(page)
const enhancedContextButton = chatFrame.getByTitle('Configure Enhanced Context')
await enhancedContextButton.click()
await expect(chatFrame.locator('.codicon-circle-slash')).toBeVisible({
timeout: 60000,
})
Expand Down Expand Up @@ -141,8 +138,6 @@ test
await openFile(page, 'main.c')
await sidebarSignin(page, sidebar)
const chatFrame = await newChat(page)
const enhancedContextButton = chatFrame.getByTitle('Configure Enhanced Context')
await enhancedContextButton.click()

const enableEmbeddingsButton = chatFrame.getByText('Enable Embeddings')
// This may take a while, we download and start cody-engine
Expand Down
6 changes: 3 additions & 3 deletions vscode/webviews/Chat.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,8 @@ body[data-vscode-theme-kind='vscode-light'] .transcript-item pre > code {
.context-button {
color: var(--button-primary-background);
position: absolute;
right: 0.3rem;
bottom: 0.3rem;
right: 0.3125rem;
bottom: 0.3125rem;
fill: currentColor;
background: none;
border: none;
Expand Down Expand Up @@ -205,7 +205,7 @@ body[data-vscode-theme-kind='vscode-light'] .transcript-item pre > code {
height: 2rem;
width: 2rem;

margin-left: 0.5rem;
margin-left: 0.3125rem;

background: var(--vscode-button-background);
color: var(--vscode-button-foreground);
Expand Down
19 changes: 13 additions & 6 deletions vscode/webviews/Chat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ export const Chat: React.FunctionComponent<React.PropsWithChildren<ChatboxProps>
}) => {
const [messageBeingEdited, setMessageBeingEdited] = useState<number | undefined>(undefined)

// Display the enhanced context settings on first chats
const [isEnhancedContextOpen, setIsEnhancedContextOpen] = useState(false)

const editorRef = useRef<PromptEditorRefAPI>(null)
const setEditorState = useCallback((state: SerializedPromptEditorState | null) => {
editorRef.current?.setEditorState(state)
Expand Down Expand Up @@ -188,9 +191,6 @@ export const Chat: React.FunctionComponent<React.PropsWithChildren<ChatboxProps>

const setInputFocus = useCallback((focus: boolean): void => {
editorRef.current?.setFocus(focus)
if (focus) {
setIsEnhancedContextOpen(false)
}
}, [])

// When New Chat Mode is enabled, all non-edit questions will be asked in a new chat session
Expand Down Expand Up @@ -405,8 +405,6 @@ export const Chat: React.FunctionComponent<React.PropsWithChildren<ChatboxProps>
]
)

const [isEnhancedContextOpen, setIsEnhancedContextOpen] = useState(false)

// Focus the textarea when the webview (re)gains focus (unless there is text selected or a modal
// is open). This makes it so that the user can immediately start typing to Cody after invoking
// `Cody: Focus on Chat View` with the keyboard.
Expand Down Expand Up @@ -442,6 +440,14 @@ export const Chat: React.FunctionComponent<React.PropsWithChildren<ChatboxProps>
[chatIDHistory, postMessage]
)

// biome-ignore lint/correctness/useExhaustiveDependencies: We don't want to re-run this effect.
const onEnhancedContextTogglerClick = useCallback((focus: boolean) => {
if (!isEnhancedContextOpen && !focus) {
setInputFocus(true)
}
setIsEnhancedContextOpen(focus)
}, [])

const [isEditorFocused, setIsEditorFocused] = useState(false)

const isNewChat = transcript.length === 0
Expand Down Expand Up @@ -517,8 +523,9 @@ export const Chat: React.FunctionComponent<React.PropsWithChildren<ChatboxProps>
<div className={styles.contextButton}>
<EnhancedContextSettings
isOpen={isEnhancedContextOpen}
setOpen={setIsEnhancedContextOpen}
setOpen={onEnhancedContextTogglerClick}
presentationMode={userInfo.isDotComUser ? 'consumer' : 'enterprise'}
isFirstChat={transcript.length < 1}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I only spotted this logic. This is more like isEmptyChat which isn't what we want here. We want it to appear on the first chat that's created for first time users, not every time a chat is created.

/>
</div>
</div>
Expand Down
75 changes: 32 additions & 43 deletions vscode/webviews/Components/EnhancedContextSettings.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -182,61 +182,50 @@
outline-offset: 2px;
}

.settings-btn {
.settings-btns {
position: relative;
display: flex;
color: var(--vscode-editor-foreground);
border: 1px solid transparent;
border: 1px solid var(--vscode-button-border, transparent);
border-radius: 3px;
position: relative;
}

.settings-btn i {
.settings-indicator {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}

.settings-btn {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
border-left-width: 0;
}

/* Active borders on keyboard focus but not mousedown (:focus-visible doesn't seem to work) */

.settings-indicator:focus:not(:active) {
border-color: var(--vscode-inputOption-activeBorder);
}

.settings-btn:focus:not(:active) {
border-color: var(--vscode-inputOption-activeBorder);
border-left-width: 1px;
}

.settings-indicator:has(+ .settings-btn:focus:not(:active)) {
border-right-width: 0;
}

.settings-indicator img {
width: 16px;
height: 16px;
}

.settings-btn-active {
.settings-indicator-active {
color: var(--vscode-inputOption-activeForeground);
border-color: var(--vscode-inputOption-activeBorder);
background-color: var(--vscode-inputOption-activeBackground);
}

.glowy-dot {
width: 6px;
height: 6px;
border-radius: 100%;
background-color: var(--vscode-inputOption-activeBorder);
position: absolute;
top: -3px;
left: -3px;
z-index: 2;
}

.glowy-dot::after {
content: '';
box-sizing: border-box;
width: 6px;
height: 6px;
border-radius: 100%;
border: 0.75px solid var(--vscode-inputOption-activeForeground);
position: absolute;
top: 0;
left: 0;
z-index: 1;
animation: 2.5s linear infinite glowy;
}

@keyframes glowy {
50% {
transform: scale(300%);
opacity: 0;
}

100% {
transform: scale(300%);
opacity: 0;
}
}

.popup .hint {
font-size: 11px;
line-height: 1.3;
Expand Down
4 changes: 4 additions & 0 deletions vscode/webviews/Components/EnhancedContextSettings.story.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ export const SingleTile: StoryObj<typeof EnhancedContextSettings | SingleTileArg
isOpen={isOpen}
setOpen={() => setIsOpen(!isOpen)}
presentationMode={args.presentationMode}
isFirstChat={false}
/>
</div>
</EnhancedContextEventHandlers.Provider>
Expand Down Expand Up @@ -171,6 +172,7 @@ export const ConsumerMultipleProviders: StoryObj<typeof EnhancedContextSettings>
isOpen={isOpen}
setOpen={() => setIsOpen(!isOpen)}
presentationMode={EnhancedContextPresentationMode.Consumer}
isFirstChat={false}
/>
</div>
</EnhancedContextContext.Provider>
Expand Down Expand Up @@ -198,6 +200,7 @@ export const EnterpriseNoRepositories: StoryObj<typeof EnhancedContextSettings>
presentationMode={EnhancedContextPresentationMode.Enterprise}
isOpen={isOpen}
setOpen={() => setIsOpen(!isOpen)}
isFirstChat={false}
/>
</div>
</EnhancedContextContext.Provider>
Expand Down Expand Up @@ -262,6 +265,7 @@ export const EnterpriseMultipleRepositories: StoryObj<typeof EnhancedContextSett
presentationMode={EnhancedContextPresentationMode.Enterprise}
isOpen={isOpen}
setOpen={() => setIsOpen(!isOpen)}
isFirstChat={false}
/>
</div>
</EnhancedContextContext.Provider>
Expand Down
Loading
Loading