Compare commits

...

18 Commits

Author SHA1 Message Date
Supra4E8C
6837100dec feat(app.js, i18n, styles): implement custom headers functionality in API key management
- Added the ability to input custom HTTP headers for API keys in the CLIProxyManager.
- Introduced new methods for adding, populating, collecting, and applying custom headers.
- Updated the UI to include input fields for custom headers in the modals for adding and editing API keys.
- Enhanced internationalization strings to support custom headers in both English and Chinese.
- Added corresponding styles for the new header input fields and badges in the UI.
2025-11-09 18:27:29 +08:00
Supra4E8C
8542041981 feat(app.js, i18n): support batch addition of Gemini API keys with enhanced UI
- Updated the Gemini API key modal to allow input of multiple keys, each on a new line.
- Added a hint for users on how to input multiple keys and updated the corresponding internationalization strings.
- Enhanced the key addition logic to handle success, skipped, and failed counts, providing detailed feedback to users.
- Improved error handling and notifications for batch operations.
2025-11-09 18:06:56 +08:00
Supra4E8C
35ceab0dae feat(app.js, i18n): enhance Gemini API key management with base URL support
- Updated the CLIProxyManager to handle both API keys and optional base URLs for Gemini.
- Added new input fields in the modal for base URL entry during key addition and editing.
- Updated internationalization strings to include labels and placeholders for the base URL in both English and Chinese.
- Improved key normalization and rendering logic to accommodate the new structure.
2025-11-09 17:51:38 +08:00
Supra4E8C
d3fe186df7 refactor(app.js): remove modal click event listener to streamline modal behavior 2025-11-09 17:20:02 +08:00
Supra4E8C
5aff22a20b fix(ui): update modal styles for better responsiveness and overflow handling 2025-11-09 17:17:49 +08:00
hkfires
aa1dedc932 feat(ui): mark runtime-only auth files and disable actions 2025-11-07 22:02:29 +08:00
hkfires
61e75eee97 feat(auth-files): add AI Studio and Gemini CLI with filename masking 2025-11-07 21:46:46 +08:00
hkfires
3a2d96725f fix(ui): align file-item footer and adjust badge spacing 2025-11-06 14:23:07 +08:00
Supra4E8C
8283e99909 fix(app.js):尝试修复codex配置异常丢失数据的问题 2025-11-05 18:06:22 +08:00
hkfires
181cba6886 feat(logs): limit lines, incremental updates, and highlighting 2025-11-03 18:44:50 +08:00
hkfires
aa729914c5 fix(ui): position API Keys/Providers action buttons; prevent overlap 2025-11-03 16:31:29 +08:00
hkfires
f98f31f2ed fix(i18n, ui): correct syntax error in generateDynamicTypeLabel method
- Move generateDynamicTypeLabel method from inside updateAuthFileFilterButtons to proper class method location
- Fix JavaScript syntax error that prevented app from loading
- Preserve all internationalization functionality from 257260b

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 16:05:51 +08:00
hkfires
1e79f918e2 fix(ui): harden key list rendering and config handling 2025-10-31 16:24:43 +08:00
Supra4E8C
257260b1d2 feat(i18n, ui): enhance file type filtering with internationalization support
- Updated the file type display logic to utilize i18n for dynamic translations.
- Refactored filter button definitions to include data attributes for improved localization.
- Added new translation keys for various file types and filter options in both English and Chinese.
- Implemented a method to refresh button texts upon language change, ensuring consistent UI updates.
2025-10-26 17:46:57 +08:00
Supra4E8C
8372906820 为筛选按钮容器 .auth-file-filter 改用事件委托,并在重新渲染时移除旧监听器,避免多次绑定导致回调重复执行。 2025-10-26 17:33:06 +08:00
Supra4E8C
5feea2e345 Merge pull request #10 from coulsontl/new_ui
feat(ui): 添加类型筛选和详细信息视图来优化认证文件管理,并优化布局结构
2025-10-26 17:23:09 +08:00
coulsontl
825ad53c2c refactor(ui): streamline auth file actions with event delegation
- Updated the auth file action buttons to use data attributes for improved event handling.
- Implemented event delegation for button actions (show details, download, delete) to enhance performance and maintainability.
- Added a hidden class in CSS to manage file item visibility more effectively.
- Refactored modal button actions to utilize event delegation for better code organization.
2025-10-26 17:09:33 +08:00
coulsontl
3e9413172c feat(ui): enhance auth file management with type filtering and detailed view
- Implemented file type collection and dynamic filter button updates based on available file types.
- Added a detailed view for auth files, displaying JSON content in a modal with copy functionality.
- Improved UI layout for file items and added responsive design for better usability.
2025-10-26 16:43:06 +08:00
5 changed files with 1950 additions and 147 deletions

1194
app.js

File diff suppressed because it is too large Load Diff

72
i18n.js
View File

@@ -40,6 +40,12 @@ const i18n = {
'common.alias': '别名', 'common.alias': '别名',
'common.failure': '失败', 'common.failure': '失败',
'common.unknown_error': '未知错误', 'common.unknown_error': '未知错误',
'common.copy': '复制',
'common.custom_headers_label': '自定义请求头',
'common.custom_headers_hint': '可选,设置需要附带到请求中的 HTTP 头,名称和值均不能为空。',
'common.custom_headers_add': '添加请求头',
'common.custom_headers_key_placeholder': 'Header 名称,例如 X-Custom-Header',
'common.custom_headers_value_placeholder': 'Header 值',
// 页面标题 // 页面标题
'title.main': 'CLI Proxy API Management Center', 'title.main': 'CLI Proxy API Management Center',
@@ -130,10 +136,14 @@ const i18n = {
'ai_providers.gemini_empty_desc': '点击上方按钮添加第一个密钥', 'ai_providers.gemini_empty_desc': '点击上方按钮添加第一个密钥',
'ai_providers.gemini_item_title': 'Gemini密钥', 'ai_providers.gemini_item_title': 'Gemini密钥',
'ai_providers.gemini_add_modal_title': '添加Gemini API密钥', 'ai_providers.gemini_add_modal_title': '添加Gemini API密钥',
'ai_providers.gemini_add_modal_key_label': 'API密钥:', 'ai_providers.gemini_add_modal_key_label': 'API密钥列表:',
'ai_providers.gemini_add_modal_key_placeholder': '请输入Gemini API密钥', 'ai_providers.gemini_add_modal_key_placeholder': '请输入Gemini API密钥(每行一个)',
'ai_providers.gemini_add_modal_key_hint': '可一次粘贴多个密钥,每行一个。',
'ai_providers.gemini_add_modal_url_label': 'Base URL (可选):',
'ai_providers.gemini_add_modal_url_placeholder': '例如: https://generativelanguage.googleapis.com',
'ai_providers.gemini_edit_modal_title': '编辑Gemini API密钥', 'ai_providers.gemini_edit_modal_title': '编辑Gemini API密钥',
'ai_providers.gemini_edit_modal_key_label': 'API密钥:', 'ai_providers.gemini_edit_modal_key_label': 'API密钥:',
'ai_providers.gemini_edit_modal_url_label': 'Base URL (可选):',
'ai_providers.gemini_delete_confirm': '确定要删除这个Gemini密钥吗', 'ai_providers.gemini_delete_confirm': '确定要删除这个Gemini密钥吗',
'ai_providers.codex_title': 'Codex API 配置', 'ai_providers.codex_title': 'Codex API 配置',
@@ -221,6 +231,25 @@ const i18n = {
'auth_files.delete_success': '文件删除成功', 'auth_files.delete_success': '文件删除成功',
'auth_files.delete_all_success': '成功删除', 'auth_files.delete_all_success': '成功删除',
'auth_files.files_count': '个文件', 'auth_files.files_count': '个文件',
'auth_files.filter_all': '全部',
'auth_files.filter_qwen': 'Qwen',
'auth_files.filter_gemini': 'Gemini',
'auth_files.filter_gemini-cli': 'GeminiCLI',
'auth_files.filter_aistudio': 'AIStudio',
'auth_files.filter_claude': 'Claude',
'auth_files.filter_codex': 'Codex',
'auth_files.filter_iflow': 'iFlow',
'auth_files.filter_empty': '空文件',
'auth_files.filter_unknown': '其他',
'auth_files.type_qwen': 'Qwen',
'auth_files.type_gemini': 'Gemini',
'auth_files.type_gemini-cli': 'GeminiCLI',
'auth_files.type_aistudio': 'AIStudio',
'auth_files.type_claude': 'Claude',
'auth_files.type_codex': 'Codex',
'auth_files.type_iflow': 'iFlow',
'auth_files.type_empty': '空文件',
'auth_files.type_unknown': '其他',
// Codex OAuth // Codex OAuth
@@ -380,6 +409,9 @@ const i18n = {
'notification.gemini_key_added': 'Gemini密钥添加成功', 'notification.gemini_key_added': 'Gemini密钥添加成功',
'notification.gemini_key_updated': 'Gemini密钥更新成功', 'notification.gemini_key_updated': 'Gemini密钥更新成功',
'notification.gemini_key_deleted': 'Gemini密钥删除成功', 'notification.gemini_key_deleted': 'Gemini密钥删除成功',
'notification.gemini_multi_input_required': '请先输入至少一个Gemini密钥',
'notification.gemini_multi_failed': 'Gemini密钥批量添加失败',
'notification.gemini_multi_summary': 'Gemini批量添加完成成功 {success},跳过 {skipped},失败 {failed}',
'notification.codex_config_added': 'Codex配置添加成功', 'notification.codex_config_added': 'Codex配置添加成功',
'notification.codex_config_updated': 'Codex配置更新成功', 'notification.codex_config_updated': 'Codex配置更新成功',
'notification.codex_config_deleted': 'Codex配置删除成功', 'notification.codex_config_deleted': 'Codex配置删除成功',
@@ -466,6 +498,12 @@ const i18n = {
'common.alias': 'Alias', 'common.alias': 'Alias',
'common.failure': 'Failure', 'common.failure': 'Failure',
'common.unknown_error': 'Unknown error', 'common.unknown_error': 'Unknown error',
'common.copy': 'Copy',
'common.custom_headers_label': 'Custom Headers',
'common.custom_headers_hint': 'Optional HTTP headers to send with the request. Leave blank to remove.',
'common.custom_headers_add': 'Add Header',
'common.custom_headers_key_placeholder': 'Header name, e.g. X-Custom-Header',
'common.custom_headers_value_placeholder': 'Header value',
// Page titles // Page titles
'title.main': 'CLI Proxy API Management Center', 'title.main': 'CLI Proxy API Management Center',
@@ -556,10 +594,14 @@ const i18n = {
'ai_providers.gemini_empty_desc': 'Click the button above to add the first key', 'ai_providers.gemini_empty_desc': 'Click the button above to add the first key',
'ai_providers.gemini_item_title': 'Gemini Key', 'ai_providers.gemini_item_title': 'Gemini Key',
'ai_providers.gemini_add_modal_title': 'Add Gemini API Key', 'ai_providers.gemini_add_modal_title': 'Add Gemini API Key',
'ai_providers.gemini_add_modal_key_label': 'API Key:', 'ai_providers.gemini_add_modal_key_label': 'API Keys:',
'ai_providers.gemini_add_modal_key_placeholder': 'Please enter Gemini API key', 'ai_providers.gemini_add_modal_key_placeholder': 'Enter Gemini API keys (one per line)',
'ai_providers.gemini_add_modal_key_hint': 'You can paste multiple keys, one per line.',
'ai_providers.gemini_add_modal_url_label': 'Base URL (optional):',
'ai_providers.gemini_add_modal_url_placeholder': 'e.g. https://generativelanguage.googleapis.com',
'ai_providers.gemini_edit_modal_title': 'Edit Gemini API Key', 'ai_providers.gemini_edit_modal_title': 'Edit Gemini API Key',
'ai_providers.gemini_edit_modal_key_label': 'API Key:', 'ai_providers.gemini_edit_modal_key_label': 'API Key:',
'ai_providers.gemini_edit_modal_url_label': 'Base URL (optional):',
'ai_providers.gemini_delete_confirm': 'Are you sure you want to delete this Gemini key?', 'ai_providers.gemini_delete_confirm': 'Are you sure you want to delete this Gemini key?',
'ai_providers.codex_title': 'Codex API Configuration', 'ai_providers.codex_title': 'Codex API Configuration',
@@ -647,6 +689,25 @@ const i18n = {
'auth_files.delete_success': 'File deleted successfully', 'auth_files.delete_success': 'File deleted successfully',
'auth_files.delete_all_success': 'Successfully deleted', 'auth_files.delete_all_success': 'Successfully deleted',
'auth_files.files_count': 'files', 'auth_files.files_count': 'files',
'auth_files.filter_all': 'All',
'auth_files.filter_qwen': 'Qwen',
'auth_files.filter_gemini': 'Gemini',
'auth_files.filter_gemini-cli': 'GeminiCLI',
'auth_files.filter_aistudio': 'AIStudio',
'auth_files.filter_claude': 'Claude',
'auth_files.filter_codex': 'Codex',
'auth_files.filter_iflow': 'iFlow',
'auth_files.filter_empty': 'Empty',
'auth_files.filter_unknown': 'Other',
'auth_files.type_qwen': 'Qwen',
'auth_files.type_gemini': 'Gemini',
'auth_files.type_gemini-cli': 'GeminiCLI',
'auth_files.type_aistudio': 'AIStudio',
'auth_files.type_claude': 'Claude',
'auth_files.type_codex': 'Codex',
'auth_files.type_iflow': 'iFlow',
'auth_files.type_empty': 'Empty',
'auth_files.type_unknown': 'Other',
// Codex OAuth // Codex OAuth
'auth_login.codex_oauth_title': 'Codex OAuth', 'auth_login.codex_oauth_title': 'Codex OAuth',
@@ -805,6 +866,9 @@ const i18n = {
'notification.gemini_key_added': 'Gemini key added successfully', 'notification.gemini_key_added': 'Gemini key added successfully',
'notification.gemini_key_updated': 'Gemini key updated successfully', 'notification.gemini_key_updated': 'Gemini key updated successfully',
'notification.gemini_key_deleted': 'Gemini key deleted successfully', 'notification.gemini_key_deleted': 'Gemini key deleted successfully',
'notification.gemini_multi_input_required': 'Please enter at least one Gemini key',
'notification.gemini_multi_failed': 'Gemini bulk add failed',
'notification.gemini_multi_summary': 'Gemini bulk add finished: {success} added, {skipped} skipped, {failed} failed',
'notification.codex_config_added': 'Codex configuration added successfully', 'notification.codex_config_added': 'Codex configuration added successfully',
'notification.codex_config_updated': 'Codex configuration updated successfully', 'notification.codex_config_updated': 'Codex configuration updated successfully',
'notification.codex_config_deleted': 'Codex configuration deleted successfully', 'notification.codex_config_deleted': 'Codex configuration deleted successfully',

View File

@@ -418,9 +418,23 @@
<!-- 认证文件 --> <!-- 认证文件 -->
<div class="card"> <div class="card">
<div class="card-header"> <div class="card-header card-header-with-filter">
<h3><i class="fas fa-file-alt"></i> <span <div class="header-left">
data-i18n="auth_files.title_section">认证文件</span></h3> <h3><i class="fas fa-file-alt"></i> <span
data-i18n="auth_files.title_section">认证文件</span></h3>
<!-- 类型筛选 -->
<div class="auth-file-filter">
<button class="filter-btn active" data-type="all" data-i18n-text="auth_files.filter_all">All</button>
<button class="filter-btn" data-type="qwen" data-i18n-text="auth_files.filter_qwen">Qwen</button>
<button class="filter-btn" data-type="gemini" data-i18n-text="auth_files.filter_gemini">Gemini</button>
<button class="filter-btn" data-type="gemini-cli" data-i18n-text="auth_files.filter_gemini-cli">GeminiCLI</button>
<button class="filter-btn" data-type="aistudio" data-i18n-text="auth_files.filter_aistudio">AIStudio</button>
<button class="filter-btn" data-type="claude" data-i18n-text="auth_files.filter_claude">Claude</button>
<button class="filter-btn" data-type="codex" data-i18n-text="auth_files.filter_codex">Codex</button>
<button class="filter-btn" data-type="iflow" data-i18n-text="auth_files.filter_iflow">iFlow</button>
<button class="filter-btn" data-type="empty" data-i18n-text="auth_files.filter_empty">Empty</button>
</div>
</div>
<div class="header-actions"> <div class="header-actions">
<button id="upload-auth-file" class="btn btn-primary"> <button id="upload-auth-file" class="btn btn-primary">
<i class="fas fa-upload"></i> <span <i class="fas fa-upload"></i> <span
@@ -433,7 +447,7 @@
</div> </div>
</div> </div>
<div class="card-content"> <div class="card-content">
<div id="auth-files-list" class="file-list"></div> <div id="auth-files-list" class="file-list file-grid"></div>
<input type="file" id="auth-file-input" accept=".json" style="display: none;"> <input type="file" id="auth-file-input" accept=".json" style="display: none;">
</div> </div>
</div> </div>

View File

@@ -26,6 +26,5 @@
"repository": { "repository": {
"type": "git", "type": "git",
"url": "local" "url": "local"
}, }
"dependencies": {}
} }

View File

@@ -1195,6 +1195,76 @@ body {
font-size: 14px; font-size: 14px;
} }
/* 小按钮样式 - 与stat-badge高度一致 */
.btn-small {
padding: 4px 12px;
border: 1px solid transparent;
border-radius: 4px;
cursor: pointer;
font-size: 13px;
font-weight: 500;
transition: all 0.2s ease;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 5px;
min-height: 28px;
white-space: nowrap;
}
.btn-small.btn-primary {
background: var(--primary-color);
color: white;
border-color: var(--primary-color);
}
.btn-small.btn-primary:hover {
background: var(--primary-hover);
box-shadow: 0 2px 4px rgba(59, 130, 246, 0.3);
transform: translateY(-1px);
}
.btn-small.btn-danger {
background: #ef4444;
color: white;
border-color: #ef4444;
}
.btn-small.btn-danger:hover {
background: #dc2626;
box-shadow: 0 2px 4px rgba(239, 68, 68, 0.3);
transform: translateY(-1px);
}
.btn-small.btn-info {
background: #06b6d4;
color: white;
border-color: #06b6d4;
}
.btn-small.btn-info:hover {
background: #0891b2;
box-shadow: 0 2px 4px rgba(6, 182, 212, 0.3);
transform: translateY(-1px);
}
.btn-small i {
font-size: 13px;
}
/* 响应式调整 */
@media (max-width: 768px) {
.btn-small {
padding: 3px 10px;
font-size: 12px;
min-height: 26px;
}
.btn-small i {
font-size: 12px;
}
}
/* 表单元素 */ /* 表单元素 */
.form-group { .form-group {
margin-bottom: 18px; margin-bottom: 18px;
@@ -1454,10 +1524,189 @@ input:checked+.slider:before {
/* 认证文件列表填满页面,保留版本信息空间 */ /* 认证文件列表填满页面,保留版本信息空间 */
max-height: calc(100vh - 300px); /* 减去导航栏、padding和版本信息的高度 */ max-height: calc(100vh - 300px); /* 减去导航栏、padding和版本信息的高度 */
overflow-y: auto; overflow-y: auto;
margin-right: -12px; /* 向右扩展 */
padding-right: 12px; /* 滚动条不贴着最右侧卡片,保持左右边距一致 */
} }
/* 认证文件Grid布局 */
.file-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 15px;
}
/* 响应式中等屏幕2列 */
@media (max-width: 1400px) {
.file-grid {
grid-template-columns: repeat(2, 1fr);
}
}
/* 响应式:小屏幕单列 */
@media (max-width: 768px) {
.file-grid {
grid-template-columns: 1fr;
}
}
/* 带筛选器的卡片头部布局 */
.card-header-with-filter {
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
gap: 15px;
}
.card-header-with-filter .header-left {
display: flex;
align-items: center;
gap: 20px;
flex: 1;
flex-wrap: wrap;
}
.card-header-with-filter .header-left h3 {
margin: 0;
white-space: nowrap;
}
/* 认证文件筛选按钮 */
.auth-file-filter {
display: flex;
gap: 8px;
flex-wrap: wrap;
}
.filter-btn {
padding: 6px 16px;
border: 1px solid var(--border-primary);
background: var(--bg-quaternary);
color: var(--text-secondary);
border-radius: 6px;
cursor: pointer;
font-size: 0.9rem;
transition: all 0.2s ease;
}
.filter-btn:hover {
background: var(--bg-tertiary);
border-color: var(--border-secondary);
}
.filter-btn.active {
background: var(--primary-color);
color: white;
border-color: var(--primary-color);
}
/* 文件类型标签 */
.file-type-badge {
display: inline-block;
padding: 2px 8px;
border-radius: 4px;
font-size: 0.75rem;
font-weight: 600;
margin-right: 8px;
}
.file-type-badge.qwen {
background: #e8f5e9;
color: #2e7d32;
}
[data-theme="dark"] .file-type-badge.qwen {
background: #1b5e20;
color: #81c784;
}
.file-type-badge.gemini {
background: #e3f2fd;
color: #1565c0;
}
[data-theme="dark"] .file-type-badge.gemini {
background: #0d47a1;
color: #64b5f6;
}
.file-type-badge.claude {
background: #fce4ec;
color: #c2185b;
}
[data-theme="dark"] .file-type-badge.claude {
background: #880e4f;
color: #f48fb1;
}
.file-type-badge.codex {
background: #fff3e0;
color: #ef6c00;
}
[data-theme="dark"] .file-type-badge.codex {
background: #e65100;
color: #ffb74d;
}
.file-type-badge.iflow {
background: #f3e5f5;
color: #7b1fa2;
}
[data-theme="dark"] .file-type-badge.iflow {
background: #4a148c;
color: #ce93d8;
}
.file-type-badge.empty {
background: #f5f5f5;
color: #616161;
}
[data-theme="dark"] .file-type-badge.empty {
background: #424242;
color: #bdbdbd;
}
/* 未知类型通用样式 */
.file-type-badge:not(.qwen):not(.gemini):not(.claude):not(.codex):not(.iflow):not(.empty) {
background: #f0f0f0;
color: #666666;
border: 1px dashed #999999;
}
[data-theme="dark"] .file-type-badge:not(.qwen):not(.gemini):not(.claude):not(.codex):not(.iflow):not(.empty) {
background: #3a3a3a;
color: #aaaaaa;
border: 1px dashed #666666;
}
/* 虚拟认证文件标记样式 */
.virtual-auth-badge {
background: linear-gradient(135deg, var(--primary-color) 0%, var(--primary-hover) 100%);
color: white;
font-size: 13px;
padding: 4px 8px;
border-radius: 12px;
font-weight: 500;
text-align: center;
display: inline-block;
width: 100%;
box-shadow: 0 2px 4px rgba(59, 130, 246, 0.25);
letter-spacing: 0.3px;
}
[data-theme="dark"] .virtual-auth-badge {
background: linear-gradient(135deg, var(--primary-color) 0%, var(--primary-hover) 100%);
box-shadow: 0 2px 4px rgba(96, 165, 250, 0.25);
}
.provider-list { .provider-list {
/* 默认不限制高度,动态设置 */ /* 默认不限制高度,动态设置 */
min-height: 0;
} }
.key-item, .key-item,
@@ -1469,9 +1718,13 @@ input:checked+.slider:before {
padding: 15px; padding: 15px;
margin-bottom: 10px; margin-bottom: 10px;
display: flex; display: flex;
justify-content: space-between; flex-direction: column;
align-items: center;
transition: all 0.3s ease; transition: all 0.3s ease;
position: relative;
}
.file-item.hidden {
display: none;
} }
.key-item:hover, .key-item:hover,
@@ -1484,16 +1737,103 @@ input:checked+.slider:before {
.item-content { .item-content {
flex: 1; flex: 1;
width: 100%;
}
/* 为 API Keys 和 AI Providers 的项优化按钮位置 */
.key-item .item-content,
.provider-item .item-content {
padding-right: 120px; /* 为按钮预留空间,防止内容重叠 */
min-width: 0; /* 确保内容可以正常换行 */
}
.item-footer {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 12px;
flex-wrap: wrap;
gap: 10px;
align-items: flex-start;
}
.item-stats {
display: flex;
gap: 8px;
flex-wrap: wrap;
align-items: center;
} }
.item-actions { .item-actions {
display: flex; display: flex;
gap: 8px; gap: 8px;
flex-wrap: nowrap;
align-items: center;
justify-content: flex-end;
}
/* 认证文件项的底部布局优化 - 确保状态标签和操作按钮垂直对齐 */
.file-item .item-footer {
align-items: center; /* 垂直居中对齐 */
gap: 12px; /* 增加状态标签和按钮之间的间距 */
}
.file-item .item-stats {
align-items: center; /* 状态徽章垂直居中 */
margin: 0; /* 移除默认外边距 */
}
.file-item .item-actions {
align-items: center; /* 操作按钮垂直居中 */
gap: 6px; /* 按钮之间适当间距 */
}
/* API Keys 和 AI Providers 的按钮组 - 绝对定位到右侧垂直居中 */
.key-item .item-actions,
.provider-item .item-actions {
position: absolute;
top: 50%;
right: 15px;
transform: translateY(-50%);
gap: 8px;
}
/* 按钮样式优化 */
.key-item .item-actions .btn,
.provider-item .item-actions .btn {
min-width: 40px;
height: 40px;
padding: 0;
display: flex;
align-items: center;
justify-content: center;
border-radius: 8px;
transition: all 0.3s ease;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.key-item .item-actions .btn:hover,
.provider-item .item-actions .btn:hover {
transform: scale(1.05);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
}
.key-item .item-actions .btn i,
.provider-item .item-actions .btn i {
font-size: 16px;
margin: 0;
} }
.item-title { .item-title {
font-weight: 600; font-weight: 600;
color: var(--text-secondary); color: var(--text-secondary);
margin-bottom: 6px;
}
.item-meta {
display: flex;
gap: 16px;
flex-wrap: wrap;
margin-bottom: 4px; margin-bottom: 4px;
} }
@@ -1572,6 +1912,150 @@ input:checked+.slider:before {
color: var(--text-tertiary); color: var(--text-tertiary);
} }
/* JSON模态框 */
.json-modal {
position: fixed;
z-index: 2000;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: var(--bg-modal);
backdrop-filter: blur(5px);
display: flex;
align-items: center;
justify-content: center;
animation: fadeIn 0.2s ease;
}
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
.json-modal-content {
background-color: var(--bg-secondary);
border-radius: 12px;
width: 90%;
max-width: 800px;
max-height: 90vh;
box-shadow: var(--shadow-modal);
display: flex;
flex-direction: column;
animation: slideUp 0.3s ease;
}
@keyframes slideUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.json-modal-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20px 24px;
border-bottom: 1px solid var(--border-primary);
}
.json-modal-header h3 {
margin: 0;
color: var(--text-secondary);
font-size: 1.1rem;
font-weight: 600;
display: flex;
align-items: center;
gap: 10px;
}
.json-modal-close {
background: none;
border: none;
color: var(--text-tertiary);
font-size: 24px;
cursor: pointer;
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
transition: all 0.2s ease;
}
.json-modal-close:hover {
background: var(--bg-tertiary);
color: var(--text-secondary);
}
.json-modal-body {
flex: 1;
overflow-y: auto;
padding: 20px 24px;
min-height: 200px;
max-height: calc(90vh - 160px);
}
.json-content {
background: var(--bg-tertiary);
border: 1px solid var(--border-primary);
border-radius: 8px;
padding: 16px;
margin: 0;
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', 'Courier New', monospace;
font-size: 13px;
line-height: 1.6;
color: var(--text-secondary);
overflow-x: auto;
white-space: pre;
}
.json-modal-footer {
display: flex;
justify-content: flex-end;
align-items: center;
gap: 10px;
padding: 16px 24px;
border-top: 1px solid var(--border-primary);
}
@media (max-width: 768px) {
.json-modal-content {
width: 95%;
max-height: 95vh;
}
.json-modal-header,
.json-modal-body,
.json-modal-footer {
padding: 16px;
}
.json-content {
font-size: 12px;
padding: 12px;
}
.json-modal-footer {
flex-wrap: wrap;
}
.json-modal-footer .btn {
flex: 1;
min-width: 80px;
}
}
/* 模态框 */ /* 模态框 */
.modal { .modal {
display: none; display: none;
@@ -1592,9 +2076,13 @@ input:checked+.slider:before {
border-radius: 15px; border-radius: 15px;
width: 90%; width: 90%;
max-width: 550px; max-width: 550px;
max-height: 90vh;
box-shadow: var(--shadow-modal); box-shadow: var(--shadow-modal);
animation: modalSlideIn 0.3s ease; animation: modalSlideIn 0.3s ease;
position: relative; position: relative;
display: flex;
flex-direction: column;
overflow: hidden;
} }
@keyframes modalSlideIn { @keyframes modalSlideIn {
@@ -1635,6 +2123,9 @@ input:checked+.slider:before {
#modal-body { #modal-body {
padding: 35px 30px 30px 30px; padding: 35px 30px 30px 30px;
flex: 1;
min-height: 0;
overflow-y: auto;
} }
/* 模态框标题样式 */ /* 模态框标题样式 */
@@ -2323,6 +2814,68 @@ input:checked+.slider:before {
align-self: center; align-self: center;
} }
.header-input-list {
display: flex;
flex-direction: column;
gap: 8px;
}
.header-input-row .header-input-group {
display: flex;
align-items: center;
gap: 8px;
}
.header-key-input {
flex: 1;
}
.header-value-input {
flex: 2;
}
.header-separator {
color: var(--text-tertiary);
}
.header-badges-wrapper {
display: flex;
flex-direction: column;
gap: 4px;
}
.header-badge-list {
display: flex;
flex-wrap: wrap;
gap: 6px;
}
.header-badge {
background: var(--accent-tertiary);
border: 1px solid var(--border-primary);
border-radius: 12px;
padding: 4px 8px;
font-size: 12px;
color: var(--text-secondary);
display: inline-flex;
gap: 4px;
}
.header-badge strong {
font-weight: 600;
color: var(--text-primary);
}
[data-theme="dark"] .header-badge {
background: rgba(59, 130, 246, 0.15);
border-color: rgba(59, 130, 246, 0.3);
color: var(--text-secondary);
}
[data-theme="dark"] .header-badge strong {
color: var(--text-secondary);
}
/* Codex OAuth 样式 */ /* Codex OAuth 样式 */
#codex-oauth-content { #codex-oauth-content {
transition: all 0.3s ease; transition: all 0.3s ease;
@@ -2833,9 +3386,10 @@ input:checked+.slider:before {
border: 1px solid var(--border-primary); border: 1px solid var(--border-primary);
border-radius: 6px; border-radius: 6px;
padding: 16px; padding: 16px;
font-size: 12px; font-size: 13px;
line-height: 1.6; line-height: 1.6;
color: var(--text-secondary); color: var(--text-secondary);
font-weight: 600;
white-space: pre-wrap; white-space: pre-wrap;
word-wrap: break-word; word-wrap: break-word;
max-height: calc(100vh - 480px); max-height: calc(100vh - 480px);
@@ -2844,6 +3398,133 @@ input:checked+.slider:before {
margin: 0; margin: 0;
} }
.logs-text .log-line {
display: block;
padding: 2px 0;
margin: 0;
color: inherit;
}
.logs-text .log-line + .log-line {
margin-top: 2px;
}
.logs-text .log-status-tag {
display: inline-block;
padding: 0 8px;
margin-right: 6px;
border-radius: 6px;
font-weight: 700;
letter-spacing: 0.3px;
}
.logs-text .log-status-tag.log-status-2xx {
background: rgba(16, 185, 129, 0.18);
color: #047857;
}
.logs-text .log-status-tag.log-status-3xx {
background: rgba(6, 182, 212, 0.2);
color: #0f766e;
}
.logs-text .log-status-tag.log-status-4xx {
background: rgba(251, 191, 36, 0.26);
color: #92400e;
}
.logs-text .log-status-tag.log-status-5xx {
background: rgba(239, 68, 68, 0.22);
color: #b91c1c;
}
.logs-text .log-status-tag.log-status-1xx {
background: rgba(165, 180, 252, 0.22);
color: #4338ca;
}
/* 日志高亮样式 */
/* 时间戳高亮 */
.logs-text .log-timestamp {
color: #8b5cf6;
font-weight: 600;
}
/* 日志级别高亮 */
.logs-text .log-level {
display: inline-block;
padding: 1px 6px;
border-radius: 4px;
font-weight: 700;
font-size: 0.95em;
letter-spacing: 0.5px;
}
.logs-text .log-level-error {
background: rgba(239, 68, 68, 0.20);
color: #dc2626;
}
.logs-text .log-level-warn {
background: rgba(251, 191, 36, 0.25);
color: #d97706;
}
.logs-text .log-level-info {
background: rgba(59, 130, 246, 0.20);
color: #2563eb;
}
.logs-text .log-level-debug {
background: rgba(107, 114, 128, 0.18);
color: #6b7280;
}
/* HTTP 方法高亮 */
.logs-text .log-http-method {
color: #0891b2;
font-weight: 700;
}
/* 路径/URL 高亮 */
.logs-text .log-path {
color: #059669;
text-decoration: underline;
text-decoration-style: dotted;
text-decoration-color: rgba(5, 150, 105, 0.3);
}
/* IP 地址高亮 */
.logs-text .log-ip {
color: #7c3aed;
font-weight: 600;
}
/* 关键字高亮 - 成功 */
.logs-text .log-keyword-success {
color: #059669;
font-weight: 600;
}
/* 关键字高亮 - 错误 */
.logs-text .log-keyword-error {
color: #dc2626;
font-weight: 600;
}
/* 关键字高亮 - 警告 */
.logs-text .log-keyword-warn {
color: #ea580c;
font-weight: 600;
}
/* 数字和单位高亮 */
.logs-text .log-number-unit {
color: #db2777;
font-weight: 600;
}
.logs-text::-webkit-scrollbar { .logs-text::-webkit-scrollbar {
width: 8px; width: 8px;
height: 8px; height: 8px;
@@ -2977,18 +3658,97 @@ input:checked+.slider:before {
color: var(--text-tertiary); color: var(--text-tertiary);
} }
[data-theme="dark"] .logs-text .log-status-tag.log-status-2xx {
background: rgba(16, 185, 129, 0.32);
color: #6ee7b7;
}
[data-theme="dark"] .logs-text .log-status-tag.log-status-3xx {
background: rgba(6, 182, 212, 0.34);
color: #5eead4;
}
[data-theme="dark"] .logs-text .log-status-tag.log-status-4xx {
background: rgba(251, 191, 36, 0.38);
color: #fcd34d;
}
[data-theme="dark"] .logs-text .log-status-tag.log-status-5xx {
background: rgba(239, 68, 68, 0.38);
color: #fca5a5;
}
[data-theme="dark"] .logs-text .log-status-tag.log-status-1xx {
background: rgba(165, 180, 252, 0.38);
color: #c7d2fe;
}
[data-theme="dark"] .logs-container { [data-theme="dark"] .logs-container {
background: rgba(15, 23, 42, 0.3); background: rgba(15, 23, 42, 0.3);
} }
/* 暗色主题 - 日志高亮样式 */
[data-theme="dark"] .logs-text .log-timestamp {
color: #c4b5fd;
}
[data-theme="dark"] .logs-text .log-level-error {
background: rgba(239, 68, 68, 0.32);
color: #fca5a5;
}
[data-theme="dark"] .logs-text .log-level-warn {
background: rgba(251, 191, 36, 0.35);
color: #fbbf24;
}
[data-theme="dark"] .logs-text .log-level-info {
background: rgba(59, 130, 246, 0.32);
color: #93c5fd;
}
[data-theme="dark"] .logs-text .log-level-debug {
background: rgba(107, 114, 128, 0.28);
color: #9ca3af;
}
[data-theme="dark"] .logs-text .log-http-method {
color: #22d3ee;
}
[data-theme="dark"] .logs-text .log-path {
color: #34d399;
text-decoration-color: rgba(52, 211, 153, 0.3);
}
[data-theme="dark"] .logs-text .log-ip {
color: #a78bfa;
}
[data-theme="dark"] .logs-text .log-keyword-success {
color: #34d399;
}
[data-theme="dark"] .logs-text .log-keyword-error {
color: #f87171;
}
[data-theme="dark"] .logs-text .log-keyword-warn {
color: #fb923c;
}
[data-theme="dark"] .logs-text .log-number-unit {
color: #f472b6;
}
/* ===== AI提供商统计徽章样式 ===== */ /* ===== AI提供商统计徽章样式 ===== */
/* 统计信息容器 */ /* 统计信息容器 */
.item-stats { .item-stats {
display: flex; display: flex;
gap: 8px; gap: 8px;
margin-top: 10px;
flex-wrap: wrap; flex-wrap: wrap;
margin-top: 8px;
} }
/* 统计徽章基础样式 */ /* 统计徽章基础样式 */
@@ -3060,6 +3820,44 @@ input:checked+.slider:before {
font-size: 11px !important; font-size: 11px !important;
} }
} }
/* 响应式设计 - API Keys 和 AI Providers 按钮优化 */
@media (max-width: 768px) {
/* 移动端按钮位置调整 */
.key-item .item-actions,
.provider-item .item-actions {
position: relative;
top: auto;
right: auto;
transform: none;
margin-top: 12px;
justify-content: flex-start;
}
/* 移动端移除内容右侧内边距 */
.key-item .item-content,
.provider-item .item-content {
padding-right: 0;
}
/* 移动端按钮尺寸调整 */
.key-item .item-actions .btn,
.provider-item .item-actions .btn {
min-width: 36px;
height: 36px;
}
.key-item .item-actions .btn:hover,
.provider-item .item-actions .btn:hover {
transform: scale(1.05);
}
.key-item .item-actions .btn i,
.provider-item .item-actions .btn i {
font-size: 14px;
}
}
#config-management .CodeMirror .CodeMirror-lines { #config-management .CodeMirror .CodeMirror-lines {
padding: 12px; padding: 12px;
} }