feat(chat): show playful teacher introductions on hover
This commit is contained in:
1 parent
b26e25c9ed
commit
772da4f8e4
16 files changed
+433
-62
No files matched your search
@@ -454,10 +454,10 @@ async function installCodingFirstChatHost(
|
||||
...overrides,
|
||||
});
|
||||
|
||||
const teacherDefinition = {schema_version:1,teacher_id:'coding-teacher',name:'代码智能体',description:'',avatar_id:'avatar-01',welcome_message:'一起理解代码',suggested_questions:['解释当前代码'],system_prompt:'教学',skills:[],model:{model_id:'model-a',reasoning_choice:{mode:'default'}},limits:{max_input_tokens:8000,max_output_tokens:1500}};
|
||||
const teacherDefinition = {schema_version:1,teacher_id:'coding-teacher',name:'代码智能体',description:'一起发现代码里的小奥秘,把想法变成作品。',avatar_id:'avatar-01',welcome_message:'一起理解代码',suggested_questions:['解释当前代码'],system_prompt:'教学',skills:[],model:{model_id:'model-a',reasoning_choice:{mode:'default'}},limits:{max_input_tokens:8000,max_output_tokens:1500}};
|
||||
const teachers = [
|
||||
{ teacher_id: 'teacher-code', version: 1, is_default: true, definition: { ...teacherDefinition, config_id: 'teacher-code', runtime: 'yuxi', system_prompt: '', yuxi: { agent_slug: 'code', agent_version: 2 } } },
|
||||
{ teacher_id: 'teacher-algorithm', version: 9, is_default: false, definition: { ...teacherDefinition, config_id: 'teacher-algorithm', runtime: 'yuxi', name: '朋友', system_prompt: '', yuxi: { agent_slug: 'algorithm', agent_version: 4 } } },
|
||||
{ teacher_id: 'teacher-algorithm', version: 9, is_default: false, definition: { ...teacherDefinition, config_id: 'teacher-algorithm', runtime: 'yuxi', name: '朋友', description: '陪你听听玩家的想法,让作品更有趣。', system_prompt: '', yuxi: { agent_slug: 'algorithm', agent_version: 4 } } },
|
||||
];
|
||||
const consultationTopics: Record<string, Record<string, unknown>> = {};
|
||||
ipcMain.removeHandler('hostapi:fetch');
|
||||
@@ -1786,8 +1786,18 @@ test('project consultations preserve student drafts and switch between work and
|
||||
await expect(composer).toBeHidden();
|
||||
await page.getByRole('tab',{name:'操作对话',exact:true}).click();
|
||||
await expect(composer).toHaveValue('保留我的草稿');
|
||||
await page.getByRole('button',{name:'与代码智能体聊天',exact:true}).hover();
|
||||
await expect(page.getByTestId('teacher-introduction')).toContainText('一起发现代码里的小奥秘,把想法变成作品。');
|
||||
await page.getByRole('button',{name:'与朋友聊天',exact:true}).hover();
|
||||
await expect(page.getByTestId('teacher-introduction')).toHaveCount(1);
|
||||
await expect(page.getByTestId('teacher-introduction')).toContainText('陪你听听玩家的想法,让作品更有趣。');
|
||||
await expect(page.getByTestId('teacher-chat-panel')).toHaveCount(0);
|
||||
expect((await readState(electronApp)).captured.filter(item => /\/(messages|teacher-check-in)$/.test(item.path) && item.method === 'POST')).toHaveLength(0);
|
||||
await page.getByRole('button',{name:'与朋友聊天',exact:true}).click();
|
||||
const teacher=page.getByTestId('teacher-chat-panel');
|
||||
await expect(page.getByTestId('teacher-introduction')).toHaveCount(0);
|
||||
await expect(teacher.locator('header').getByRole('button')).toHaveCount(0);
|
||||
await expect(teacher.getByText('陪你听听玩家的想法,让作品更有趣。')).toHaveCount(0);
|
||||
await expect(page.getByRole('tab',{name:'作品',exact:true})).toHaveAttribute('aria-selected','true');
|
||||
await expect(page.getByRole('tabpanel',{name:'作品',exact:true})).toBeVisible();
|
||||
await expect(composer).toBeHidden();
|
||||
@@ -1819,7 +1829,9 @@ test('project consultations preserve student drafts and switch between work and
|
||||
await expect(teacher.getByRole('textbox',{name:'向智能体提问'})).toHaveValue('还没说完的困惑');
|
||||
await page.getByRole('tab',{name:'操作对话',exact:true}).click();
|
||||
await expect(composer).toBeVisible();
|
||||
await teacher.getByRole('button',{name:'关闭智能体'}).click();
|
||||
await page.getByRole('button',{name:'与朋友聊天',exact:true}).click();
|
||||
await expect(teacher).toHaveCount(0);
|
||||
await expect(page.getByRole('button',{name:'与朋友聊天',exact:true})).toBeFocused();
|
||||
await page.getByRole('button', { name: '与代码智能体聊天' }).click();
|
||||
await expect(teacher.getByRole('heading', { name: '代码智能体' })).toBeVisible();
|
||||
await expect(teacher.getByRole('textbox', { name: '向智能体提问' })).toHaveValue('');
|
||||
@@ -1827,6 +1839,11 @@ test('project consultations preserve student drafts and switch between work and
|
||||
await page.getByRole('button',{name:'与朋友聊天',exact:true}).click();
|
||||
await expect(teacher.getByRole('textbox',{name:'向智能体提问'})).toHaveValue('还没说完的困惑');
|
||||
await expect(teacher.getByText('先理解状态如何随点击变化,再修改代码。')).toBeVisible();
|
||||
// Moving inside the clicked entry must not schedule its introduction again.
|
||||
const friendEntry = (await page.getByRole('button', { name: '与朋友聊天', exact: true }).boundingBox())!;
|
||||
await page.mouse.move(friendEntry.x + friendEntry.width / 2 + 2, friendEntry.y + friendEntry.height / 2);
|
||||
await page.waitForTimeout(300);
|
||||
await expect(page.getByTestId('teacher-introduction')).toHaveCount(0);
|
||||
const requests=(await readState(electronApp)).captured;
|
||||
expect(requests.filter(item=>item.path.endsWith('/prompt')&&item.method==='POST')).toHaveLength(0);
|
||||
const consultationRequests=requests.filter(item=>item.path.endsWith('/messages')&&item.method==='POST');
|
||||
@@ -1838,7 +1855,7 @@ test('project consultations preserve student drafts and switch between work and
|
||||
expect(consultationRequests[1].body?.intent).toBeUndefined();
|
||||
expect(consultationRequests[2].body).toMatchObject({intent:'guided-help',text:'我也说不清,你带我看看'});
|
||||
await page.screenshot({path:test.info().outputPath('project-teacher-side-chat.png')});
|
||||
await teacher.getByRole('button',{name:'关闭智能体'}).click();await expect(teacher).toHaveCount(0);
|
||||
await page.getByRole('button',{name:'与朋友聊天',exact:true}).click();await expect(teacher).toHaveCount(0);
|
||||
await page.getByRole('button',{name:'与朋友聊天',exact:true}).click();
|
||||
await expect(page.getByText('先理解状态如何随点击变化,再修改代码。',{exact:true})).toBeVisible();
|
||||
await expect(teacher.getByRole('textbox',{name:'向智能体提问'})).toHaveValue('还没说完的困惑');
|
||||
@@ -1900,9 +1917,9 @@ test('teacher stays in the top-right header and proactively checks in without ta
|
||||
await page.reload(); page = await getStableWindow(electronApp);
|
||||
await page.getByTestId('ai-module-option-programming').click();
|
||||
await page.evaluate(() => { window.location.hash = '/chat'; });
|
||||
const header = page.getByTestId('coding-conversation-header');
|
||||
const companion = header.getByTestId('teacher-companion');
|
||||
const teacherButton = companion.getByRole('button', { name: '智能体', exact: true });
|
||||
const header = page.getByTestId('coding-conversation-header-titlebar');
|
||||
const companion = page.getByTestId('teacher-companion');
|
||||
const teacherButton = companion.getByRole('button', { name: '与代码智能体聊天', exact: true });
|
||||
await expect(teacherButton).toHaveText('代码智能体');
|
||||
await expect(teacherButton).toBeVisible();
|
||||
await expect(teacherButton.locator('img')).toHaveAttribute('src', /avatar-01/);
|
||||
@@ -1926,6 +1943,22 @@ test('teacher stays in the top-right header and proactively checks in without ta
|
||||
await composer.fill('我还在写自己的想法');
|
||||
await page.getByRole('tab', { name: '作品', exact: true }).click();
|
||||
await expect(teacherButton).toBeVisible();
|
||||
await teacherButton.hover();
|
||||
await page.clock.runFor(250);
|
||||
const introduction = page.getByTestId('teacher-introduction');
|
||||
await expect(introduction).toContainText('一起发现代码里的小奥秘,把想法变成作品。');
|
||||
await expect(introduction).toBeInViewport({ ratio: 1 });
|
||||
const [introductionBounds, introductionViewportBounds] = await Promise.all([
|
||||
introduction.boundingBox(), page.getByTestId('agent-browser-viewport').boundingBox(),
|
||||
]);
|
||||
// Native WebContentsView must leave room for the portaled introduction too.
|
||||
expect(introductionBounds).not.toBeNull();
|
||||
expect(introductionViewportBounds).not.toBeNull();
|
||||
expect(introductionViewportBounds!.y).toBeGreaterThanOrEqual(introductionBounds!.y + introductionBounds!.height);
|
||||
await page.keyboard.press('Escape');
|
||||
await expect(introduction).toHaveCount(0);
|
||||
await expect(invitation).toHaveAttribute('data-bubble-kind', 'welcome');
|
||||
expect((await readState(electronApp)).captured.filter(item => /\/(messages|teacher-check-in)$/.test(item.path) && item.method === 'POST')).toHaveLength(0);
|
||||
await page.getByRole('tab', { name: '操作对话', exact: true }).click();
|
||||
await composer.focus();
|
||||
await page.clock.fastForward(300_000);
|
||||
@@ -1977,9 +2010,10 @@ test('teacher stays in the top-right header and proactively checks in without ta
|
||||
const teacherInput = teacher.getByRole('textbox', { name: '向智能体提问' });
|
||||
await expect(teacherInput).toBeFocused();
|
||||
await teacherInput.fill('我想比谁更了解自己的宠物');
|
||||
await teacher.getByRole('button', { name: '关闭智能体' }).click();
|
||||
await expect(page.getByRole('button', { name: '智能体', exact: true })).toBeFocused();
|
||||
await page.getByRole('button', { name: '智能体', exact: true }).click();
|
||||
await teacherInput.press('Escape');
|
||||
await expect(teacher).toHaveCount(0);
|
||||
await expect(teacherButton).toBeFocused();
|
||||
await teacherButton.click();
|
||||
await expect(teacherInput).toHaveValue('我想比谁更了解自己的宠物');
|
||||
await teacherInput.fill('');
|
||||
// Let the cleared draft propagate through the parent before advancing the check-in timer.
|
||||
|
||||
Reference in new issue
Block a user