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:
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')}
|
||||
|
||||
@@ -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 />}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user