feat(android): expose advanced debug tools on mobile (#890)

* feat(android): expose advanced debug tools on mobile

Show Advanced debug tools on Android with mobile-friendly layout. Backend SAF log export already landed in #877.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(settings): 调试工具保留条数说明仅移动端展示,导出成功消息可换行并显示完整路径

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Chris233 <h-chris233@outlook.com>
This commit is contained in:
authored and GitHub committed 2026-08-03 19:02:38 +08:00
1 parent 26cd012433
commit f5fc5fd0c1
2 files changed
+36 -14

No files matched your search

@@ -5,6 +5,7 @@
import { useEffect, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { exportErrorLog } from '../../lib/ipc';
import { useMobileLayout } from '../../lib/useMobileLayout';
import { useHotkeySettings } from '../../state/HotkeySettingsContext';
import { Btn, Card } from '../_atoms';
import { SettingRow, Toggle, SectionTitle, inputStyle } from './shared';
@@ -13,6 +14,7 @@ const clamp = (n: number, min: number, max: number) => Math.max(min, Math.min(ma
export function DebugToolsSection() {
const { t } = useTranslation();
const mobile = useMobileLayout();
const { prefs, updatePrefs: savePrefs } = useHotkeySettings();
const [exportStatus, setExportStatus] = useState<'idle' | 'busy' | 'ok' | 'err'>('idle');
const [exportMessage, setExportMessage] = useState<string>('');
@@ -71,33 +73,49 @@ export function DebugToolsSection() {
<Toggle on={prefs.recordAudioForDebug} onToggle={onRecordAudioForDebugChange} />
</SettingRow>
<SettingRow label={t('settings.recording.audioRecordingMaxEntriesLabel')}>
<input
type="number"
min={1}
max={200}
placeholder="200"
value={prefs.audioRecordingMaxEntries ?? ''}
onChange={e => onAudioRecordingMaxEntriesChange(e.target.value)}
style={{ ...inputStyle, width: 80, textAlign: 'right' }}
disabled={!prefs.recordAudioForDebug}
/>
<div style={{ display: 'flex', flexDirection: mobile ? 'column' : 'row', gap: 8, alignItems: mobile ? 'stretch' : 'center', width: '100%' }}>
<input
type="number"
min={1}
max={200}
placeholder="200"
value={prefs.audioRecordingMaxEntries ?? ''}
onChange={e => onAudioRecordingMaxEntriesChange(e.target.value)}
style={{ ...inputStyle, width: mobile ? '100%' : 80, textAlign: 'right' }}
disabled={!prefs.recordAudioForDebug}
/>
{mobile && (
<span style={{ fontSize: 11, color: 'var(--ol-ink-4)', lineHeight: 1.45 }}>
{t('settings.recording.audioRecordingMaxEntriesDesc')}
</span>
)}
</div>
</SettingRow>
<SettingRow label={t('modal.about.exportErrorLog')}>
<div style={{ display: 'flex', gap: 8, alignItems: 'center' }}>
<div style={{ display: 'flex', gap: 8, alignItems: 'center', flexWrap: 'wrap' }}>
<Btn variant="ghost" size="sm" disabled={exportStatus === 'busy'} onClick={onExportLog}>
{exportStatus === 'busy' ? t('modal.about.exporting') : t('modal.about.exportErrorLogBtn')}
</Btn>
{exportStatus === 'ok' && (
<span
style={{ fontSize: 11, color: 'var(--ol-ok)', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', maxWidth: 220 }}
style={{
fontSize: 11,
color: 'var(--ol-ok)',
whiteSpace: mobile ? 'normal' : 'nowrap',
overflow: mobile ? 'visible' : 'hidden',
textOverflow: mobile ? 'clip' : 'ellipsis',
wordBreak: 'break-word',
maxWidth: mobile ? '100%' : 220,
}}
title={exportMessage}
>
{t('modal.about.exportSuccess')}
{mobile && exportMessage ? `:${exportMessage}` : ''}
</span>
)}
{exportStatus === 'err' && (
<span
style={{ fontSize: 11, color: 'var(--ol-err)', lineHeight: 1.45, wordBreak: 'break-word', maxWidth: 280 }}
style={{ fontSize: 11, color: 'var(--ol-err)', lineHeight: 1.45, wordBreak: 'break-word', maxWidth: mobile ? '100%' : 280 }}
title={exportMessage}
>
{t('modal.about.exportFailed')}
+5 -1
View File
@@ -101,16 +101,20 @@ export function PrivacyTab() {
// 高级:只留真正的实验性/开发者功能 —— Less Computer · Claude 控制台 · 调试工具。
// (本地模型移入「服务」、更新相关移入「关于」,这个 tab 不再是杂物抽屉。)
// 调试工具本身是跨端的:Android 复用同一份 prefs / 录音导出入口;
// 这里只做平台 gating,不把桌面特有能力耦合进移动端运行时。
export function AdvancedTab() {
const os = detectOS();
const platformCaps = usePlatformCaps();
const showDesktopAdvanced = platformCaps?.platform === 'desktop';
const showDebugTools =
platformCaps?.platform === 'desktop' || platformCaps?.platform === 'android';
return (
<>
{showDesktopAdvanced && os !== 'win' && <CodingAgentSection />}
{showDesktopAdvanced && os !== 'win' && <ClaudeConsoleSection />}
{showDesktopAdvanced && <DebugToolsSection />}
{showDebugTools && <DebugToolsSection />}
</>
);
}