fix: align channel runtime and settings UI
This commit is contained in:
@@ -863,6 +863,11 @@ describe('App', () => {
|
||||
provider: 'model',
|
||||
profileId: modelProfileId
|
||||
})
|
||||
expect(
|
||||
screen
|
||||
.getByText('正在连接 Agent Runtime')
|
||||
.querySelector('.message__status-dot')
|
||||
).toHaveClass('message__status-dot--active')
|
||||
const userMessage = screen
|
||||
.getAllByText('帮我分析项目')
|
||||
.map((element) => element.closest('article'))
|
||||
@@ -2179,6 +2184,15 @@ describe('App', () => {
|
||||
})
|
||||
|
||||
expect(await screen.findByText('已取消')).toBeInTheDocument()
|
||||
const cancelledStatus = screen
|
||||
.getAllByText('请求已取消')
|
||||
.find((element) => element.classList.contains('message__status'))
|
||||
expect(cancelledStatus).toBeDefined()
|
||||
const cancelledDot = cancelledStatus?.querySelector(
|
||||
'.message__status-dot'
|
||||
)
|
||||
expect(cancelledDot).toHaveClass('message__status-dot')
|
||||
expect(cancelledDot).not.toHaveClass('message__status-dot--active')
|
||||
fireEvent.click(screen.getByText('任务与活动'))
|
||||
expect((await screen.findAllByText('已取消')).length).toBeGreaterThan(0)
|
||||
fireEvent.click(screen.getByRole('button', { name: '进行中' }))
|
||||
@@ -2187,6 +2201,37 @@ describe('App', () => {
|
||||
).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('keeps persisted completed message statuses static', async () => {
|
||||
vi.mocked(api.conversations.list).mockResolvedValueOnce([
|
||||
{
|
||||
id: '00000000-0000-4000-8000-000000000210',
|
||||
projectId,
|
||||
title: '已完成会话',
|
||||
updatedAt: 1_775_000_000_000,
|
||||
messages: [
|
||||
{
|
||||
id: '00000000-0000-4000-8000-000000000211',
|
||||
role: 'assistant',
|
||||
content: '任务结果',
|
||||
createdAt: 1_775_000_000_000,
|
||||
state: 'complete',
|
||||
status: '任务已完成'
|
||||
}
|
||||
]
|
||||
}
|
||||
])
|
||||
|
||||
render(<App />)
|
||||
|
||||
const completedStatus = await screen.findByText('任务已完成')
|
||||
expect(
|
||||
completedStatus.querySelector('.message__status-dot')
|
||||
).toHaveClass('message__status-dot')
|
||||
expect(
|
||||
completedStatus.querySelector('.message__status-dot')
|
||||
).not.toHaveClass('message__status-dot--active')
|
||||
})
|
||||
|
||||
it('switches runtime profiles from the composer dropdown', async () => {
|
||||
render(<App />)
|
||||
|
||||
|
||||
@@ -4925,7 +4925,14 @@ function App(): React.JSX.Element {
|
||||
: 'message__status'
|
||||
}
|
||||
>
|
||||
<span className="thinking-dot" />
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className={
|
||||
message.state === 'streaming'
|
||||
? 'message__status-dot message__status-dot--active'
|
||||
: 'message__status-dot'
|
||||
}
|
||||
/>
|
||||
{message.status}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -339,6 +339,50 @@ describe('ChannelSettingsSection', () => {
|
||||
expect(trigger).toHaveFocus()
|
||||
})
|
||||
|
||||
it('renders disconnecting a configured Weixin binding as a danger action', async () => {
|
||||
const configuredSnapshot: ChannelSettingsSnapshot = {
|
||||
...snapshot,
|
||||
weixin: {
|
||||
enabled: true,
|
||||
bindingConfigured: true,
|
||||
accountDisplay: '微信用户',
|
||||
source: 'encrypted',
|
||||
status: { state: 'running' }
|
||||
}
|
||||
}
|
||||
const api = bindingApi()
|
||||
Object.defineProperty(window, 'goodbuddy', {
|
||||
configurable: true,
|
||||
value: {
|
||||
channels: {
|
||||
...api,
|
||||
getSnapshot: vi.fn(async () => configuredSnapshot),
|
||||
apply: vi.fn(),
|
||||
testConnection: vi.fn()
|
||||
},
|
||||
projects: {
|
||||
list: vi.fn(async () => projects),
|
||||
update: vi.fn()
|
||||
},
|
||||
settings: settingsApi()
|
||||
} as unknown as DesktopApi
|
||||
})
|
||||
|
||||
render(<ChannelSettingsSection />)
|
||||
const disconnect = await screen.findByRole('button', {
|
||||
name: '断开本机绑定'
|
||||
})
|
||||
expect(disconnect).toHaveClass(
|
||||
'danger-button',
|
||||
'danger-button--quiet'
|
||||
)
|
||||
|
||||
fireEvent.click(disconnect)
|
||||
await waitFor(() =>
|
||||
expect(api.disconnectWeixin).toHaveBeenCalledOnce()
|
||||
)
|
||||
})
|
||||
|
||||
it('shows Weixin verification failures inside the QR dialog', async () => {
|
||||
Object.defineProperty(window, 'goodbuddy', {
|
||||
configurable: true,
|
||||
@@ -451,6 +495,11 @@ describe('ChannelSettingsSection', () => {
|
||||
value: agentRuntimeSelectionKey({ provider: 'opencode' })
|
||||
}
|
||||
})
|
||||
expect(
|
||||
screen.getByText(
|
||||
'通过 OpenCode Agent Runtime 运行,并跟随“Agent Runtime”设置中的全局 OpenCode 配置。'
|
||||
)
|
||||
).toBeInTheDocument()
|
||||
fireEvent.click(
|
||||
screen.getByRole('button', { name: '保存通道设置' })
|
||||
)
|
||||
|
||||
@@ -239,15 +239,7 @@ function runtimeSelectionDescription(
|
||||
}
|
||||
const runtimeLabel =
|
||||
selection.provider === 'opencode' ? 'OpenCode' : 'Continue'
|
||||
const profile =
|
||||
'profileId' in selection
|
||||
? settings.modelProfiles.find(
|
||||
(candidate) => candidate.id === selection.profileId
|
||||
)
|
||||
: undefined
|
||||
return profile
|
||||
? `通过 ${runtimeLabel} Agent Runtime 运行,并使用 ${profile.name}。`
|
||||
: `通过 ${runtimeLabel} Agent Runtime 及其当前模型配置运行。`
|
||||
return `通过 ${runtimeLabel} Agent Runtime 运行,并跟随“Agent Runtime”设置中的全局 ${runtimeLabel} 配置。`
|
||||
}
|
||||
|
||||
function ChannelProjectControls({
|
||||
@@ -887,7 +879,7 @@ function WeixinChannelEditor({
|
||||
</button>
|
||||
{settings.bindingConfigured && (
|
||||
<button
|
||||
className="danger-ghost"
|
||||
className="danger-button danger-button--quiet"
|
||||
disabled={busy}
|
||||
onClick={onDisconnect}
|
||||
type="button"
|
||||
|
||||
@@ -2816,14 +2816,18 @@ textarea:focus-visible {
|
||||
color: #ff4d4f;
|
||||
}
|
||||
|
||||
.thinking-dot {
|
||||
.message__status-dot {
|
||||
flex: 0 0 auto;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
margin-top: 4px;
|
||||
border-radius: 50%;
|
||||
background: currentColor;
|
||||
}
|
||||
|
||||
.message__status-dot--active {
|
||||
animation: pulse 1.1s ease-in-out infinite;
|
||||
background: #1677ff;
|
||||
background: var(--accent-solid);
|
||||
}
|
||||
|
||||
.tool-execution-list {
|
||||
@@ -4191,6 +4195,10 @@ details.settings-section > :not(summary) {
|
||||
margin-left: var(--space-4);
|
||||
}
|
||||
|
||||
details.settings-section > :not(summary) + :not(summary) {
|
||||
margin-top: var(--space-3);
|
||||
}
|
||||
|
||||
.model-connection-manager {
|
||||
display: grid;
|
||||
min-width: 0;
|
||||
|
||||
Reference in New Issue
Block a user