Add contextual teacher help entry and simplify consultation panel

This commit is contained in:
鲨鱼辣椒
2026-09-22 16:55:07 +08:00
parent 68e676cb62
commit 12d800ec51
11 changed files with 603 additions and 50 deletions

View File

@@ -534,7 +534,9 @@ async function installCodingFirstChatHost(
return respond(consultationTopics[consultationRole],201);
}
if (path.endsWith(`${topicBase}/${topicId}/messages`) && method === 'POST') {
consultationTopics[consultationRole]={...currentTopic,revision:2,requests:[{id:body!.requestId,text:body!.text,references:body!.references??[],createdAt:now,sourceCursor:{workerGeneration:1,seq:1},sourceCapturedAt:now,includedSourceMessageIds:[],omittedMessages:0,status:'completed',response:consultationRole==='friend'?'听你的描述,我有点好奇该从哪里开始玩。':'先理解状态如何随点击变化,再修改代码。'}]};
const suggestionRequest = body!.intent === 'suggestions';
const teacherResponse = suggestionRequest ? '我们可以从你最近试过的地方聊起。' : body!.intent === 'guided-help' ? '你最近做的哪一步,让你停下来想了一会儿?' : '先理解状态如何随点击变化,再修改代码。';
consultationTopics[consultationRole]={...currentTopic,revision:Number(currentTopic?.revision??0)+1,requests:[...(currentTopic?.requests as unknown[]??[]),{id:body!.requestId,text:body!.text,intent:body!.intent,references:body!.references??[],createdAt:now,sourceCursor:{workerGeneration:1,seq:1},sourceCapturedAt:now,includedSourceMessageIds:[],omittedMessages:0,status:'completed',response:consultationRole==='friend'?'听你的描述,我有点好奇该从哪里开始玩。':teacherResponse,...(suggestionRequest?{suggestedQuestions:['怎样观察别人玩游戏?','我该先试哪个想法?']}:{})}]};
return respond(consultationTopics[consultationRole],202);
}
if (path.endsWith(`${topicBase}/${topicId}`)) return respond(currentTopic);
@@ -1736,17 +1738,24 @@ test('project consultations preserve student drafts and switch between work and
await expect(composer).toHaveValue('保留我的草稿');
await page.getByRole('button',{name:'老师',exact:true}).click();
const teacher=page.getByTestId('teacher-chat-panel');
await expect(teacher.getByText('一起理解代码')).toBeVisible();
await teacher.getByRole('button',{name:'解释当前代码',exact:true}).click();
await expect(teacher.getByRole('textbox',{name:'向老师提问'})).toHaveValue('解释当前代码');
await expect(teacher.getByText('有问题,随时来找我')).toBeVisible();
await expect(teacher.getByText('当前项目',{exact:true})).toHaveCount(0);
await expect(teacher.getByRole('button',{name:'解释当前代码',exact:true})).toHaveCount(0);
expect((await readState(electronApp)).captured.filter(item=>item.path.endsWith('/messages')&&item.method==='POST')).toHaveLength(0);
await teacher.getByRole('textbox',{name:'向老师提问'}).fill('帮我理解当前代码');
await teacher.getByRole('button',{name:'提问',exact:true}).click();
await teacher.getByRole('textbox',{name:'向老师提问'}).fill('还没说完的困惑');
await teacher.getByRole('button',{name:'老师,帮我看看',exact:true}).click();
await expect(teacher.getByText('我们可以从你最近试过的地方聊起。')).toBeVisible();
await expect(teacher.getByRole('textbox',{name:'向老师提问'})).toHaveValue('还没说完的困惑');
expect((await readState(electronApp)).captured.filter(item=>item.path.endsWith('/messages')&&item.method==='POST')).toHaveLength(1);
await teacher.getByRole('button',{name:'怎样观察别人玩游戏?',exact:true}).click();
await expect(teacher.getByText('先理解状态如何随点击变化,再修改代码。')).toBeVisible();
await expect(teacher.getByRole('textbox',{name:'向老师提问'})).toHaveValue('还没说完的困惑');
await expect(teacher.getByRole('button',{name:'带回主会话草稿'})).toHaveCount(0);
await teacher.getByRole('button',{name:'我去试一试'}).click();
await expect(composer).toHaveValue('保留我的草稿');
await teacher.getByRole('textbox',{name:'向老师提问'}).fill('还没说完的困惑');
await teacher.getByRole('button',{name:'我也说不清,你带我看看',exact:true}).click();
await expect(teacher.getByText('你最近做的哪一步,让你停下来想了一会儿?')).toBeVisible();
await expect(teacher.getByRole('textbox',{name:'向老师提问'})).toHaveValue('还没说完的困惑');
await page.getByRole('button',{name:'朋友',exact:true}).click();
await expect(teacher).toHaveAttribute('data-role','friend');
await expect(teacher.getByText('一起看看你的作品')).toBeVisible();
@@ -1762,7 +1771,12 @@ test('project consultations preserve student drafts and switch between work and
await expect(teacher.getByText('先理解状态如何随点击变化,再修改代码。')).toBeVisible();
const requests=(await readState(electronApp)).captured;
expect(requests.filter(item=>item.path.endsWith('/prompt')&&item.method==='POST')).toHaveLength(0);
expect(requests.filter(item=>item.path.endsWith('/messages')&&item.method==='POST')).toHaveLength(1);
const consultationRequests=requests.filter(item=>item.path.endsWith('/messages')&&item.method==='POST');
expect(consultationRequests).toHaveLength(3);
expect(consultationRequests[0].body).toMatchObject({intent:'suggestions',text:'老师,帮我看看'});
expect(consultationRequests[1].body).toMatchObject({text:'怎样观察别人玩游戏?'});
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();