feat(auth-files): merge auth file details into editor modal

This commit is contained in:
Supra4E8C
2026-03-22 01:29:23 +08:00
parent e3e1db0272
commit faf1e3fbab
8 changed files with 60 additions and 103 deletions
@@ -3,13 +3,7 @@ import { Button } from '@/components/ui/Button';
import { LoadingSpinner } from '@/components/ui/LoadingSpinner';
import { SelectionCheckbox } from '@/components/ui/SelectionCheckbox';
import { ToggleSwitch } from '@/components/ui/ToggleSwitch';
import {
IconBot,
IconCode,
IconDownload,
IconInfo,
IconTrash2,
} from '@/components/ui/icons';
import { IconBot, IconDownload, IconSettings, IconTrash2 } from '@/components/ui/icons';
import { ProviderStatusBar } from '@/components/providers/ProviderStatusBar';
import type { AuthFileItem } from '@/types';
import { resolveAuthProvider } from '@/utils/quota';
@@ -44,7 +38,6 @@ export type AuthFileCardProps = {
keyStats: KeyStats;
statusBarCache: Map<string, AuthFileStatusBarData>;
onShowModels: (file: AuthFileItem) => void;
onShowDetails: (file: AuthFileItem) => void;
onDownload: (name: string) => void;
onOpenPrefixProxyEditor: (file: AuthFileItem) => void;
onDelete: (name: string) => void;
@@ -71,7 +64,6 @@ export function AuthFileCard(props: AuthFileCardProps) {
keyStats,
statusBarCache,
onShowModels,
onShowDetails,
onDownload,
onOpenPrefixProxyEditor,
onDelete,
@@ -95,13 +87,13 @@ export function AuthFileCard(props: AuthFileCardProps) {
? styles.antigravityCard
: quotaType === 'claude'
? styles.claudeCard
: quotaType === 'codex'
? styles.codexCard
: quotaType === 'gemini-cli'
? styles.geminiCliCard
: quotaType === 'kimi'
? styles.kimiCard
: '';
: quotaType === 'codex'
? styles.codexCard
: quotaType === 'gemini-cli'
? styles.geminiCliCard
: quotaType === 'kimi'
? styles.kimiCard
: '';
const rawAuthIndex = file['auth_index'] ?? file.authIndex;
const authIndexKey = normalizeAuthIndex(rawAuthIndex);
@@ -153,7 +145,8 @@ export function AuthFileCard(props: AuthFileCardProps) {
</span>
{priorityValue !== undefined && (
<span className={styles.priorityBadge}>
{t('auth_files.priority_display')}: <span className={styles.priorityValue}>{priorityValue}</span>
{t('auth_files.priority_display')}:{' '}
<span className={styles.priorityValue}>{priorityValue}</span>
</span>
)}
</div>
@@ -205,16 +198,6 @@ export function AuthFileCard(props: AuthFileCardProps) {
)}
{!isRuntimeOnly && (
<>
<Button
variant="secondary"
size="sm"
onClick={() => onShowDetails(file)}
className={styles.iconButton}
title={t('common.info', { defaultValue: '关于' })}
disabled={disableControls}
>
<IconInfo className={styles.actionIcon} size={16} />
</Button>
<Button
variant="secondary"
size="sm"
@@ -233,7 +216,7 @@ export function AuthFileCard(props: AuthFileCardProps) {
title={t('auth_files.prefix_proxy_button')}
disabled={disableControls}
>
<IconCode className={styles.actionIcon} size={16} />
<IconSettings className={styles.actionIcon} size={16} />
</Button>
<Button
variant="danger"
@@ -1,47 +0,0 @@
import { useTranslation } from 'react-i18next';
import { Modal } from '@/components/ui/Modal';
import { Button } from '@/components/ui/Button';
import type { AuthFileItem } from '@/types';
import styles from '@/pages/AuthFilesPage.module.scss';
export type AuthFileDetailModalProps = {
open: boolean;
file: AuthFileItem | null;
onClose: () => void;
onCopyText: (text: string) => void;
};
export function AuthFileDetailModal({ open, file, onClose, onCopyText }: AuthFileDetailModalProps) {
const { t } = useTranslation();
return (
<Modal
open={open}
onClose={onClose}
title={file?.name || t('auth_files.title_section')}
footer={
<>
<Button variant="secondary" onClick={onClose}>
{t('common.close')}
</Button>
<Button
onClick={() => {
if (!file) return;
const text = JSON.stringify(file, null, 2);
onCopyText(text);
}}
>
{t('common.copy')}
</Button>
</>
}
>
{file && (
<div className={styles.detailContent}>
<pre className={styles.jsonContent}>{JSON.stringify(file, null, 2)}</pre>
</div>
)}
</Modal>
);
}
@@ -17,13 +17,24 @@ export type AuthFilesPrefixProxyEditorModalProps = {
updatedText: string;
dirty: boolean;
onClose: () => void;
onCopyText: (text: string) => void | Promise<void>;
onSave: () => void;
onChange: (field: PrefixProxyEditorField, value: PrefixProxyEditorFieldValue) => void;
};
export function AuthFilesPrefixProxyEditorModal(props: AuthFilesPrefixProxyEditorModalProps) {
const { t } = useTranslation();
const { disableControls, editor, updatedText, dirty, onClose, onSave, onChange } = props;
const { disableControls, editor, updatedText, dirty, onClose, onCopyText, onSave, onChange } =
props;
const formatJsonText = (text: string) => {
if (!text) return '';
try {
return JSON.stringify(JSON.parse(text), null, 2);
} catch {
return text;
}
};
const previewText = formatJsonText(updatedText);
return (
<Modal
@@ -39,7 +50,17 @@ export function AuthFilesPrefixProxyEditorModal(props: AuthFilesPrefixProxyEdito
footer={
<>
<Button variant="secondary" onClick={onClose} disabled={editor?.saving === true}>
{t('common.cancel')}
{dirty ? t('common.cancel') : t('common.close')}
</Button>
<Button
variant="secondary"
onClick={() => {
if (!updatedText) return;
void onCopyText(updatedText);
}}
disabled={editor?.saving === true || !updatedText}
>
{t('common.copy')}
</Button>
<Button
onClick={onSave}
@@ -61,6 +82,17 @@ export function AuthFilesPrefixProxyEditorModal(props: AuthFilesPrefixProxyEdito
) : (
<>
{editor.error && <div className={styles.prefixProxyError}>{editor.error}</div>}
<div className={styles.prefixProxyJsonWrapper}>
<label className={styles.prefixProxyLabel}>
{t('auth_files.prefix_proxy_info_label')}
</label>
<textarea
className={styles.prefixProxyTextarea}
rows={8}
readOnly
value={editor.fileInfoText}
/>
</div>
<div className={styles.prefixProxyJsonWrapper}>
<label className={styles.prefixProxyLabel}>
{t('auth_files.prefix_proxy_source_label')}
@@ -69,7 +101,7 @@ export function AuthFilesPrefixProxyEditorModal(props: AuthFilesPrefixProxyEdito
className={styles.prefixProxyTextarea}
rows={10}
readOnly
value={updatedText}
value={previewText}
/>
</div>
<div className={styles.prefixProxyFields}>
@@ -27,6 +27,7 @@ export type PrefixProxyEditorFieldValue = string | boolean;
export type PrefixProxyEditorState = {
fileName: string;
fileInfoText: string;
isCodexFile: boolean;
loading: boolean;
saving: boolean;
@@ -54,7 +55,7 @@ export type UseAuthFilesPrefixProxyEditorResult = {
prefixProxyEditor: PrefixProxyEditorState | null;
prefixProxyUpdatedText: string;
prefixProxyDirty: boolean;
openPrefixProxyEditor: (file: Pick<AuthFileItem, 'name' | 'type' | 'provider'>) => Promise<void>;
openPrefixProxyEditor: (file: AuthFileItem) => Promise<void>;
closePrefixProxyEditor: () => void;
handlePrefixProxyChange: (
field: PrefixProxyEditorField,
@@ -127,7 +128,7 @@ export function useAuthFilesPrefixProxyEditor(
setPrefixProxyEditor(null);
};
const openPrefixProxyEditor = async (file: Pick<AuthFileItem, 'name' | 'type' | 'provider'>) => {
const openPrefixProxyEditor = async (file: AuthFileItem) => {
const name = file.name;
const normalizedType = String(file.type ?? '')
.trim()
@@ -145,6 +146,7 @@ export function useAuthFilesPrefixProxyEditor(
setPrefixProxyEditor({
fileName: name,
fileInfoText: JSON.stringify(file, null, 2),
isCodexFile,
loading: true,
saving: false,