Merge remote-tracking branch 'origin/master' into mergebot/pr883

# Conflicts:
#	packages/client/tsdown.client.ts
#	packages/client/ui-conversation/src/client/chat/ChatView.tsx
#	packages/client/ui-conversation/src/client/chat/PendingCard.tsx
#	packages/client/ui-conversation/src/client/chat/ToolRow.tsx
#	packages/client/ui-conversation/src/client/toolviews/todo-row.module.css
#	packages/client/ui-conversation/src/client/toolviews/todo-row.tsx
#	packages/client/ui-conversation/tests/chat-apply.spec.tsx
#	packages/client/ui-conversation/tests/coverage-tails.spec.tsx
#	packages/client/ui-conversation/tests/todo-panel.spec.tsx
#	packages/client/ui-primitives/src/icons/index.tsx
#	packages/client/ui-primitives/tests/icons.spec.tsx
#	packages/client/ui-question/README.i18n.yaml
#	packages/client/ui-question/src/client/QuestionComposer.module.css
#	packages/client/ui-question/src/client/QuestionComposer.tsx
This commit is contained in:
imccyu
2026-07-30 01:01:14 +08:00
1663 changed files with 59630 additions and 13353 deletions
@@ -34,6 +34,7 @@ const kit = {
useSession: (() => { throw new Error('unused') }) as unknown as SnapshotSelectorHook<ConversationSnapshot>,
useSessions: (() => { throw new Error('unused') }) as unknown as SnapshotSelectorHook<SessionListState>,
useWorkspaces: (() => { throw new Error('unused') }) as unknown as SnapshotSelectorHook<WorkspaceListState>,
useProjection: (() => undefined) as never,
useInput: (() => { throw new Error('unused') }) as never,
inputActions: { setDraft: () => { throw new Error('unused') }, submit: () => { throw new Error('unused') } } as never,
}
@@ -80,7 +81,11 @@ describe('QuestionComposer', () => {
expect(screen.getByText('1 / 3')).toBeTruthy()
expect(screen.getByText('推荐')).toBeTruthy()
expect(screen.getByText('工程落地型')).toBeTruthy()
expect(screen.getByText('按当前空缺岗位的优先级选择。')).toBeTruthy()
const detail = screen.getByText('按当前空缺岗位的优先级选择。')
const scrollRegion = detail.closest('[data-question-scroll]')
expect(scrollRegion).toBeTruthy()
expect(scrollRegion?.contains(screen.getByRole('radio', { name: /工程落地型/ }))).toBe(true)
expect(scrollRegion?.contains(screen.getByText('下一题').closest('button'))).toBe(false)
fireEvent.keyDown(screen.getByRole('radio', { name: /工程落地型/ }), { key: 'Enter' })
expect(respond).not.toHaveBeenCalled()
fireEvent.click(screen.getByRole('radio', { name: /工程落地型/ }))
@@ -111,6 +116,29 @@ describe('QuestionComposer', () => {
expect(screen.getByRole<HTMLButtonElement>('button', { name: '正在提交…' }).disabled).toBe(true)
})
it('renders plan detail through the shared assistant Markdown primitive', () => {
const carrier = new PendingWait(
'question',
RpcId('markdown-plan'),
SID,
{
questions: [{
id: 'plan',
question: '批准这个计划吗?',
detail: '# 实施计划\n\n- **先验证**现状\n- 修改 `QuestionComposer`',
options: [{ label: '批准' }],
}],
},
vi.fn(),
)
const view = render(<QuestionComposer matched={carrier} interactions={[carrier]} {...kit} />)
expect(screen.getByRole('heading', { level: 1, name: '实施计划' })).toBeTruthy()
expect(view.container.querySelector('strong')?.textContent).toBe('先验证')
expect(view.container.querySelector('code')?.textContent).toBe('QuestionComposer')
expect(view.container.querySelectorAll('li')).toHaveLength(2)
})
it('skips individual questions without discarding earlier answers', () => {
const { carrier, respond } = wait()
render(<QuestionComposer matched={carrier} interactions={[carrier]} {...kit} />)