Files
antigravity-claude-proxy/public/js/store.js
Wha1eChai 217053839f feat(webui): Enhance dashboard, global styles, and settings module
## Dashboard Enhancements
- Add Request Volume trend chart with Chart.js line graph
  - Support Family/Model display modes for aggregation levels
  - Show Total/Today/1H usage statistics
  - Hierarchical filter dropdown with Smart select (Top 5 by 24h usage)
  - Persist chart preferences to localStorage
- Improve account health detection logic
  - Core models (sonnet/opus/pro/flash) require >5% quota to be healthy
  - Dynamic quota ring chart supporting any model family
- Unify table styles with standard-table class

## Global Style Refactoring
- Add CSS variable system for theming
  - Space color scale (950/900/850/800/border)
  - Neon accent colors (purple/green/cyan/yellow/red)
  - Text hierarchy (main/dim/muted/bright)
  - Chart palette (16 colors)
- Add unified component classes
  - .view-container for consistent page layouts
  - .section-header/.section-title/.section-desc
  - .standard-table for table styling
- Update scrollbar, nav-item, progress-bar to use theme variables

## Settings Module Extensions
- Add model mapping column in Models tab
- Enhance model selectors with family color indicators
- Support horizontal scroll for tabs on narrow screens
- Add defaultCooldownMs and maxWaitBeforeErrorMs config options

## New Module
- Add src/modules/usage-stats.js for request tracking
  - Track /v1/messages and /v1/chat/completions endpoints
  - Hierarchical storage: { hour: { family: { model: count } } }
  - Auto-save every minute, 30-day retention
  - GET /api/stats/history endpoint for dashboard chart

## Backend Changes
- Add direct account manipulation helpers (bypass AccountManager)
- Add POST /api/config/password endpoint for WebUI password change
- Auto-reload AccountManager after account operations
- Use CSS variables in OAuth callback pages

## Other
- Update .gitignore for runtime data directory
- Add i18n keys for new UI elements (EN/zh_CN)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-08 19:04:43 +08:00

368 lines
16 KiB
JavaScript

/**
* Global Store for Antigravity Console
* Handles Translations, Toasts, and Shared Config
*/
document.addEventListener('alpine:init', () => {
Alpine.store('global', {
// App State
version: '1.0.0',
activeTab: 'dashboard',
webuiPassword: localStorage.getItem('antigravity_webui_password') || '',
// i18n
lang: localStorage.getItem('app_lang') || 'en',
translations: {
en: {
dashboard: "Dashboard",
accounts: "Accounts",
logs: "Logs",
settings: "Settings",
online: "ONLINE",
offline: "OFFLINE",
totalAccounts: "TOTAL ACCOUNTS",
active: "ACTIVE",
operational: "Operational",
rateLimited: "RATE LIMITED",
cooldown: "Cooldown",
searchPlaceholder: "Search models...",
allAccounts: "All Accounts",
stat: "STAT",
modelIdentity: "MODEL IDENTITY",
globalQuota: "GLOBAL QUOTA",
nextReset: "NEXT RESET",
distribution: "ACCOUNT DISTRIBUTION",
systemConfig: "System Configuration",
language: "Language",
pollingInterval: "Polling Interval",
logBufferSize: "Log Buffer Size",
showExhausted: "Show Exhausted Models",
showExhaustedDesc: "Display models even if they have 0% remaining quota.",
compactMode: "Compact Mode",
compactModeDesc: "Reduce padding in tables for higher information density.",
saveChanges: "Save Changes",
autoScroll: "Auto-scroll",
clearLogs: "Clear Logs",
accessCredentials: "Access Credentials",
manageTokens: "Manage OAuth tokens and session states",
addNode: "Add Node",
status: "STATUS",
enabled: "ENABLED",
health: "HEALTH",
identity: "IDENTITY (EMAIL)",
projectId: "PROJECT ID",
sessionState: "SESSION STATE",
operations: "OPERATIONS",
delete: "Delete",
confirmDelete: "Are you sure you want to remove this account?",
connectGoogle: "Connect Google Account",
reauthenticated: "re-authenticated",
added: "added",
successfully: "successfully",
failedToGetAuthUrl: "Failed to get auth URL",
failedToStartOAuth: "Failed to start OAuth flow",
family: "Family",
model: "Model",
activeSuffix: "Active",
// Tabs
tabInterface: "Interface",
tabClaude: "Claude CLI",
tabModels: "Models",
tabServer: "Server Info",
// Dashboard
registeredNodes: "Registered Nodes",
noSignal: "NO SIGNAL DETECTED",
establishingUplink: "ESTABLISHING UPLINK...",
// Settings - Models
modelsDesc: "Manage visibility and ordering of models in the dashboard.",
showHidden: "Show Hidden Models",
modelId: "Model ID",
alias: "Alias",
actions: "Actions",
pinToTop: "Pin to top",
toggleVisibility: "Toggle Visibility",
noModels: "NO MODELS DETECTED",
// Settings - Claude
proxyConnection: "Proxy Connection",
modelSelection: "Model Selection",
aliasOverrides: "ALIAS OVERRIDES",
opusAlias: "Opus Alias",
sonnetAlias: "Sonnet Alias",
haikuAlias: "Haiku Alias",
claudeSettingsAlert: "Settings below directly modify ~/.claude/settings.json. Restart Claude CLI to apply.",
writeToConfig: "Write to Config",
// Settings - Server
port: "Port",
uiVersion: "UI Version",
debugMode: "Debug Mode",
environment: "Environment",
serverReadOnly: "Server settings are read-only. Modify config.json or .env and restart the server to change.",
dangerZone: "Danger Zone / Advanced",
reloadConfigTitle: "Reload Account Config",
reloadConfigDesc: "Force reload accounts.json from disk",
reload: "Reload",
// Config Specific
primaryModel: "Primary Model",
subAgentModel: "Sub-agent Model",
advancedOverrides: "Default Model Overrides",
opusModel: "Opus Model",
sonnetModel: "Sonnet Model",
haikuModel: "Haiku Model",
authToken: "Auth Token",
saveConfig: "Save to ~/.claude/settings.json",
envVar: "Env",
// New Keys
systemName: "ANTIGRAVITY",
systemDesc: "CLAUDE PROXY SYSTEM",
connectGoogleDesc: "Connect a Google Workspace account to increase your API quota limit. The account will be used to proxy Claude requests via Antigravity.",
useCliCommand: "Use CLI Command",
close: "Close",
requestVolume: "Request Volume",
filter: "Filter",
all: "All",
none: "None",
noDataTracked: "No data tracked yet",
selectFamilies: "Select families to display",
selectModels: "Select models to display",
noLogsMatch: "No logs match filter",
connecting: "CONNECTING",
main: "Main",
system: "System",
refreshData: "Refresh Data",
connectionLost: "Connection Lost",
lastUpdated: "Last Updated",
grepLogs: "grep logs...",
noMatchingModels: "No matching models",
typeToSearch: "Type to search or select...",
or: "OR",
refreshingAccount: "Refreshing {email}...",
refreshedAccount: "Refreshed {email}",
refreshFailed: "Refresh failed",
accountToggled: "Account {email} {status}",
toggleFailed: "Toggle failed",
reauthenticating: "Re-authenticating {email}...",
authUrlFailed: "Failed to get auth URL",
deletedAccount: "Deleted {email}",
deleteFailed: "Delete failed",
accountsReloaded: "Accounts reloaded",
reloadFailed: "Reload failed",
claudeConfigSaved: "Claude configuration saved",
saveConfigFailed: "Failed to save configuration",
claudeActive: "Claude Active",
claudeEmpty: "Claude Empty",
geminiActive: "Gemini Active",
geminiEmpty: "Gemini Empty",
fix: "FIX",
synced: "SYNCED",
syncing: "SYNCING...",
// Additional
reloading: "Reloading...",
reloaded: "Reloaded",
lines: "lines",
enabledSeeLogs: "Enabled (See Logs)",
production: "Production",
configSaved: "Configuration Saved",
enterPassword: "Enter Web UI Password:",
ready: "READY",
familyClaude: "Claude",
familyGemini: "Gemini",
familyOther: "Other",
enabledStatus: "enabled",
disabledStatus: "disabled",
logLevelInfo: "INFO",
logLevelSuccess: "SUCCESS",
logLevelWarn: "WARN",
logLevelError: "ERR",
},
zh: {
dashboard: "仪表盘",
accounts: "账号管理",
logs: "运行日志",
settings: "系统设置",
online: "在线",
offline: "离线",
totalAccounts: "账号总数",
active: "活跃状态",
operational: "运行中",
rateLimited: "受限状态",
cooldown: "冷却中",
searchPlaceholder: "搜索模型...",
allAccounts: "所有账号",
stat: "状态",
modelIdentity: "模型标识",
globalQuota: "全局配额",
nextReset: "重置时间",
distribution: "账号分布",
systemConfig: "系统配置",
language: "语言设置",
pollingInterval: "数据轮询间隔",
logBufferSize: "日志缓冲大小",
showExhausted: "显示耗尽模型",
showExhaustedDesc: "即使配额为 0% 也显示模型。",
compactMode: "紧凑模式",
compactModeDesc: "减少表格间距以显示更多信息。",
saveChanges: "保存更改",
autoScroll: "自动滚动",
clearLogs: "清除日志",
accessCredentials: "访问凭证",
manageTokens: "管理 OAuth 令牌和会话状态",
addNode: "添加节点",
status: "状态",
enabled: "启用",
health: "健康度",
identity: "身份 (邮箱)",
projectId: "项目 ID",
sessionState: "会话状态",
operations: "操作",
delete: "删除",
confirmDelete: "确定要移除此账号吗?",
connectGoogle: "连接 Google 账号",
reauthenticated: "已重新认证",
added: "已添加",
successfully: "成功",
failedToGetAuthUrl: "获取认证链接失败",
failedToStartOAuth: "启动 OAuth 流程失败",
family: "系列",
model: "模型",
activeSuffix: "活跃",
manualReload: "重新加载配置",
// Tabs
tabInterface: "界面设置",
tabClaude: "Claude CLI",
tabModels: "模型管理",
tabServer: "服务器信息",
// Dashboard
registeredNodes: "已注册节点",
noSignal: "无信号连接",
establishingUplink: "正在建立上行链路...",
// Settings - Models
modelsDesc: "管理仪表盘中模型的可见性和排序。",
showHidden: "显示隐藏模型",
modelId: "模型 ID",
alias: "别名",
actions: "操作",
pinToTop: "置顶",
toggleVisibility: "切换可见性",
noModels: "未检测到模型",
// Settings - Claude
proxyConnection: "代理连接",
modelSelection: "模型选择",
aliasOverrides: "别名覆盖",
opusAlias: "Opus 别名",
sonnetAlias: "Sonnet 别名",
haikuAlias: "Haiku 别名",
claudeSettingsAlert: "以下设置直接修改 ~/.claude/settings.json。重启 Claude CLI 生效。",
writeToConfig: "写入配置",
// Settings - Server
port: "端口",
uiVersion: "UI 版本",
debugMode: "调试模式",
environment: "运行环境",
serverReadOnly: "服务器设置只读。修改 config.json 或 .env 并重启服务器以生效。",
dangerZone: "危险区域 / 高级",
reloadConfigTitle: "重载账号配置",
reloadConfigDesc: "强制从磁盘重新读取 accounts.json",
reload: "重载",
// Config Specific
primaryModel: "主模型",
subAgentModel: "子代理模型",
advancedOverrides: "默认模型覆盖 (高级)",
opusModel: "Opus 模型",
sonnetModel: "Sonnet 模型",
haikuModel: "Haiku 模型",
authToken: "认证令牌",
saveConfig: "保存到 ~/.claude/settings.json",
envVar: "环境变量",
// New Keys
systemName: "ANTIGRAVITY",
systemDesc: "CLAUDE 代理系统",
connectGoogleDesc: "连接 Google Workspace 账号以增加 API 配额。该账号将用于通过 Antigravity 代理 Claude 请求。",
useCliCommand: "使用命令行",
close: "关闭",
requestVolume: "请求量",
filter: "筛选",
all: "全选",
none: "清空",
noDataTracked: "暂无追踪数据",
selectFamilies: "选择要显示的系列",
selectModels: "选择要显示的模型",
noLogsMatch: "没有符合过滤条件的日志",
connecting: "正在连接",
main: "主菜单",
system: "系统",
refreshData: "刷新数据",
connectionLost: "连接已断开",
lastUpdated: "最后更新",
grepLogs: "过滤日志...",
noMatchingModels: "没有匹配的模型",
typeToSearch: "输入以搜索或选择...",
or: "或",
refreshingAccount: "正在刷新 {email}...",
refreshedAccount: "已完成刷新 {email}",
refreshFailed: "刷新失败",
accountToggled: "账号 {email} 已{status}",
toggleFailed: "切换失败",
reauthenticating: "正在重新认证 {email}...",
authUrlFailed: "获取认证链接失败",
deletedAccount: "已删除 {email}",
deleteFailed: "删除失败",
accountsReloaded: "账号配置已重载",
reloadFailed: "重载失败",
claudeConfigSaved: "Claude 配置已保存",
saveConfigFailed: "保存配置失败",
claudeActive: "Claude 活跃",
claudeEmpty: "Claude 耗尽",
geminiActive: "Gemini 活跃",
geminiEmpty: "Gemini 耗尽",
fix: "修复",
synced: "已同步",
syncing: "正在同步...",
// Additional
reloading: "正在重载...",
reloaded: "已重载",
lines: "行",
enabledSeeLogs: "已启用 (见日志)",
production: "生产环境",
configSaved: "配置已保存",
enterPassword: "请输入 Web UI 密码:",
ready: "就绪",
familyClaude: "Claude 系列",
familyGemini: "Gemini 系列",
familyOther: "其他系列",
enabledStatus: "已启用",
disabledStatus: "已禁用",
logLevelInfo: "信息",
logLevelSuccess: "成功",
logLevelWarn: "警告",
logLevelError: "错误",
}
},
// Toast Messages
toast: null,
t(key, params = {}) {
let str = this.translations[this.lang][key] || key;
if (typeof str === 'string') {
Object.keys(params).forEach(p => {
str = str.replace(`{${p}}`, params[p]);
});
}
return str;
},
setLang(l) {
this.lang = l;
localStorage.setItem('app_lang', l);
},
showToast(message, type = 'info') {
const id = Date.now();
this.toast = { message, type, id };
setTimeout(() => {
if (this.toast && this.toast.id === id) this.toast = null;
}, 3000);
}
});
});