diff --git a/src/renderer/src/components/tab-bar/NativeChatNamingFailure.fixture.tsx b/src/renderer/src/components/tab-bar/NativeChatNamingFailure.fixture.tsx
new file mode 100644
index 00000000000..9a4533ea577
--- /dev/null
+++ b/src/renderer/src/components/tab-bar/NativeChatNamingFailure.fixture.tsx
@@ -0,0 +1,21 @@
+import { defineFixture } from '../../component-fixtures/component-fixture'
+import { NativeChatNamingErrorBlock } from './NativeChatNamingFailure'
+
+export const failed = defineFixture({
+ width: 288,
+ render: () => (
+
+ {},
+ dialogOpen: false,
+ setDialogOpen: () => {},
+ readFullOutput: async () => null
+ }}
+ />
+
+ )
+})
diff --git a/src/renderer/src/components/tab-bar/NativeChatNamingFailure.test.tsx b/src/renderer/src/components/tab-bar/NativeChatNamingFailure.test.tsx
index 7c50bd4d2d4..ffdd413048a 100644
--- a/src/renderer/src/components/tab-bar/NativeChatNamingFailure.test.tsx
+++ b/src/renderer/src/components/tab-bar/NativeChatNamingFailure.test.tsx
@@ -10,9 +10,20 @@ import { useNativeChatNamingFailure } from './use-native-chat-naming-failure'
const client = vi.hoisted(() => ({
supported: false,
+ web: false,
emit: (_event: AgentSessionStatusEvent) => {},
regenerate: vi.fn<() => Promise>()
}))
+const settings = vi.hoisted(() => ({
+ openSettingsPage: vi.fn(),
+ openSettingsTarget: vi.fn()
+}))
+vi.mock('@/store', () => ({
+ useAppStore: { getState: () => settings }
+}))
+vi.mock('@/lib/web-client-location', () => ({
+ isWebClientLocation: () => client.web
+}))
vi.mock('@/runtime/structured-agent-session-client', () => ({
subscribeStructuredAgentSessionStatus: async (_target: unknown, emit: typeof client.emit) => {
client.emit = emit
@@ -60,6 +71,7 @@ async function hoverChatTab(): Promise {
describe('chat tab naming failure', () => {
beforeEach(() => {
vi.useFakeTimers()
+ vi.clearAllMocks()
client.regenerate.mockResolvedValue(true)
})
afterEach(() => {
@@ -68,13 +80,30 @@ describe('chat tab naming failure', () => {
vi.useRealTimers()
})
- it.each([true, false])(
- 'shows the error, with Regenerate name when the host has it: %s',
- async (supported) => {
+ it.each([
+ { supported: true, web: false },
+ { supported: false, web: false },
+ { supported: true, web: true },
+ { supported: false, web: true }
+ ])(
+ 'shows recovery actions for host regeneration support=$supported, web=$web',
+ async ({ supported, web }) => {
client.supported = supported
+ client.web = web
await hoverChatTab()
expect(screen.getAllByText("Couldn't name this chat").length).toBeGreaterThan(0)
expect(screen.getAllByRole('button', { name: 'view error' }).length).toBeGreaterThan(0)
+ const settingsButtons = screen.queryAllByRole('button', { name: 'Settings' })
+ expect(settingsButtons.length > 0).toBe(!web)
+ if (!web) {
+ fireEvent.click(settingsButtons[0])
+ expect(settings.openSettingsPage).toHaveBeenCalledOnce()
+ expect(settings.openSettingsTarget).toHaveBeenCalledExactlyOnceWith({
+ pane: 'chat',
+ repoId: null,
+ sectionId: 'chat-names'
+ })
+ }
const regenerate = screen.queryAllByRole('button', { name: 'Regenerate name' })
expect(regenerate.length > 0).toBe(supported)
if (supported) {
diff --git a/src/renderer/src/components/tab-bar/NativeChatNamingFailure.tsx b/src/renderer/src/components/tab-bar/NativeChatNamingFailure.tsx
index aa17f2d81cd..a998b910e30 100644
--- a/src/renderer/src/components/tab-bar/NativeChatNamingFailure.tsx
+++ b/src/renderer/src/components/tab-bar/NativeChatNamingFailure.tsx
@@ -1,5 +1,7 @@
-import { AlertCircle, RefreshCw } from 'lucide-react'
+import { AlertCircle, RefreshCw, Settings } from 'lucide-react'
import { Button } from '@/components/ui/button'
+import { useAppStore } from '@/store'
+import { isWebClientLocation } from '@/lib/web-client-location'
import { NameGenerationFailedDialog } from '../sidebar/AutoRenameFailedDialog'
import { translate } from '@/i18n/i18n'
import type { NativeChatNamingFailureControls } from './use-native-chat-naming-failure'
@@ -27,7 +29,7 @@ export function NativeChatNamingErrorBlock({
'The chat-name agent stopped with an error, so the chat keeps its first message as its name.'
)}
-
+
) : null}
+ {isWebClientLocation() ? null : (
+ {
+ const store = useAppStore.getState()
+ store.openSettingsPage()
+ store.openSettingsTarget({ pane: 'chat', repoId: null, sectionId: 'chat-names' })
+ }}
+ >
+
+ {translate('tabHoverCard.namingFailure.settings', 'Settings')}
+
+ )}
)
diff --git a/src/renderer/src/i18n/locales/en.json b/src/renderer/src/i18n/locales/en.json
index ec12d851838..649e8b9c30c 100644
--- a/src/renderer/src/i18n/locales/en.json
+++ b/src/renderer/src/i18n/locales/en.json
@@ -21457,6 +21457,7 @@
"title": "Couldn't name this chat",
"description": "The chat-name agent stopped with an error, so the chat keeps its first message as its name.",
"viewError": "view error",
+ "settings": "Settings",
"dialogTitle": "Chat auto-name failed",
"dialogDescriptionStart": "Orca couldn't generate a name for",
"dialogDescriptionEnd": "from its first message."