mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-13 21:05:19 +00:00
feat(core): support chat panel chips and suggest current doc for embedding (#9747)
Support issue [BS-2347](https://linear.app/affine-design/issue/BS-2347). ## What Changed? - Add chat panel components - `chat-panel-chips` - `chat-panel-doc-chip` - `chat-panel-file-chip` - `chat-panel-chip` - Add `chips` and `docs` field in `ChatContextValue` - Add `extractMarkdownFromDoc` function to extract markdown content of a doc - Add e2e test Click a candidate card to add it into AI chat context: <div class='graphite__hidden'> <div>🎥 Video uploaded on Graphite:</div> <a href="https://app.graphite.dev/media/video/sJGviKxfE3Ap685cl5bj/4e6b11ef-f993-4e6a-9f40-b2826af1990c.mov"> <img src="https://app.graphite.dev/api/v1/graphite/video/thumbnail/sJGviKxfE3Ap685cl5bj/4e6b11ef-f993-4e6a-9f40-b2826af1990c.mov"> </a> </div> <video src="https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/sJGviKxfE3Ap685cl5bj/4e6b11ef-f993-4e6a-9f40-b2826af1990c.mov">录屏2025-01-17 01.02.04.mov</video>
This commit is contained in:
@@ -86,6 +86,13 @@ const typeChat = async (page: Page, content: string) => {
|
||||
await page.keyboard.type(content);
|
||||
};
|
||||
|
||||
const typeChatSequentially = async (page: Page, content: string) => {
|
||||
const input = await page.locator('chat-panel-input textarea').nth(0);
|
||||
await input.pressSequentially(content, {
|
||||
delay: 50,
|
||||
});
|
||||
};
|
||||
|
||||
const makeChat = async (page: Page, content: string) => {
|
||||
await openChat(page);
|
||||
await typeChat(page, content);
|
||||
@@ -210,8 +217,8 @@ test.describe('chat panel', () => {
|
||||
await createLocalWorkspace({ name: 'test' }, page);
|
||||
await clickNewPageButton(page);
|
||||
const sendButton = await page.getByTestId('chat-panel-send');
|
||||
// oxlint-disable-next-line unicorn/prefer-dom-node-dataset
|
||||
await openChat(page);
|
||||
// oxlint-disable-next-line unicorn/prefer-dom-node-dataset
|
||||
expect(await sendButton.getAttribute('aria-disabled')).toBe('true');
|
||||
await typeChat(page, 'hello');
|
||||
// oxlint-disable-next-line unicorn/prefer-dom-node-dataset
|
||||
@@ -399,11 +406,12 @@ test.describe('chat panel', () => {
|
||||
await page.getByTestId('modal-close-button').click();
|
||||
await openChat(page);
|
||||
await page.getByTestId('chat-network-search').click();
|
||||
await makeChat(page, 'hello');
|
||||
await typeChatSequentially(page, 'What is the weather in Shanghai today?');
|
||||
await page.keyboard.press('Enter');
|
||||
let history = await collectChat(page);
|
||||
expect(history[0]).toEqual({
|
||||
name: 'You',
|
||||
content: 'hello',
|
||||
content: 'What is the weather in Shanghai today?',
|
||||
});
|
||||
expect(history[1].name).toBe('AFFiNE AI');
|
||||
expect(
|
||||
@@ -413,11 +421,12 @@ test.describe('chat panel', () => {
|
||||
await clearChat(page);
|
||||
expect((await collectChat(page)).length).toBe(0);
|
||||
await page.getByTestId('chat-network-search').click();
|
||||
await makeChat(page, 'hello');
|
||||
await typeChatSequentially(page, 'What is the weather in Shanghai today?');
|
||||
await page.keyboard.press('Enter');
|
||||
history = await collectChat(page);
|
||||
expect(history[0]).toEqual({
|
||||
name: 'You',
|
||||
content: 'hello',
|
||||
content: 'What is the weather in Shanghai today?',
|
||||
});
|
||||
expect(history[1].name).toBe('AFFiNE AI');
|
||||
expect(await page.locator('chat-panel affine-link').count()).toBe(0);
|
||||
@@ -704,3 +713,64 @@ test.describe('chat with block', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('chat with doc', () => {
|
||||
let user: {
|
||||
email: string;
|
||||
password: string;
|
||||
};
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
user = await getUser();
|
||||
await loginUser(page, user);
|
||||
});
|
||||
|
||||
test('can chat with current doc', async ({ page }) => {
|
||||
await page.reload();
|
||||
await clickSideBarAllPageButton(page);
|
||||
await page.waitForTimeout(200);
|
||||
await createLocalWorkspace({ name: 'test' }, page);
|
||||
await clickNewPageButton(page);
|
||||
|
||||
await openChat(page);
|
||||
const chipTitle = await page.getByTestId('chat-panel-chip-title');
|
||||
expect(await chipTitle.textContent()).toBe('Untitled');
|
||||
|
||||
const editorTitle = await page.locator('doc-title .inline-editor').nth(0);
|
||||
await editorTitle.pressSequentially('AFFiNE AI', {
|
||||
delay: 50,
|
||||
});
|
||||
await page.keyboard.press('Enter', { delay: 50 });
|
||||
// Wait for the editor to be ready and focused
|
||||
await page.waitForSelector('page-editor affine-paragraph .inline-editor');
|
||||
const richText = await page
|
||||
.locator('page-editor affine-paragraph .inline-editor')
|
||||
.nth(0);
|
||||
await richText.click(); // Ensure proper focus
|
||||
await page.keyboard.type(
|
||||
'AFFiNE AI is an assistant with the ability to create well-structured outlines for any given content.',
|
||||
{
|
||||
delay: 50,
|
||||
}
|
||||
);
|
||||
|
||||
expect(await chipTitle.textContent()).toBe('AFFiNE AI');
|
||||
const chip = await page.getByTestId('chat-panel-chip');
|
||||
// oxlint-disable-next-line unicorn/prefer-dom-node-dataset
|
||||
expect(await chip.getAttribute('data-state')).toBe('candidate');
|
||||
await chip.click();
|
||||
// oxlint-disable-next-line unicorn/prefer-dom-node-dataset
|
||||
expect(await chip.getAttribute('data-state')).toBe('success');
|
||||
|
||||
await makeChat(page, 'summarize');
|
||||
const history = await collectChat(page);
|
||||
expect(history[0]).toEqual({
|
||||
name: 'You',
|
||||
content:
|
||||
'AFFiNE AI is an assistant with the ability to create well-structured outlines for any given content.\nsummarize',
|
||||
});
|
||||
expect(history[1].name).toBe('AFFiNE AI');
|
||||
await clearChat(page);
|
||||
expect((await collectChat(page)).length).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user