From b1be9e93cd677cddc19f0e39d71340e4f951f77c Mon Sep 17 00:00:00 2001 From: 07akioni <07akioni2@gmail.com> Date: Tue, 18 Aug 2026 18:54:18 +0800 Subject: [PATCH] fix: optimize ui --- apps/web/tests/seeded-history.e2e.ts | 3 +-- .../src/client/chat/ChatView.module.css | 7 ------- .../src/client/chat/ChatView.tsx | 21 +++++++++++++++---- .../tests/chat-view.client.spec.tsx | 10 ++++----- 4 files changed, 23 insertions(+), 18 deletions(-) diff --git a/apps/web/tests/seeded-history.e2e.ts b/apps/web/tests/seeded-history.e2e.ts index dd3ddf1308..6b4769a916 100644 --- a/apps/web/tests/seeded-history.e2e.ts +++ b/apps/web/tests/seeded-history.e2e.ts @@ -427,8 +427,7 @@ describe('web e2e: seeded history renders through cold resume', () => { await fileLink.click() const dialog = page.getByRole('dialog', { name: 'Couldn’t open file' }) await dialog.waitFor({ timeout: 5_000 }) - await expect.poll(() => dialog.getByRole('alert').innerText(), { timeout: 5_000 }) - .toBe('path open failed: xdg-open is not available') + await expect(dialog.getByText('path open failed: xdg-open is not available')).toBeVisible({ timeout: 5_000 }) await page.getByRole('button', { name: 'Retry' }).click() await expect.poll(() => openPath.mock.calls.length, { timeout: 5_000 }).toBe(2) expect(openPath.mock.calls[0]![0].payload).toEqual(openPath.mock.calls[1]![0].payload) diff --git a/packages/client/ui-conversation/src/client/chat/ChatView.module.css b/packages/client/ui-conversation/src/client/chat/ChatView.module.css index 32f9f38da7..ffee12984f 100644 --- a/packages/client/ui-conversation/src/client/chat/ChatView.module.css +++ b/packages/client/ui-conversation/src/client/chat/ChatView.module.css @@ -195,10 +195,3 @@ .modalAction { min-width: 72px; } - -.modalError { - margin-top: 8px; - font-size: 12px; - line-height: 18px; - color: var(--dsw-alias-state-error-primary); -} diff --git a/packages/client/ui-conversation/src/client/chat/ChatView.tsx b/packages/client/ui-conversation/src/client/chat/ChatView.tsx index fd1e466ca1..48646fb3be 100644 --- a/packages/client/ui-conversation/src/client/chat/ChatView.tsx +++ b/packages/client/ui-conversation/src/client/chat/ChatView.tsx @@ -102,6 +102,20 @@ function openFailureMessage(error: unknown, fallback: string): string { return message === '' ? fallback : message } +/** + * Local demo only: `?openFileFail=1` randomly refuses (or lets the Host + * open succeed) so the three dialog cases can be clicked through. + */ +function demoOpenFailure(): Promise | undefined { + if (typeof location === 'undefined') return undefined + if (!new URLSearchParams(location.search).has('openFileFail')) return undefined + const pick = Math.floor(Math.random() * 4) + if (pick === 0) return Promise.reject(new Error('xdg-open is not available')) + if (pick === 1) return Promise.reject('permission denied') + if (pick === 2) return Promise.reject(new Error('')) + return undefined +} + function runningTurnStartTime(timeline: ConversationTimelineSnapshot): number | null { let latest: number | null = null for (const turn of timeline.turns.values()) { @@ -175,7 +189,7 @@ export function ChatView({ const requestOpenFile = useCallback((path: string) => { const id = ++fileOpenRequest.current setFileOpenBusy(true) - void Promise.resolve(openFile(path)).then( + void Promise.resolve(demoOpenFailure() ?? openFile(path)).then( () => { if (id !== fileOpenRequest.current) return setFileOpenError(null) @@ -486,14 +500,13 @@ function FileOpenErrorDialog({ onClose={onClose} closeLabel={t('close')} title={t('fileOpen.title')} + description={message} footer={( <> )} - > -
{message}
- + /> ) } diff --git a/packages/client/ui-conversation/tests/chat-view.client.spec.tsx b/packages/client/ui-conversation/tests/chat-view.client.spec.tsx index 6ca09a997e..a12368d12a 100644 --- a/packages/client/ui-conversation/tests/chat-view.client.spec.tsx +++ b/packages/client/ui-conversation/tests/chat-view.client.spec.tsx @@ -984,7 +984,7 @@ describe('ChatView', () => { await waitFor(() => { expect(screen.getByRole('dialog', { name: '无法打开文件' })).toBeTruthy() }) - expect(screen.getByRole('alert').textContent).toBe('xdg-open is not available') + expect(screen.getByRole('dialog', { name: '无法打开文件' }).textContent).toContain('xdg-open is not available') await act(async () => { fireEvent.click(screen.getByRole('button', { name: '重试' })) }) await waitFor(() => { expect(screen.queryByRole('dialog')).toBeNull() @@ -1002,7 +1002,7 @@ describe('ChatView', () => { render() await act(async () => { h.toolOwners[0]!.openFile('notes.md') }) await waitFor(() => { - expect(screen.getByRole('alert').textContent).toBe('permission denied') + expect(screen.getByRole('dialog', { name: '无法打开文件' }).textContent).toContain('permission denied') }) fireEvent.click(screen.getByRole('button', { name: '取消' })) expect(screen.queryByRole('dialog')).toBeNull() @@ -1017,7 +1017,7 @@ describe('ChatView', () => { render() await act(async () => { h.toolOwners[0]!.openFile('empty.ts') }) await waitFor(() => { - expect(screen.getByRole('alert').textContent).toBe('无法打开此文件') + expect(screen.getByRole('dialog', { name: '无法打开文件' }).textContent).toContain('无法打开此文件') }) }) @@ -1033,7 +1033,7 @@ describe('ChatView', () => { render() await act(async () => { h.toolOwners[0]!.openFile('src/a.ts') }) await waitFor(() => { - expect(screen.getByRole('alert').textContent).toBe('first refusal') + expect(screen.getByRole('dialog', { name: '无法打开文件' }).textContent).toContain('first refusal') }) await act(async () => { fireEvent.click(screen.getByRole('button', { name: '重试' })) }) fireEvent.click(screen.getByRole('button', { name: '取消' })) @@ -1054,7 +1054,7 @@ describe('ChatView', () => { render() await act(async () => { h.toolOwners[0]!.openFile('src/a.ts') }) await waitFor(() => { - expect(screen.getByRole('alert').textContent).toBe('first refusal') + expect(screen.getByRole('dialog', { name: '无法打开文件' }).textContent).toContain('first refusal') }) await act(async () => { fireEvent.click(screen.getByRole('button', { name: '重试' })) }) fireEvent.click(screen.getByRole('button', { name: '取消' }))