mirror of
https://github.com/router-for-me/Cli-Proxy-API-Management-Center.git
synced 2026-02-03 03:10:50 +08:00
Add files via upload
This commit is contained in:
246
app.js
246
app.js
@@ -38,7 +38,6 @@ class CLIProxyManager {
|
|||||||
if (savedTheme && ['light', 'dark'].includes(savedTheme)) {
|
if (savedTheme && ['light', 'dark'].includes(savedTheme)) {
|
||||||
this.currentTheme = savedTheme;
|
this.currentTheme = savedTheme;
|
||||||
} else {
|
} else {
|
||||||
// 根据系统偏好自动选择
|
|
||||||
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
|
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
|
||||||
this.currentTheme = 'dark';
|
this.currentTheme = 'dark';
|
||||||
} else {
|
} else {
|
||||||
@@ -586,6 +585,13 @@ class CLIProxyManager {
|
|||||||
addOpenaiProvider.addEventListener('click', () => this.showAddOpenAIProviderModal());
|
addOpenaiProvider.addEventListener('click', () => this.showAddOpenAIProviderModal());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Gemini Web Token
|
||||||
|
const geminiWebTokenBtn = document.getElementById('gemini-web-token-btn');
|
||||||
|
if (geminiWebTokenBtn) {
|
||||||
|
geminiWebTokenBtn.addEventListener('click', () => this.saveGeminiWebTokenDirect());
|
||||||
|
}
|
||||||
|
|
||||||
// 认证文件管理
|
// 认证文件管理
|
||||||
const uploadAuthFile = document.getElementById('upload-auth-file');
|
const uploadAuthFile = document.getElementById('upload-auth-file');
|
||||||
const deleteAllAuthFiles = document.getElementById('delete-all-auth-files');
|
const deleteAllAuthFiles = document.getElementById('delete-all-auth-files');
|
||||||
@@ -726,8 +732,7 @@ class CLIProxyManager {
|
|||||||
this.managementKey = savedKey;
|
this.managementKey = savedKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 注意:不再处理DOM元素,因为认证配置已改为只读显示
|
|
||||||
// DOM更新由updateConnectionInfo()方法处理
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// API 请求方法
|
// API 请求方法
|
||||||
@@ -2187,6 +2192,241 @@ class CLIProxyManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// === OAuth 认证方法 ===
|
||||||
|
|
||||||
|
// 显示/隐藏 Gemini CLI 项目表单
|
||||||
|
showGeminiCliProjectForm() {
|
||||||
|
document.getElementById('gemini-cli-project-group').style.display = 'block';
|
||||||
|
}
|
||||||
|
|
||||||
|
hideGeminiCliProjectForm() {
|
||||||
|
document.getElementById('gemini-cli-project-group').style.display = 'none';
|
||||||
|
document.getElementById('gemini-cli-project-id').value = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 开始 Anthropic (Claude) OAuth 认证
|
||||||
|
async startAnthropicAuth() {
|
||||||
|
try {
|
||||||
|
const response = await this.makeRequest('/anthropic-auth-url');
|
||||||
|
this.handleOAuthFlow(response.url, 'Anthropic (Claude)');
|
||||||
|
} catch (error) {
|
||||||
|
this.showNotification(`启动 Anthropic 认证失败: ${error.message}`, 'error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 开始 Codex OAuth 认证
|
||||||
|
async startCodexAuth() {
|
||||||
|
try {
|
||||||
|
const response = await this.makeRequest('/codex-auth-url');
|
||||||
|
this.handleOAuthFlow(response.url, 'Codex');
|
||||||
|
} catch (error) {
|
||||||
|
this.showNotification(`启动 Codex 认证失败: ${error.message}`, 'error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 开始 Gemini CLI OAuth 认证
|
||||||
|
async startGeminiCliAuth() {
|
||||||
|
try {
|
||||||
|
const projectId = document.getElementById('gemini-cli-project-id').value.trim();
|
||||||
|
let url = '/gemini-cli-auth-url';
|
||||||
|
if (projectId) {
|
||||||
|
url += `?project_id=${encodeURIComponent(projectId)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await this.makeRequest(url);
|
||||||
|
this.hideGeminiCliProjectForm();
|
||||||
|
this.handleOAuthFlow(response.url, 'Gemini CLI');
|
||||||
|
} catch (error) {
|
||||||
|
this.showNotification(`启动 Gemini CLI 认证失败: ${error.message}`, 'error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 处理 OAuth 流程
|
||||||
|
handleOAuthFlow(authUrl, providerName) {
|
||||||
|
// 从 URL 中提取 state 参数
|
||||||
|
const url = new URL(authUrl);
|
||||||
|
const state = url.searchParams.get('state');
|
||||||
|
|
||||||
|
// 显示 OAuth 状态模态框
|
||||||
|
this.showOAuthStatusModal(authUrl, providerName, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示 OAuth 状态模态框
|
||||||
|
showOAuthStatusModal(authUrl, providerName, state) {
|
||||||
|
const modalBody = document.getElementById('modal-body');
|
||||||
|
modalBody.innerHTML = `
|
||||||
|
<div class="oauth-status-modal loading">
|
||||||
|
<div class="oauth-icon">
|
||||||
|
<i class="fas fa-spinner"></i>
|
||||||
|
</div>
|
||||||
|
<h3>${i18n.t('auth_login.oauth_in_progress')}</h3>
|
||||||
|
<p>${providerName} ${i18n.t('auth_login.oauth_open_browser')}</p>
|
||||||
|
|
||||||
|
<div class="oauth-url-display">
|
||||||
|
${authUrl}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="oauth-actions">
|
||||||
|
<button class="btn btn-primary" onclick="navigator.clipboard.writeText('${authUrl}')">
|
||||||
|
<i class="fas fa-copy"></i> ${i18n.t('auth_login.copy_url')}
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-secondary" onclick="window.open('${authUrl}', '_blank')">
|
||||||
|
<i class="fas fa-external-link-alt"></i> ${i18n.t('auth_login.open_browser')}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p style="margin-top: 20px; color: var(--text-tertiary); font-size: 14px;">
|
||||||
|
${i18n.t('auth_login.oauth_waiting')}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
this.showModal();
|
||||||
|
|
||||||
|
// 如果有 state,开始轮询认证状态
|
||||||
|
if (state) {
|
||||||
|
this.pollOAuthStatus(state, providerName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 轮询 OAuth 认证状态
|
||||||
|
async pollOAuthStatus(state, providerName) {
|
||||||
|
const maxAttempts = 60; // 最多轮询 5 分钟(每 5 秒一次)
|
||||||
|
let attempts = 0;
|
||||||
|
|
||||||
|
const poll = async () => {
|
||||||
|
attempts++;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await this.makeRequest(`/get-auth-status?state=${encodeURIComponent(state)}`);
|
||||||
|
|
||||||
|
if (response.status === 'ok') {
|
||||||
|
// 认证成功
|
||||||
|
this.showOAuthSuccess(providerName);
|
||||||
|
this.loadAuthFiles(); // 刷新认证文件列表
|
||||||
|
return;
|
||||||
|
} else if (response.status === 'error') {
|
||||||
|
// 认证失败
|
||||||
|
this.showOAuthError(response.error || '认证失败');
|
||||||
|
return;
|
||||||
|
} else if (response.status === 'wait') {
|
||||||
|
// 继续等待
|
||||||
|
if (attempts < maxAttempts) {
|
||||||
|
setTimeout(poll, 5000); // 5 秒后重试
|
||||||
|
} else {
|
||||||
|
this.showOAuthError(i18n.t('auth_login.oauth_timeout'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('OAuth 状态轮询失败:', error);
|
||||||
|
if (attempts < maxAttempts) {
|
||||||
|
setTimeout(poll, 5000); // 5 秒后重试
|
||||||
|
} else {
|
||||||
|
this.showOAuthError(`${i18n.t('auth_login.oauth_failed')}: ${error.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 开始轮询
|
||||||
|
setTimeout(poll, 2000); // 2 秒后开始第一次检查
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示 OAuth 认证成功
|
||||||
|
showOAuthSuccess(providerName) {
|
||||||
|
const modalBody = document.getElementById('modal-body');
|
||||||
|
modalBody.innerHTML = `
|
||||||
|
<div class="oauth-status-modal success">
|
||||||
|
<div class="oauth-icon">
|
||||||
|
<i class="fas fa-check-circle"></i>
|
||||||
|
</div>
|
||||||
|
<h3>${i18n.t('auth_login.oauth_success')}</h3>
|
||||||
|
<p>${providerName} 认证已完成!</p>
|
||||||
|
|
||||||
|
<div class="oauth-actions">
|
||||||
|
<button class="btn btn-primary" onclick="manager.closeModal()">
|
||||||
|
<i class="fas fa-check"></i> ${i18n.t('common.confirm')}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示 OAuth 认证失败
|
||||||
|
showOAuthError(errorMessage) {
|
||||||
|
const modalBody = document.getElementById('modal-body');
|
||||||
|
modalBody.innerHTML = `
|
||||||
|
<div class="oauth-status-modal error">
|
||||||
|
<div class="oauth-icon">
|
||||||
|
<i class="fas fa-times-circle"></i>
|
||||||
|
</div>
|
||||||
|
<h3>${i18n.t('auth_login.oauth_failed')}</h3>
|
||||||
|
<p>${errorMessage}</p>
|
||||||
|
|
||||||
|
<div class="oauth-actions">
|
||||||
|
<button class="btn btn-danger" onclick="manager.closeModal()">
|
||||||
|
<i class="fas fa-times"></i> ${i18n.t('common.close')}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示 Gemini Web Token 模态框
|
||||||
|
showGeminiWebTokenModal() {
|
||||||
|
const modalBody = document.getElementById('modal-body');
|
||||||
|
modalBody.innerHTML = `
|
||||||
|
<h3>${i18n.t('auth_login.gemini_web_button')}</h3>
|
||||||
|
<div class="gemini-web-form">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="modal-secure-1psid">${i18n.t('auth_login.secure_1psid_label')}</label>
|
||||||
|
<input type="text" id="modal-secure-1psid" placeholder="${i18n.t('auth_login.secure_1psid_placeholder')}" required>
|
||||||
|
<div class="form-hint">从浏览器开发者工具 → Application → Cookies 中获取</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="modal-secure-1psidts">${i18n.t('auth_login.secure_1psidts_label')}</label>
|
||||||
|
<input type="text" id="modal-secure-1psidts" placeholder="${i18n.t('auth_login.secure_1psidts_placeholder')}" required>
|
||||||
|
<div class="form-hint">从浏览器开发者工具 → Application → Cookies 中获取</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-actions">
|
||||||
|
<button class="btn btn-secondary" onclick="manager.closeModal()">${i18n.t('common.cancel')}</button>
|
||||||
|
<button class="btn btn-primary" onclick="manager.saveGeminiWebToken()">${i18n.t('common.save')}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
this.showModal();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存 Gemini Web Token
|
||||||
|
async saveGeminiWebToken() {
|
||||||
|
const secure1psid = document.getElementById('modal-secure-1psid').value.trim();
|
||||||
|
const secure1psidts = document.getElementById('modal-secure-1psidts').value.trim();
|
||||||
|
|
||||||
|
if (!secure1psid || !secure1psidts) {
|
||||||
|
this.showNotification('请填写完整的 Cookie 信息', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await this.makeRequest('/gemini-web-token', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
secure_1psid: secure1psid,
|
||||||
|
secure_1psidts: secure1psidts
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
this.closeModal();
|
||||||
|
this.loadAuthFiles(); // 刷新认证文件列表
|
||||||
|
this.showNotification(`${i18n.t('auth_login.gemini_web_saved')}: ${response.file}`, 'success');
|
||||||
|
} catch (error) {
|
||||||
|
this.showNotification(`保存失败: ${error.message}`, 'error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 关闭模态框
|
// 关闭模态框
|
||||||
closeModal() {
|
closeModal() {
|
||||||
document.getElementById('modal').style.display = 'none';
|
document.getElementById('modal').style.display = 'none';
|
||||||
|
|||||||
24
i18n.js
24
i18n.js
@@ -182,9 +182,11 @@ const i18n = {
|
|||||||
'ai_providers.openai_keys_count': '密钥数量',
|
'ai_providers.openai_keys_count': '密钥数量',
|
||||||
'ai_providers.openai_models_count': '模型数量',
|
'ai_providers.openai_models_count': '模型数量',
|
||||||
|
|
||||||
|
|
||||||
// 认证文件管理
|
// 认证文件管理
|
||||||
'auth_files.title': '认证文件管理',
|
'auth_files.title': '认证文件管理',
|
||||||
'auth_files.title_section': '认证文件',
|
'auth_files.title_section': '认证文件',
|
||||||
|
'auth_files.description': '这里管理 Qwen 和 Gemini 的认证配置文件。上传 JSON 格式的认证文件以启用相应的 AI 服务。',
|
||||||
'auth_files.upload_button': '上传文件',
|
'auth_files.upload_button': '上传文件',
|
||||||
'auth_files.delete_all_button': '删除全部',
|
'auth_files.delete_all_button': '删除全部',
|
||||||
'auth_files.empty_title': '暂无认证文件',
|
'auth_files.empty_title': '暂无认证文件',
|
||||||
@@ -202,6 +204,16 @@ const i18n = {
|
|||||||
'auth_files.delete_all_success': '成功删除',
|
'auth_files.delete_all_success': '成功删除',
|
||||||
'auth_files.files_count': '个文件',
|
'auth_files.files_count': '个文件',
|
||||||
|
|
||||||
|
// Gemini Web Token
|
||||||
|
'auth_login.gemini_web_title': 'Gemini Web Token',
|
||||||
|
'auth_login.gemini_web_button': '保存 Gemini Web Token',
|
||||||
|
'auth_login.gemini_web_hint': '从浏览器开发者工具中获取 Gemini 网页版的 Cookie 值,用于直接认证访问 Gemini。',
|
||||||
|
'auth_login.secure_1psid_label': '__Secure-1PSID Cookie:',
|
||||||
|
'auth_login.secure_1psid_placeholder': '输入 __Secure-1PSID cookie 值',
|
||||||
|
'auth_login.secure_1psidts_label': '__Secure-1PSIDTS Cookie:',
|
||||||
|
'auth_login.secure_1psidts_placeholder': '输入 __Secure-1PSIDTS cookie 值',
|
||||||
|
'auth_login.gemini_web_saved': 'Gemini Web Token 保存成功',
|
||||||
|
|
||||||
// 系统信息
|
// 系统信息
|
||||||
'system_info.title': '系统信息',
|
'system_info.title': '系统信息',
|
||||||
'system_info.connection_status_title': '连接状态',
|
'system_info.connection_status_title': '连接状态',
|
||||||
@@ -443,9 +455,11 @@ const i18n = {
|
|||||||
'ai_providers.openai_keys_count': 'Keys Count',
|
'ai_providers.openai_keys_count': 'Keys Count',
|
||||||
'ai_providers.openai_models_count': 'Models Count',
|
'ai_providers.openai_models_count': 'Models Count',
|
||||||
|
|
||||||
|
|
||||||
// Auth files management
|
// Auth files management
|
||||||
'auth_files.title': 'Auth Files Management',
|
'auth_files.title': 'Auth Files Management',
|
||||||
'auth_files.title_section': 'Auth Files',
|
'auth_files.title_section': 'Auth Files',
|
||||||
|
'auth_files.description': 'Here you can manage authentication configuration files for Qwen and Gemini. Upload JSON format authentication files to enable the corresponding AI services.',
|
||||||
'auth_files.upload_button': 'Upload File',
|
'auth_files.upload_button': 'Upload File',
|
||||||
'auth_files.delete_all_button': 'Delete All',
|
'auth_files.delete_all_button': 'Delete All',
|
||||||
'auth_files.empty_title': 'No Auth Files',
|
'auth_files.empty_title': 'No Auth Files',
|
||||||
@@ -463,6 +477,16 @@ const i18n = {
|
|||||||
'auth_files.delete_all_success': 'Successfully deleted',
|
'auth_files.delete_all_success': 'Successfully deleted',
|
||||||
'auth_files.files_count': 'files',
|
'auth_files.files_count': 'files',
|
||||||
|
|
||||||
|
// Gemini Web Token
|
||||||
|
'auth_login.gemini_web_title': 'Gemini Web Token',
|
||||||
|
'auth_login.gemini_web_button': 'Save Gemini Web Token',
|
||||||
|
'auth_login.gemini_web_hint': 'Obtain the Cookie value of the Gemini web version from the browser\'s developer tools, used for direct authentication to access Gemini.',
|
||||||
|
'auth_login.secure_1psid_label': '__Secure-1PSID Cookie:',
|
||||||
|
'auth_login.secure_1psid_placeholder': 'Enter __Secure-1PSID cookie value',
|
||||||
|
'auth_login.secure_1psidts_label': '__Secure-1PSIDTS Cookie:',
|
||||||
|
'auth_login.secure_1psidts_placeholder': 'Enter __Secure-1PSIDTS cookie value',
|
||||||
|
'auth_login.gemini_web_saved': 'Gemini Web Token saved successfully',
|
||||||
|
|
||||||
// System info
|
// System info
|
||||||
'system_info.title': 'System Information',
|
'system_info.title': 'System Information',
|
||||||
'system_info.connection_status_title': 'Connection Status',
|
'system_info.connection_status_title': 'Connection Status',
|
||||||
|
|||||||
33
index.html
33
index.html
@@ -394,12 +394,45 @@
|
|||||||
<div id="openai-providers-list" class="provider-list"></div>
|
<div id="openai-providers-list" class="provider-list"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Gemini Web Token -->
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
<h3><i class="fab fa-google"></i> <span data-i18n="auth_login.gemini_web_title">Gemini Web Token</span></h3>
|
||||||
|
<button id="gemini-web-token-btn" class="btn btn-primary">
|
||||||
|
<i class="fas fa-save"></i> <span data-i18n="auth_login.gemini_web_button">保存 Gemini Web Token</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="card-content">
|
||||||
|
<p class="form-hint" style="margin-bottom: 20px;" data-i18n="auth_login.gemini_web_hint">
|
||||||
|
从浏览器开发者工具中获取 Gemini 网页版的 Cookie 值,用于直接认证访问 Gemini。
|
||||||
|
</p>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="secure-1psid-input" data-i18n="auth_login.secure_1psid_label">__Secure-1PSID Cookie:</label>
|
||||||
|
<input type="text" id="secure-1psid-input" data-i18n="auth_login.secure_1psid_placeholder" placeholder="输入 __Secure-1PSID cookie 值">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="secure-1psidts-input" data-i18n="auth_login.secure_1psidts_label">__Secure-1PSIDTS Cookie:</label>
|
||||||
|
<input type="text" id="secure-1psidts-input" data-i18n="auth_login.secure_1psidts_placeholder" placeholder="输入 __Secure-1PSIDTS cookie 值">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<!-- 认证文件管理 -->
|
<!-- 认证文件管理 -->
|
||||||
<section id="auth-files" class="content-section">
|
<section id="auth-files" class="content-section">
|
||||||
<h2 data-i18n="auth_files.title">认证文件管理</h2>
|
<h2 data-i18n="auth_files.title">认证文件管理</h2>
|
||||||
|
|
||||||
|
<div class="card" style="margin-bottom: 20px;">
|
||||||
|
<div class="card-content">
|
||||||
|
<p class="form-hint" data-i18n="auth_files.description">
|
||||||
|
这里管理 Qwen 和 Gemini 的认证配置文件。上传 JSON 格式的认证文件以启用相应的 AI 服务。
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 认证文件 -->
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<h3><i class="fas fa-file-alt"></i> <span 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>
|
||||||
|
|||||||
41
styles.css
41
styles.css
@@ -436,6 +436,7 @@
|
|||||||
|
|
||||||
.local-url-group input[type="number"] {
|
.local-url-group input[type="number"] {
|
||||||
-moz-appearance: textfield;
|
-moz-appearance: textfield;
|
||||||
|
appearance: textfield;
|
||||||
}
|
}
|
||||||
|
|
||||||
.login-title {
|
.login-title {
|
||||||
@@ -1572,3 +1573,43 @@ input:checked + .slider:before {
|
|||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Gemini Web Token 模态框样式 */
|
||||||
|
.gemini-web-form .form-group {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gemini-web-form .form-group label {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gemini-web-form .form-group input {
|
||||||
|
width: 100%;
|
||||||
|
padding: 12px 16px;
|
||||||
|
border: 2px solid var(--border-primary);
|
||||||
|
border-radius: 8px;
|
||||||
|
font-size: 14px;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
background: var(--bg-tertiary);
|
||||||
|
color: var(--text-primary);
|
||||||
|
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gemini-web-form .form-group input:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: var(--border-focus);
|
||||||
|
box-shadow: 0 0 0 3px var(--border-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.gemini-web-form .form-hint {
|
||||||
|
margin-top: 6px;
|
||||||
|
color: var(--text-tertiary);
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user