diff --git a/desktop/src/__tests__/generalSettings.test.tsx b/desktop/src/__tests__/generalSettings.test.tsx index 42834878..997a2e6a 100644 --- a/desktop/src/__tests__/generalSettings.test.tsx +++ b/desktop/src/__tests__/generalSettings.test.tsx @@ -94,6 +94,10 @@ vi.mock('../pages/ActivitySettings', () => ({ ActivitySettings: () =>
Activity Settings Mock
, })) +vi.mock('../pages/TraceList', () => ({ + TraceList: () =>
Trace List Mock
, +})) + vi.mock('../stores/agentStore', () => ({ useAgentStore: () => ({ activeAgents: [], @@ -677,6 +681,20 @@ describe('Settings > General tab', () => { expect(screen.getByText('Activity Settings Mock')).toBeInTheDocument() }) + it('opens the Trace tab from Settings navigation between Token usage and Diagnostics', () => { + render() + + const usageTab = screen.getByText('Token usage') + const traceTab = screen.getByText('Trace') + const diagnosticsTab = screen.getByText('Diagnostics') + expect((usageTab.compareDocumentPosition(traceTab) & Node.DOCUMENT_POSITION_FOLLOWING) !== 0).toBe(true) + expect((traceTab.compareDocumentPosition(diagnosticsTab) & Node.DOCUMENT_POSITION_FOLLOWING) !== 0).toBe(true) + + fireEvent.click(traceTab) + + expect(screen.getByText('Trace List Mock')).toBeInTheDocument() + }) + it('lets the user disable WebFetch preflight skipping', () => { render() diff --git a/desktop/src/components/layout/Sidebar.tsx b/desktop/src/components/layout/Sidebar.tsx index 689e75e7..0e468ac8 100644 --- a/desktop/src/components/layout/Sidebar.tsx +++ b/desktop/src/components/layout/Sidebar.tsx @@ -5,7 +5,7 @@ import { useUIStore } from '../../stores/uiStore' import { useTranslation, type TranslationKey } from '../../i18n' import { ConfirmDialog } from '../shared/ConfirmDialog' import type { SessionListItem } from '../../types/session' -import { useTabStore, SETTINGS_TAB_ID, SCHEDULED_TAB_ID, TRACE_LIST_TAB_ID } from '../../stores/tabStore' +import { useTabStore, SETTINGS_TAB_ID, SCHEDULED_TAB_ID } from '../../stores/tabStore' import { useChatStore } from '../../stores/chatStore' import { useOpenTargetStore } from '../../stores/openTargetStore' import { desktopUiPreferencesApi, type SidebarProjectPreferences } from '../../api/desktopUiPreferences' @@ -686,21 +686,6 @@ export function Sidebar({ isMobile = false, onRequestClose }: SidebarProps) { {t('sidebar.scheduled')} )} - {!isMobile && ( - { - useTabStore.getState().openTracesTab(t('sidebar.traces')) - closeMobileDrawer() - }} - icon={account_tree} - > - {t('sidebar.traces')} - - )} {expanded ? ( diff --git a/desktop/src/i18n/locales/en.ts b/desktop/src/i18n/locales/en.ts index aecb762a..dc73a734 100644 --- a/desktop/src/i18n/locales/en.ts +++ b/desktop/src/i18n/locales/en.ts @@ -21,7 +21,6 @@ export const en = { // ─── Sidebar ────────────────────────────────────── 'sidebar.newSession': 'New session', 'sidebar.scheduled': 'Scheduled', - 'sidebar.traces': 'Trace', 'sidebar.terminal': 'Terminal', 'sidebar.settings': 'Settings', 'sidebar.searchPlaceholder': 'Search sessions...', @@ -171,6 +170,7 @@ export const en = { 'settings.tab.providers': 'Providers', 'settings.tab.permissions': 'Permissions', 'settings.tab.activity': 'Token usage', + 'settings.tab.trace': 'Trace', 'settings.tab.general': 'General', 'settings.tab.h5Access': 'H5 Access', 'settings.tab.terminal': 'Terminal', diff --git a/desktop/src/i18n/locales/jp.ts b/desktop/src/i18n/locales/jp.ts index f1bc1ad2..4ac0d9d4 100644 --- a/desktop/src/i18n/locales/jp.ts +++ b/desktop/src/i18n/locales/jp.ts @@ -23,7 +23,6 @@ export const jp: Record = { // ─── Sidebar ────────────────────────────────────── 'sidebar.newSession': '新しいセッション', 'sidebar.scheduled': 'スケジュール', - 'sidebar.traces': 'Trace', 'sidebar.terminal': 'ターミナル', 'sidebar.settings': '設定', 'sidebar.searchPlaceholder': 'セッションを検索...', @@ -173,6 +172,7 @@ export const jp: Record = { 'settings.tab.providers': 'プロバイダー', 'settings.tab.permissions': '権限', 'settings.tab.activity': 'トークン使用量', + 'settings.tab.trace': 'Trace', 'settings.tab.general': '一般', 'settings.tab.h5Access': 'H5 アクセス', 'settings.tab.terminal': 'ターミナル', diff --git a/desktop/src/i18n/locales/kr.ts b/desktop/src/i18n/locales/kr.ts index 8b075014..e43721a7 100644 --- a/desktop/src/i18n/locales/kr.ts +++ b/desktop/src/i18n/locales/kr.ts @@ -23,7 +23,6 @@ export const kr: Record = { // ─── Sidebar ────────────────────────────────────── 'sidebar.newSession': '새 세션', 'sidebar.scheduled': '예약 작업', - 'sidebar.traces': 'Trace', 'sidebar.terminal': '터미널', 'sidebar.settings': '설정', 'sidebar.searchPlaceholder': '세션 검색...', @@ -173,6 +172,7 @@ export const kr: Record = { 'settings.tab.providers': '공급자', 'settings.tab.permissions': '권한', 'settings.tab.activity': '토큰 사용량', + 'settings.tab.trace': 'Trace', 'settings.tab.general': '일반', 'settings.tab.h5Access': 'H5 액세스', 'settings.tab.terminal': '터미널', diff --git a/desktop/src/i18n/locales/zh-TW.ts b/desktop/src/i18n/locales/zh-TW.ts index 554620b9..bf538026 100644 --- a/desktop/src/i18n/locales/zh-TW.ts +++ b/desktop/src/i18n/locales/zh-TW.ts @@ -23,7 +23,6 @@ export const zh: Record = { // ─── Sidebar ────────────────────────────────────── 'sidebar.newSession': '新建會話', 'sidebar.scheduled': '定時任務', - 'sidebar.traces': 'Trace', 'sidebar.terminal': '終端', 'sidebar.settings': '設定', 'sidebar.searchPlaceholder': '搜尋會話...', @@ -173,6 +172,7 @@ export const zh: Record = { 'settings.tab.providers': '服務商', 'settings.tab.permissions': '許可權', 'settings.tab.activity': 'Token 用量', + 'settings.tab.trace': 'Trace', 'settings.tab.general': '通用', 'settings.tab.h5Access': 'H5 訪問', 'settings.tab.terminal': '終端', diff --git a/desktop/src/i18n/locales/zh.ts b/desktop/src/i18n/locales/zh.ts index c5a6f8f1..479d7a35 100644 --- a/desktop/src/i18n/locales/zh.ts +++ b/desktop/src/i18n/locales/zh.ts @@ -23,7 +23,6 @@ export const zh: Record = { // ─── Sidebar ────────────────────────────────────── 'sidebar.newSession': '新建会话', 'sidebar.scheduled': '定时任务', - 'sidebar.traces': 'Trace', 'sidebar.terminal': '终端', 'sidebar.settings': '设置', 'sidebar.searchPlaceholder': '搜索会话...', @@ -173,6 +172,7 @@ export const zh: Record = { 'settings.tab.providers': '服务商', 'settings.tab.permissions': '权限', 'settings.tab.activity': 'Token 用量', + 'settings.tab.trace': 'Trace', 'settings.tab.general': '通用', 'settings.tab.h5Access': 'H5 访问', 'settings.tab.terminal': '终端', diff --git a/desktop/src/pages/ActiveSession.test.tsx b/desktop/src/pages/ActiveSession.test.tsx index c781fb7f..e7f63bdd 100644 --- a/desktop/src/pages/ActiveSession.test.tsx +++ b/desktop/src/pages/ActiveSession.test.tsx @@ -186,67 +186,6 @@ describe('ActiveSession task polling', () => { expect(screen.getByTestId('chat-input')).toHaveAttribute('data-variant', 'default') }) - it('opens the current session trace in a separate live window', () => { - const sessionId = 'trace-window-session' - const openSpy = vi.spyOn(window, 'open').mockImplementation(() => null) - - useSessionStore.setState({ - sessions: [{ - id: sessionId, - title: 'Trace Window Session', - createdAt: '2026-05-07T00:00:00.000Z', - modifiedAt: '2026-05-07T00:00:00.000Z', - messageCount: 1, - projectPath: '/workspace/project', - workDir: '/workspace/project', - workDirExists: true, - }], - activeSessionId: sessionId, - isLoading: false, - error: null, - }) - useTabStore.setState({ - tabs: [{ sessionId, title: 'Trace Window Session', type: 'session', status: 'idle' }], - activeTabId: sessionId, - }) - useChatStore.setState({ - sessions: { - [sessionId]: { - messages: [{ id: 'msg-1', type: 'assistant_text', content: 'hello', timestamp: 1 }], - chatState: 'idle', - connectionState: 'connected', - streamingText: '', - streamingToolInput: '', - activeToolUseId: null, - activeToolName: null, - activeThinkingId: null, - pendingPermission: null, - pendingComputerUsePermission: null, - tokenUsage: { input_tokens: 0, output_tokens: 0 }, - elapsedSeconds: 0, - statusVerb: '', - slashCommands: [], - agentTaskNotifications: {}, - elapsedTimer: null, - }, - }, - }) - - try { - render() - fireEvent.click(screen.getByLabelText(/独立|separate/i)) - - expect(openSpy).toHaveBeenCalledWith( - expect.stringContaining(`traceSessionId=${sessionId}`), - '_blank', - 'noopener,noreferrer', - ) - expect(openSpy.mock.calls[0]?.[0]).toContain('traceWindow=1') - } finally { - openSpy.mockRestore() - } - }) - it('renders the current goal as a lightweight header strip without a page-level panel', () => { const sessionId = 'goal-visible-session' diff --git a/desktop/src/pages/ActiveSession.tsx b/desktop/src/pages/ActiveSession.tsx index 9f79672d..b49a19f8 100644 --- a/desktop/src/pages/ActiveSession.tsx +++ b/desktop/src/pages/ActiveSession.tsx @@ -1,5 +1,5 @@ import { useEffect, useMemo, useRef, useState } from 'react' -import { ExternalLink, RadioTower, Target } from 'lucide-react' +import { Target } from 'lucide-react' import { SCHEDULED_TAB_ID, SETTINGS_TAB_ID, @@ -32,8 +32,6 @@ import type { ActiveGoalState } from '../types/chat' import { useMobileViewport } from '../hooks/useMobileViewport' import { isDesktopRuntime } from '../lib/desktopRuntime' import { publicAssetPath } from '../lib/publicAsset' -import { getDesktopHost } from '../lib/desktopHost' -import { buildTraceWindowUrl } from '../lib/traceLaunch' const TASK_POLL_INTERVAL_MS = 1000 const WORKSPACE_RESIZE_STEP = 32 @@ -364,22 +362,6 @@ export function ActiveSession() { return t('session.timeDays', { n: Math.floor(diff / 86400000) }) }, [session?.modifiedAt, t]) - const openTrace = () => { - if (!activeTabId) return - const title = session?.title || t('session.untitled') - useTabStore.getState().openTraceTab(activeTabId, `${t('trace.title')}: ${title}`) - } - - const openTraceWindow = () => { - if (!activeTabId) return - const host = getDesktopHost() - if (host.trace) { - void host.trace.openWindow(activeTabId) - return - } - window.open(buildTraceWindowUrl(activeTabId), '_blank', 'noopener,noreferrer') - } - if (!activeTabId) return null return ( @@ -480,25 +462,6 @@ export function ActiveSession() { > {session?.title || t('session.untitled')} - -
setActiveTab('plugins')} /> setActiveTab('computerUse')} /> setActiveTab('activity')} /> + setActiveTab('trace')} /> setActiveTab('diagnostics')} />
@@ -173,8 +175,8 @@ export function Settings() {
- {/* Tab content */} -
+ {/* Tab content; trace embeds a full-bleed page that manages its own scroll */} +
{activeTab === 'providers' && } {activeTab === 'activity' && } {activeTab === 'general' && } @@ -187,6 +189,7 @@ export function Settings() { {activeTab === 'memory' && } {activeTab === 'plugins' && } {activeTab === 'computerUse' && } + {activeTab === 'trace' && } {activeTab === 'diagnostics' && } {activeTab === 'about' && }
diff --git a/desktop/src/stores/uiStore.ts b/desktop/src/stores/uiStore.ts index c40f885a..4c3206cf 100644 --- a/desktop/src/stores/uiStore.ts +++ b/desktop/src/stores/uiStore.ts @@ -41,6 +41,7 @@ export type SettingsTab = | 'memory' | 'plugins' | 'computerUse' + | 'trace' | 'diagnostics' | 'about'