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

This commit is contained in:
Supra4E8C
2026-03-22 01:29:23 +08:00
Unverified
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,
+3 -2
View File
@@ -552,9 +552,10 @@
"batch_deselect": "Deselect",
"batch_enable": "Enable",
"batch_disable": "Disable",
"prefix_proxy_button": "Edit Auth Fields",
"auth_field_editor_title": "Edit Auth Fields - {{name}}",
"prefix_proxy_button": "Auth File Details / Edit",
"auth_field_editor_title": "Auth File Details / Edit - {{name}}",
"prefix_proxy_loading": "Loading auth file...",
"prefix_proxy_info_label": "Auth file info (info)",
"prefix_proxy_source_label": "Auth file JSON (preview)",
"prefix_label": "Prefix (prefix)",
"proxy_url_label": "Proxy URL (proxy_url)",
+3 -2
View File
@@ -552,9 +552,10 @@
"batch_deselect": "Отменить",
"batch_enable": "Включить",
"batch_disable": "Отключить",
"prefix_proxy_button": "Редактировать поля файла авторизации",
"auth_field_editor_title": "Редактировать поля файла авторизации - {{name}}",
"prefix_proxy_button": "Просмотр / редактирование файла авторизации",
"auth_field_editor_title": "Просмотр / редактирование файла авторизации - {{name}}",
"prefix_proxy_loading": "Загрузка файла авторизации...",
"prefix_proxy_info_label": "Информация о файле авторизации (info)",
"prefix_proxy_source_label": "JSON файла авторизации (предпросмотр)",
"prefix_label": "Префикс (prefix)",
"proxy_url_label": "URL прокси (proxy_url)",
+3 -2
View File
@@ -552,9 +552,10 @@
"batch_deselect": "取消选择",
"batch_enable": "启用",
"batch_disable": "禁用",
"prefix_proxy_button": "编辑认证文件字段",
"auth_field_editor_title": "编辑认证文件字段 - {{name}}",
"prefix_proxy_button": "认证文件详情 / 编辑",
"auth_field_editor_title": "认证文件详情 / 编辑 - {{name}}",
"prefix_proxy_loading": "正在加载认证文件...",
"prefix_proxy_info_label": "认证文件信息(info",
"prefix_proxy_source_label": "认证文件 JSON(预览)",
"prefix_label": "前缀(prefix",
"proxy_url_label": "代理 URLproxy_url",
+1 -17
View File
@@ -37,7 +37,6 @@ import {
type ResolvedTheme,
} from '@/features/authFiles/constants';
import { AuthFileCard } from '@/features/authFiles/components/AuthFileCard';
import { AuthFileDetailModal } from '@/features/authFiles/components/AuthFileDetailModal';
import { AuthFileModelsModal } from '@/features/authFiles/components/AuthFileModelsModal';
import { AuthFilesPrefixProxyEditorModal } from '@/features/authFiles/components/AuthFilesPrefixProxyEditorModal';
import { OAuthExcludedCard } from '@/features/authFiles/components/OAuthExcludedCard';
@@ -64,7 +63,6 @@ import {
type AuthFilesSortMode,
} from '@/features/authFiles/uiState';
import { useAuthStore, useNotificationStore, useThemeStore } from '@/stores';
import type { AuthFileItem } from '@/types';
import styles from './AuthFilesPage.module.scss';
const easePower3Out = (progress: number) => 1 - (1 - progress) ** 4;
@@ -109,8 +107,6 @@ export function AuthFilesPage() {
const [page, setPage] = useState(1);
const [pageSize, setPageSize] = useState(9);
const [pageSizeInput, setPageSizeInput] = useState('9');
const [detailModalOpen, setDetailModalOpen] = useState(false);
const [selectedFile, setSelectedFile] = useState<AuthFileItem | null>(null);
const [viewMode, setViewMode] = useState<'diagram' | 'list'>('list');
const [sortMode, setSortMode] = useState<AuthFilesSortMode>('default');
const [batchActionBarVisible, setBatchActionBarVisible] = useState(false);
@@ -374,11 +370,6 @@ export function AuthFilesPage() {
);
const selectedNames = useMemo(() => Array.from(selectedFiles), [selectedFiles]);
const showDetails = (file: AuthFileItem) => {
setSelectedFile(file);
setDetailModalOpen(true);
};
const copyTextWithNotification = useCallback(
async (text: string) => {
const copied = await copyToClipboard(text);
@@ -702,7 +693,6 @@ export function AuthFilesPage() {
keyStats={keyStats}
statusBarCache={statusBarCache}
onShowModels={showModels}
onShowDetails={showDetails}
onDownload={handleDownload}
onOpenPrefixProxyEditor={openPrefixProxyEditor}
onDelete={handleDelete}
@@ -768,13 +758,6 @@ export function AuthFilesPage() {
onDeleteAlias={handleDeleteAlias}
/>
<AuthFileDetailModal
open={detailModalOpen}
file={selectedFile}
onClose={() => setDetailModalOpen(false)}
onCopyText={copyTextWithNotification}
/>
<AuthFileModelsModal
open={modelsModalOpen}
fileName={modelsFileName}
@@ -793,6 +776,7 @@ export function AuthFilesPage() {
updatedText={prefixProxyUpdatedText}
dirty={prefixProxyDirty}
onClose={closePrefixProxyEditor}
onCopyText={copyTextWithNotification}
onSave={handlePrefixProxySave}
onChange={handlePrefixProxyChange}
/>