feat(i18n): replace hardcoded Chinese messages with English

This commit is contained in:
lzt404 committed 2026-09-27 16:33:50 +08:00
1 parent 89bdbc7aea
commit 9c67b9d40c
77 files changed
+2385 -2304

No files matched your search

+3 -3
View File
@@ -193,7 +193,7 @@ function AppContent() {
return;
}
} catch (error) {
console.error('读取关闭行为设置失败', error);
console.error('Failed to read close behavior settings', error);
}
setWindowsClosePrompt((current) =>
@@ -216,7 +216,7 @@ function AppContent() {
}
})
.catch((error) => {
console.error('监听 Windows 关闭确认事件失败', error);
console.error('Failed to listen for Windows close confirmation events', error);
});
return () => {
@@ -252,7 +252,7 @@ function AppContent() {
try {
await invoke('open_external_url', { url: CONTACT_URL });
} catch (error) {
console.error('打开联系我们链接失败', error);
console.error('Failed to open the contact link', error);
}
};
+1 -1
View File
@@ -12,7 +12,7 @@ export class AppErrorBoundary extends Component<Props, State> {
}
componentDidCatch(error: Error, info: ErrorInfo) {
console.error('EasyCLIProxyAPI 渲染异常', error, info.componentStack);
console.error('EasyCLIProxyAPI render failure', error, info.componentStack);
}
render() {
+1 -1
View File
@@ -99,7 +99,7 @@ export function CoreRuntimeProvider({ children }: { children: ReactNode }) {
export function useCoreRuntime() {
const context = useContext(CoreRuntimeContext);
if (!context) {
throw new Error('useCoreRuntime 必须在 CoreRuntimeProvider 内使用');
throw new Error('useCoreRuntime must be used inside CoreRuntimeProvider');
}
return context;
}
+1 -1
View File
@@ -194,7 +194,7 @@ export function CoreUpdateProvider({ children }: { children: ReactNode }) {
export function useCoreUpdate() {
const context = useContext(CoreUpdateContext);
if (!context) {
throw new Error('useCoreUpdate 必须在 CoreUpdateProvider 内使用');
throw new Error('useCoreUpdate must be used inside CoreUpdateProvider');
}
return context;
}
+5 -5
View File
@@ -16,7 +16,7 @@ export const languageOptions: ReadonlyArray<{
const STORAGE_KEY = 'easy-cli-proxy-api.locale';
const resources = { 'zh-CN': zhCN, 'zh-TW': zhTW, ja, en } as const;
let currentLocale: AppLocale = 'zh-CN';
let currentLocale: AppLocale = 'en';
export const supportedLocales: readonly AppLocale[] = ['zh-CN', 'zh-TW', 'ja', 'en'];
@@ -26,7 +26,6 @@ export function getCurrentLocale(): AppLocale {
export function normalizeLocale(value: string | null | undefined): AppLocale {
const normalized = value?.trim().toLowerCase() ?? '';
if (normalized.startsWith('en')) return 'en';
if (normalized.startsWith('ja')) return 'ja';
if (
normalized === 'zh-tw'
@@ -34,11 +33,12 @@ export function normalizeLocale(value: string | null | undefined): AppLocale {
|| normalized === 'zh-mo'
|| normalized.startsWith('zh-hant')
) return 'zh-TW';
return 'zh-CN';
if (normalized.startsWith('zh')) return 'zh-CN';
return 'en';
}
function detectInitialLocale(): AppLocale {
if (typeof window === 'undefined') return 'zh-CN';
if (typeof window === 'undefined') return 'en';
try {
const saved = window.localStorage.getItem(STORAGE_KEY);
if (saved) return normalizeLocale(saved);
@@ -55,7 +55,7 @@ function interpolate(template: string, variables?: MessageVariables): string {
}
export function translate(locale: AppLocale, key: MessageKey, variables?: MessageVariables): string {
return interpolate(resources[locale][key] ?? zhCN[key], variables);
return interpolate(resources[locale][key] ?? en[key], variables);
}
type I18nContextValue = {
+1
View File
@@ -1461,6 +1461,7 @@ export const jaOverrides = {
'kernel.error.port': 'ポートは 1~65535 の範囲で指定してください',
'kernel.error.installTask': 'インストールタスクを読み込めませんでした:{error}',
'kernel.install.installingVersion': '{version} をインストールしています',
'kernel.install.installingLatest': '最新バージョンをインストールしています',
'kernel.phase.preparingDownload': 'ダウンロードを準備中',
'kernel.phase.downloading': 'ダウンロード中',
'kernel.phase.extracting': '展開中',
+1
View File
@@ -1461,6 +1461,7 @@ export const en: Record<MessageKey, string> = {
'kernel.error.port': 'The port must be between 1 and 65535',
'kernel.error.installTask': 'Failed to read the installation task: {error}',
'kernel.install.installingVersion': 'Installing {version}',
'kernel.install.installingLatest': 'Installing the latest version',
'kernel.phase.preparingDownload': 'Preparing Download',
'kernel.phase.downloading': 'Downloading',
'kernel.phase.extracting': 'Extracting',
+1
View File
@@ -1460,6 +1460,7 @@ export const zhCN = {
'kernel.error.port': '端口必须在 1 到 65535 之间',
'kernel.error.installTask': '读取安装任务失败: {error}',
'kernel.install.installingVersion': '正在安装 {version}',
'kernel.install.installingLatest': '正在安装最新版本',
'kernel.phase.preparingDownload': '准备下载',
'kernel.phase.downloading': '下载中',
'kernel.phase.extracting': '解压中',
+10 -10
View File
@@ -35,18 +35,18 @@ function installToolbar(scenario: BrowserMockScenario, delayMs: number) {
const toolbar = document.createElement('aside');
toolbar.id = 'browser-mock-toolbar';
toolbar.setAttribute('aria-label', 'Browser Mock controls');
toolbar.title = '仅在普通浏览器开发模式中显示;Tauri 和正式构建不会启用 Mock。';
toolbar.title = 'Shown only in regular browser development mode; Mock is disabled in Tauri and production builds.';
const badge = document.createElement('strong');
badge.textContent = 'MOCK';
const select = document.createElement('select');
select.setAttribute('aria-label', 'Browser Mock 场景');
select.setAttribute('aria-label', 'Browser Mock scenario');
const scenarios: Array<[BrowserMockScenario, string]> = [
['running', '内核运行'],
['stopped', '内核停止'],
['empty', '未安装内核'],
['error', '请求错误'],
['running', 'Core running'],
['stopped', 'Core stopped'],
['empty', 'Core not installed'],
['error', 'Request error'],
];
scenarios.forEach(([value, label]) => {
const option = document.createElement('option');
@@ -64,7 +64,7 @@ function installToolbar(scenario: BrowserMockScenario, delayMs: number) {
});
const hint = document.createElement('span');
hint.textContent = delayMs > 0 ? `${delayMs} ms` : '浏览器调试';
hint.textContent = delayMs > 0 ? `${delayMs} ms` : 'Browser debugging';
toolbar.append(badge, select, hint);
document.body.append(toolbar);
@@ -74,7 +74,7 @@ function installToolbar(scenario: BrowserMockScenario, delayMs: number) {
export function installBrowserMock(): InstalledBrowserMock | null {
const options = resolveBrowserMockOptions(window.location.search, readStoredScenario());
if (options.mode === 'off') {
console.info('[Browser Mock] 已通过 ?mock=off 禁用。');
console.info('[Browser Mock] Disabled via ?mock=off.');
return null;
}
@@ -98,8 +98,8 @@ export function installBrowserMock(): InstalledBrowserMock | null {
installToolbar(options.mode, options.delayMs);
console.info(
`[Browser Mock] 已启用场景“${options.mode}”${options.delayMs ? `,延迟 ${options.delayMs} ms` : ''}。`,
'使用 ?mock=running|stopped|empty|error 切换,?mock=off 禁用。',
`[Browser Mock] Enabled scenario “${options.mode}”${options.delayMs ? ` with a ${options.delayMs} ms delay` : ''}.`,
'Use ?mock=running|stopped|empty|error to switch scenarios, or ?mock=off to disable.',
);
return { scenario: options.mode, delayMs: options.delayMs };
}
+29 -29
View File
@@ -83,7 +83,7 @@ function createCoreStatus(scenario: BrowserMockScenario) {
currentVersion: installed ? '7.3.15' : null,
installDir: 'C:\\EasyCLIProxyAPI\\cpa-core',
binaryPath: installed ? 'C:\\EasyCLIProxyAPI\\cpa-core\\cli-proxy-api.exe' : null,
message: running ? 'Browser Mock 内核运行中' : installed ? 'Browser Mock 内核已停止' : 'Browser Mock 内核未安装',
message: running ? 'Browser Mock core is running' : installed ? 'Browser Mock core is stopped' : 'Browser Mock core is not installed',
};
}
@@ -138,7 +138,7 @@ function createUsageEvents() {
endpoint: '/v1/responses',
api_key_hash: 'mock-key-hash',
api_key_display: 'sk-mock••••••••2026',
api_key_remark: '浏览器 Mock 密钥',
api_key_remark: 'Browser Mock key',
tokens: {
input_tokens: input,
output_tokens: output,
@@ -186,10 +186,10 @@ function createAgentStatuses() {
pluginInstalled: id === 'pi',
launchTargets: id === 'codex' || id === 'opencode'
? [
{ id: 'app', label: 'Desktop App', detail: 'Browser Mock 桌面入口' },
{ id: 'cli', label: 'CLI', detail: 'Browser Mock 命令行入口' },
{ id: 'app', label: 'Desktop App', detail: 'Browser Mock desktop entry point' },
{ id: 'cli', label: 'CLI', detail: 'Browser Mock command-line entry point' },
]
: [{ id: id === 'claude-desktop' ? 'app' : 'cli', label: '启动', detail: 'Browser Mock 启动目标' }],
: [{ id: id === 'claude-desktop' ? 'app' : 'cli', label: 'Launch', detail: 'Browser Mock launch target' }],
version: `1.${index + 4}.0`,
cliVersion: id === 'claude-desktop' ? null : `1.${index + 4}.0`,
appVersion: id === 'claude-desktop' ? '0.12.8' : null,
@@ -217,7 +217,7 @@ function createAgentStatuses() {
],
}
: null,
warnings: id === 'openclaw' ? ['Mock:尚未应用配置'] : [],
warnings: id === 'openclaw' ? ['Mock: configuration has not been applied'] : [],
error: null,
}));
}
@@ -345,7 +345,7 @@ function createState(scenario: BrowserMockScenario) {
{ key: 'other', label: 'Other', requests: 34, failures: 1, tokens: 333_300 },
],
apiKeys: [
{ key: 'mock-key-hash', label: '浏览器 Mock 密钥', requests: 312, failures: 7, tokens: 2_187_300 },
{ key: 'mock-key-hash', label: 'Browser Mock key', requests: 312, failures: 7, tokens: 2_187_300 },
],
};
return {
@@ -359,8 +359,8 @@ function createState(scenario: BrowserMockScenario) {
},
coreConfig: {
apiKeys: [
{ apiKey: 'sk-browser-mock-2026', remark: '浏览器 Mock 密钥' },
{ apiKey: 'sk-browser-demo-secondary', remark: '备用演示密钥' },
{ apiKey: 'sk-browser-mock-2026', remark: 'Browser Mock key' },
{ apiKey: 'sk-browser-demo-secondary', remark: 'Secondary demo key' },
],
debug: false,
commercialMode: false,
@@ -390,7 +390,7 @@ function createState(scenario: BrowserMockScenario) {
silentStartEnabled: false,
defaultTerminal: 'auto',
availableTerminals: [
{ id: 'auto', label: '自动选择' },
{ id: 'auto', label: 'Automatic selection' },
{ id: 'windows-terminal', label: 'Windows Terminal' },
{ id: 'powershell', label: 'PowerShell' },
],
@@ -512,7 +512,7 @@ function createState(scenario: BrowserMockScenario) {
models: ['gpt-5.2-codex', 'gpt-5.1-codex'].map((slug) => {
const configuration = {
display_name: slug === 'gpt-5.2-codex' ? 'GPT-5.2 Codex' : 'GPT-5.1 Codex',
description: 'Browser Mock 模型目录',
description: 'Browser Mock model catalog',
context_window: 272_000,
max_context_window: 400_000,
effective_context_window_percent: 90,
@@ -545,9 +545,9 @@ function createState(scenario: BrowserMockScenario) {
configured: true,
},
codexSessions: [
{ id: 'mock-session-1', title: '实现 Browser Mock', cwd: 'E:\\projects\\mock-demo', modelProvider: 'cpa-gui', archived: false, updatedAtMs: Date.now() - 20 * 60_000, databasePath: 'C:\\Users\\Mock\\.codex\\state.sqlite' },
{ id: 'mock-session-2', title: 'TypeScript 类型学习', cwd: 'E:\\projects\\typescript-study', modelProvider: 'openai', archived: false, updatedAtMs: Date.now() - 3 * 60 * 60_000, databasePath: 'C:\\Users\\Mock\\.codex\\state.sqlite' },
{ id: 'mock-session-3', title: '旧版界面检查', cwd: 'E:\\projects\\legacy-ui', modelProvider: 'cpa-gui', archived: true, updatedAtMs: Date.now() - 3 * 24 * 60 * 60_000, databasePath: 'C:\\Users\\Mock\\.codex\\state.sqlite' },
{ id: 'mock-session-1', title: 'Implement Browser Mock', cwd: 'E:\\projects\\mock-demo', modelProvider: 'cpa-gui', archived: false, updatedAtMs: Date.now() - 20 * 60_000, databasePath: 'C:\\Users\\Mock\\.codex\\state.sqlite' },
{ id: 'mock-session-2', title: 'Learn TypeScript types', cwd: 'E:\\projects\\typescript-study', modelProvider: 'openai', archived: false, updatedAtMs: Date.now() - 3 * 60 * 60_000, databasePath: 'C:\\Users\\Mock\\.codex\\state.sqlite' },
{ id: 'mock-session-3', title: 'Review legacy UI', cwd: 'E:\\projects\\legacy-ui', modelProvider: 'cpa-gui', archived: true, updatedAtMs: Date.now() - 3 * 24 * 60 * 60_000, databasePath: 'C:\\Users\\Mock\\.codex\\state.sqlite' },
],
};
}
@@ -769,21 +769,21 @@ export function createBrowserMockRuntime(
case 'get_core_status': return clone(state.coreStatus);
case 'start_core_process': {
if (!state.coreStatus.installed) throw new Error('Browser Mock:请先安装内核');
Object.assign(state.coreStatus, { running: true, ready: true, managed: true, starting: false, processId: 42817, message: 'Browser Mock 内核运行中' });
if (!state.coreStatus.installed) throw new Error('Browser Mock: install the core first');
Object.assign(state.coreStatus, { running: true, ready: true, managed: true, starting: false, processId: 42817, message: 'Browser Mock core is running' });
state.guiSettings.runOnStartup = true;
emit('core-status-changed', state.coreStatus);
return clone(state.coreStatus);
}
case 'stop_core_process': {
Object.assign(state.coreStatus, { running: false, ready: false, managed: false, starting: false, processId: null, message: 'Browser Mock 内核已停止' });
Object.assign(state.coreStatus, { running: false, ready: false, managed: false, starting: false, processId: null, message: 'Browser Mock core is stopped' });
state.guiSettings.runOnStartup = false;
emit('core-status-changed', state.coreStatus);
return clone(state.coreStatus);
}
case 'restart_core_process': {
if (!state.coreStatus.installed) throw new Error('Browser Mock:请先安装内核');
Object.assign(state.coreStatus, { running: true, ready: true, managed: true, starting: false, processId: 42818, message: 'Browser Mock 内核已重启' });
if (!state.coreStatus.installed) throw new Error('Browser Mock: install the core first');
Object.assign(state.coreStatus, { running: true, ready: true, managed: true, starting: false, processId: 42818, message: 'Browser Mock core restarted' });
emit('core-status-changed', state.coreStatus);
return clone(state.coreStatus);
}
@@ -853,13 +853,13 @@ export function createBrowserMockRuntime(
downloadedBytes: 18 * 1024 * 1024,
totalBytes: state.appUpdateInfo.downloadSizeBytes,
percent: 38,
message: 'Browser Mock 正在下载更新',
message: 'Browser Mock is downloading an update',
});
emit('app-update-progress', state.appUpdateTask);
return null;
}
case 'cancel_app_update': {
Object.assign(state.appUpdateTask, { running: false, cancellable: false, phase: 'cancelled', message: 'Browser Mock 已取消更新' });
Object.assign(state.appUpdateTask, { running: false, cancellable: false, phase: 'cancelled', message: 'Browser Mock update canceled' });
emit('app-update-progress', state.appUpdateTask);
return null;
}
@@ -873,8 +873,8 @@ export function createBrowserMockRuntime(
installDir: state.coreStatus.installDir,
binaryPath: 'C:\\EasyCLIProxyAPI\\cpa-core\\cli-proxy-api.exe',
};
Object.assign(state.coreStatus, { installed: true, currentVersion: result.version, binaryPath: result.binaryPath, message: 'Browser Mock 内核已安装' });
Object.assign(state.coreInstallTask, { running: false, cancellable: false, phase: 'completed', downloaded: 64 * 1024 * 1024, total: 64 * 1024 * 1024, percent: 100, message: 'Browser Mock 安装完成', result });
Object.assign(state.coreStatus, { installed: true, currentVersion: result.version, binaryPath: result.binaryPath, message: 'Browser Mock core installed' });
Object.assign(state.coreInstallTask, { running: false, cancellable: false, phase: 'completed', downloaded: 64 * 1024 * 1024, total: 64 * 1024 * 1024, percent: 100, message: 'Browser Mock installation complete', result });
emit('core-install-progress', state.coreInstallTask);
emit('core-status-changed', state.coreStatus);
return clone(result);
@@ -915,7 +915,7 @@ export function createBrowserMockRuntime(
case 'provider_health_probe': return { firstTokenLatencyMs: 184, responseLatencyMs: 642 };
case 'list_oauth_browsers': return [
{ id: 'default', label: '系统默认浏览器' },
{ id: 'default', label: 'System default browser' },
{ id: 'chrome', label: 'Google Chrome (Mock)' },
{ id: 'edge', label: 'Microsoft Edge (Mock)' },
];
@@ -940,7 +940,7 @@ export function createBrowserMockRuntime(
case 'get_usage_collector_status': return {
state: state.coreStatus.ready ? 'collecting' : 'waiting-core',
message: state.coreStatus.ready ? 'Browser Mock 正在采集使用记录' : '等待内核启动',
message: state.coreStatus.ready ? 'Browser Mock is collecting usage records' : 'Waiting for the core to start',
lastCollectedAt: new Date().toISOString(),
totalRecords: state.usageOverview.totalRequests,
};
@@ -1152,7 +1152,7 @@ export function createBrowserMockRuntime(
const ids = asArray(request.sessionIds).map(String);
state.codexSessions = state.codexSessions.filter((session) => !ids.includes(session.id));
return {
results: ids.map((id) => ({ sessionId: id, status: 'deleted', message: 'Browser Mock 已删除', backupPath: 'C:\\Users\\Mock\\.codex\\backups\\mock.zip' })),
results: ids.map((id) => ({ sessionId: id, status: 'deleted', message: 'Deleted by Browser Mock', backupPath: 'C:\\Users\\Mock\\.codex\\backups\\mock.zip' })),
deletedCount: ids.length,
failedCount: 0,
};
@@ -1167,15 +1167,15 @@ export function createBrowserMockRuntime(
}
case 'preview_codex_session_index_cleanup': return {
snapshotSha256: 'mock-snapshot-sha256',
candidates: [{ id: 'stale-thread-1', threadName: '已不存在的会话', updatedAt: isoHoursAgo(72) }],
candidates: [{ id: 'stale-thread-1', threadName: 'Missing session', updatedAt: isoHoursAgo(72) }],
};
case 'apply_codex_session_index_cleanup': return { prunedEntries: asArray(asObject(payload.request).threadIds).length, backupPath: 'C:\\Users\\Mock\\.codex\\backups\\index.json' };
case 'get_lan_ipv4': return ['192.168.100.100'];
default:
if (command.startsWith('plugin:window|') || command.startsWith('plugin:webview|')) return null;
console.warn(`[Browser Mock] 尚未实现命令:${command}`, rawPayload);
throw new Error(`Browser Mock 尚未实现命令:${command}`);
console.warn(`[Browser Mock] Command not implemented: ${command}`, rawPayload);
throw new Error(`Browser Mock command not implemented: ${command}`);
}
};
+23 -17
View File
@@ -163,7 +163,7 @@ export function VersionManagementPage() {
}
if (task.message && !task.running) {
showNotice(task.message, task.phase === '安装失败' ? 'error' : 'info');
showNotice(task.message, task.phase === 'Installation failed' ? 'error' : 'info');
}
};
@@ -252,7 +252,7 @@ export function VersionManagementPage() {
setProgress({
running: true,
cancellable: true,
phase: '准备下载',
phase: 'Preparing download',
downloaded: 0,
total: null,
percent: null,
@@ -267,7 +267,7 @@ export function VersionManagementPage() {
setProgress({
running: false,
cancellable: false,
phase: '安装完成',
phase: 'Installation complete',
downloaded: 1,
total: 1,
percent: 100,
@@ -281,11 +281,12 @@ export function VersionManagementPage() {
} catch (error) {
manualInstallInProgressRef.current = false;
const errorMessage = String(error);
showNotice(errorMessage, errorMessage.includes('取消') ? 'info' : 'error');
const cancelled = /cancel/i.test(errorMessage);
showNotice(errorMessage, cancelled ? 'info' : 'error');
setProgress((current) => ({
running: false,
cancellable: false,
phase: errorMessage.includes('取消') ? '已取消' : '安装失败',
phase: cancelled ? 'Canceled' : 'Installation failed',
downloaded: current?.downloaded ?? 0,
total: current?.total ?? null,
percent: current?.percent ?? null,
@@ -444,9 +445,9 @@ export function VersionManagementPage() {
const progressKnown = computedPercent !== null;
const progressPercent = clampPercent(computedPercent ?? 0);
const progressText = progress
? progress.phase === '安装完成'
? progress.phase === 'Installation complete'
? t('kernel.progress.completed')
: progress.phase === '解压中'
: progress.phase === 'Extracting'
? t('kernel.progress.extracting')
: progress.total
? `${formatBytes(progress.downloaded)} / ${formatBytes(progress.total)}`
@@ -457,7 +458,7 @@ export function VersionManagementPage() {
const installDialogTone: MessageType = progress?.result
? 'success'
: progress?.phase === '安装失败'
: progress?.phase === 'Installation failed'
? 'error'
: 'info';
@@ -467,7 +468,7 @@ export function VersionManagementPage() {
: t('kernel.install.titleInstalling')
: progress?.result
? t('kernel.install.titleCompleted')
: progress?.phase === '已取消'
: progress?.phase === 'Canceled'
? t('kernel.install.titleCancelled')
: t('kernel.install.titleFailed');
@@ -895,16 +896,21 @@ function localizeInstallPhase(
t: ReturnType<typeof useI18n>['t'],
) {
const keys = {
'准备下载': 'kernel.phase.preparingDownload',
'下载中': 'kernel.phase.downloading',
'解压中': 'kernel.phase.extracting',
'准备内置内核': 'kernel.phase.preparingBundled',
'安装完成': 'kernel.phase.completed',
'安装失败': 'kernel.phase.failed',
'已取消': 'kernel.phase.cancelled',
'Preparing download': 'kernel.phase.preparingDownload',
Downloading: 'kernel.phase.downloading',
Extracting: 'kernel.phase.extracting',
'Extract bundled kernel': 'kernel.phase.preparingBundled',
'Installation complete': 'kernel.phase.completed',
'Installation failed': 'kernel.phase.failed',
Canceled: 'kernel.phase.cancelled',
} as const;
const key = keys[phase as keyof typeof keys];
return key ? t(key) : phase;
if (key) return t(key);
if (phase === 'Preparing to install the latest version') {
return t('kernel.install.installingLatest');
}
const version = phase.match(/^Preparing to install (.+)$/)?.[1];
return version ? t('kernel.install.installingVersion', { version }) : phase;
}
function clampPercent(percent: number) {
+4 -1
View File
@@ -154,7 +154,10 @@ const formatDateTime = (date: Date): string | undefined => {
}).format(date);
};
export const formatQuotaTimestamp = (value: string | undefined, locale: AppLocale = 'zh-CN'): string => {
export const formatQuotaTimestamp = (
value: string | undefined,
locale: AppLocale = getCurrentLocale(),
): string => {
if (!value) return '—';
const ms = quotaResetInstant(value);
if (ms === undefined) return '—';
+1 -1
View File
@@ -9089,7 +9089,7 @@ textarea:focus-visible {
transition: width 0.3s ease;
}
/* Events Tab (请求明细) */
/* Events tab (request details) */
.usage-events-panel {
display: flex;
width: 100%;