diff --git a/src/styles.css b/src/styles.css index 583260b..e6eb57a 100644 --- a/src/styles.css +++ b/src/styles.css @@ -2350,6 +2350,32 @@ textarea:focus-visible { color: var(--theme-777067); } +.config-proxy-heading { + display: flex; + min-width: 0; + align-items: center; + justify-content: space-between; + gap: 10px; +} + +.config-proxy-heading > .config-network-label { + min-width: 0; + color: var(--theme-39352f); + font-size: var(--font-size-body); + font-weight: 700; +} + +.config-proxy-system-toggle { + display: inline-flex; + flex: none; + align-items: center; + gap: 7px; + color: var(--theme-7a746c); + font-size: var(--font-size-meta); + font-weight: 500; + cursor: pointer; +} + .config-network-input { width: 100%; height: 38px; @@ -8129,7 +8155,7 @@ textarea:focus-visible { .usage-overview-layout { display: grid; grid-template-columns: minmax(0, 1.35fr) minmax(260px, 0.65fr); - grid-template-rows: auto minmax(250px, 1fr); + grid-template-rows: auto minmax(280px, 1fr); gap: 14px; align-items: stretch; overflow-x: hidden; diff --git a/tests/fixtures/usage-layout.html b/tests/fixtures/usage-layout.html new file mode 100644 index 0000000..66cb4ab --- /dev/null +++ b/tests/fixtures/usage-layout.html @@ -0,0 +1 @@ +
diff --git a/tests/fixtures/usage-layout.tsx b/tests/fixtures/usage-layout.tsx new file mode 100644 index 0000000..f518e84 --- /dev/null +++ b/tests/fixtures/usage-layout.tsx @@ -0,0 +1,92 @@ +import React from 'react'; +import { createRoot } from 'react-dom/client'; +import { mockIPC } from '@tauri-apps/api/mocks'; +import { I18nProvider } from '../../src/i18n'; +import { UsageRecordsPage } from '../../src/pages/UsageRecordsPage'; +import '../../src/styles.css'; + +const params = new URLSearchParams(location.search); +localStorage.setItem('easy-cli-proxy-api.locale', params.get('locale') || 'zh-TW'); +localStorage.setItem('cpa-gui.usage-records-tab.v1', 'overview'); +localStorage.setItem('cpa-gui.usage-records-range.v1', '4h'); +document.documentElement.dataset.theme = params.get('theme') || 'dark'; + +const hourKey = (date: Date) => [ + date.getFullYear(), + String(date.getMonth() + 1).padStart(2, '0'), + String(date.getDate()).padStart(2, '0'), + String(date.getHours()).padStart(2, '0'), +].join('-'); +const now = new Date(); +const trendModels = [ + 'gpt-test', + 'claude-test', + 'gemini-test', + 'deepseek-test', + 'grok-test', + 'qwen-test', + 'mistral-test', + 'llama-test', +]; +const timeline = Array.from({ length: 4 }, (_, index) => ({ + hour: hourKey(new Date(now.getTime() - (3 - index) * 60 * 60 * 1000)), + requests: 12 + index, + success: 11 + index, + failure: 1, + canceled: 0, + tokens: 22_000 + index * 2_000, + models: trendModels.map((key, modelIndex) => ({ + key, + label: key, + tokens: 1_700 + modelIndex * 300 + index * 250, + })), +})); + +mockIPC(async (cmd) => { + if (cmd === 'plugin:event|listen') return 1; + if (cmd === 'plugin:event|unlisten' || cmd === 'set_app_locale') return null; + if (cmd === 'get_usage_collector_status') { + return { state: 'collecting', message: '', lastCollectedAt: now.toISOString(), totalRecords: 54 }; + } + if (cmd === 'get_usage_analysis') { + return { models: [], providers: [], sources: [], apiKeys: [] }; + } + if (cmd === 'get_usage_overview') { + return { + totalRequests: 54, + successCount: 50, + failureCount: 4, + canceledCount: 0, + successRate: 92.6, + inputTokens: 52_000, + outputTokens: 24_000, + reasoningTokens: 8_000, + cacheReadTokens: 16_000, + cacheCreationTokens: 0, + totalTokens: 100_000, + rpm: 0.23, + tpm: 416.7, + tps: 31.2, + tpsSampleCount: 54, + averageLatencyMs: 2_840, + cacheHitRate: 0.307, + estimatedCost: 1.2345, + pricedRequests: 54, + timeline, + }; + } + throw new Error(`Unhandled fixture command: ${cmd}`); +}); + +createRoot(document.getElementById('root')!).render( +