diff --git a/README.md b/README.md index e135bea..492ba48 100644 --- a/README.md +++ b/README.md @@ -59,9 +59,9 @@ DeepSeek Monitor Windows is a Windows desktop adaptation inspired by JayHome137/ ## 下载安装 -普通用户可从 [GitHub Releases](https://github.com/Joyi-code/DeepSeekMonitorWindows/releases/latest) 下载并运行 `DeepSeekMonitorWindows_1.1.0_x64-setup.exe`。覆盖安装新版本前无需卸载旧版本。 +普通用户可从 [GitHub Releases](https://github.com/Joyi-code/DeepSeekMonitorWindows/releases/latest) 下载并运行 `DeepSeekMonitorWindows_1.2.0_x64-setup.exe`。覆盖安装新版本前无需卸载旧版本。 -安装包 SHA256:`B13EF28BB7E803D923E1A00BCE4A873B4EB7F2F592AFF690173C2E9291F1D13F`。 +安装包 SHA256:构建 1.2.0 后于 GitHub Release 安装包资产中更新(见发布页)。 运行环境要求: @@ -204,12 +204,17 @@ Rust 后端依赖: 完整发布记录见 [GitHub Releases](https://github.com/Joyi-code/DeepSeekMonitorWindows/releases)。 +### v1.2.0 + +- 新增 `deepseek-v4-flash-vision-exp`(V4 Flash Vision)模型的用量监控:仪表盘新增第三个模型行(置于 V4 Flash 与 V4 Pro 之间),缓存命中趋势图合并该模型的命中/未命中/输出 Token,模型详情页支持查看其按日消耗。 +- 版本号升至 1.2.0。 + ### v1.1.0 - 支持缓存命中、缓存未命中与输出 Token 的明细显示。 - 增加亮色 UI 皮肤,支持在主面板一键切换并记住用户选择。 - 设置页增加当前版本号显示。 -- 当前 GitHub Release `v1.1.0` 已标记为 Latest,安装包为 `DeepSeekMonitorWindows_1.1.0_x64-setup.exe`。 +- GitHub Release `v1.1.0` 曾标记为 Latest,安装包为 `DeepSeekMonitorWindows_1.1.0_x64-setup.exe`(历史保留)。 - 安装包 SHA256:`B13EF28BB7E803D923E1A00BCE4A873B4EB7F2F592AFF690173C2E9291F1D13F`。 - 历史 Release `v1.0.1` 和旧安装包继续保留,便于回退和版本追溯。 diff --git a/README_EN.md b/README_EN.md index 9802027..c0ea331 100644 --- a/README_EN.md +++ b/README_EN.md @@ -55,9 +55,9 @@ Douyin: 像素与咖啡时光 ## Download and Install -Users can download and run `DeepSeekMonitorWindows_1.1.0_x64-setup.exe` from [GitHub Releases](https://github.com/Joyi-code/DeepSeekMonitorWindows/releases/latest). You do not need to uninstall an older version before installing an update. +Users can download and run `DeepSeekMonitorWindows_1.2.0_x64-setup.exe` from [GitHub Releases](https://github.com/Joyi-code/DeepSeekMonitorWindows/releases/latest). You do not need to uninstall an older version before installing an update. -Installer SHA256: `B13EF28BB7E803D923E1A00BCE4A873B4EB7F2F592AFF690173C2E9291F1D13F`. +Installer SHA256: generated and published together with the 1.2.0 installer on GitHub Releases. Runtime requirements: @@ -202,12 +202,17 @@ Rust backend dependencies: See [GitHub Releases](https://github.com/Joyi-code/DeepSeekMonitorWindows/releases) for the complete release history. +### v1.2.0 + +- Added usage monitoring for the `deepseek-v4-flash-vision-exp` (V4 Flash Vision) model: a new third model row on the dashboard (placed between V4 Flash and V4 Pro), the cache-hit trend chart now merges this model's hit/miss/output tokens, and the model detail page shows its daily usage. +- Bumped the version to 1.2.0. + ### v1.1.0 - Added detailed cache hit, cache miss, and output token statistics. - Added a light UI theme that can be toggled from the dashboard and persists across sessions. - Added the current version number to the Settings page. -- GitHub Release `v1.1.0` is marked as Latest. The installer is `DeepSeekMonitorWindows_1.1.0_x64-setup.exe`. +- GitHub Release `v1.1.0` was marked as Latest. The installer is `DeepSeekMonitorWindows_1.1.0_x64-setup.exe` (kept for history). - Installer SHA256: `B13EF28BB7E803D923E1A00BCE4A873B4EB7F2F592AFF690173C2E9291F1D13F`. - The historical `v1.0.1` release and its installer remain available for rollback and version tracking. diff --git a/package.json b/package.json index f4b7707..261f6e1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "deepseek-monitor-windows", - "version": "1.1.0", + "version": "1.2.0", "private": true, "license": "MIT", "type": "module", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 467eac2..fcf0efd 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "app" -version = "1.1.0" +version = "1.2.0" description = "DeepSeek Monitor Windows desktop app" authors = ["you"] license = "MIT" @@ -25,3 +25,7 @@ tauri = { version = "2.11.2", features = ["tray-icon"] } tauri-plugin-log = "2" tauri-plugin-single-instance = "2" reqwest = { version = "0.12", features = ["json"] } + +[features] +# This feature is used for production builds or when a dev server is not specified, DO NOT REMOVE!! +custom-protocol = ["tauri/custom-protocol"] diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index fcee6d3..8a44680 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -665,6 +665,10 @@ pub fn run() { pro_cache_hit: u64, pro_cache_miss: u64, pro_response: u64, + vision_tokens: u64, + vision_cache_hit: u64, + vision_cache_miss: u64, + vision_response: u64, total_tokens: u64, total_cost: f64, } @@ -817,6 +821,7 @@ pub fn run() { let label = match model_usage.model.as_str() { "deepseek-v4-flash" => Some(("flash", "V4 Flash")), "deepseek-v4-pro" => Some(("pro", "V4 Pro")), + "deepseek-v4-flash-vision-exp" => Some(("flashvision", "V4 Flash Vision")), _ => None, }; if let Some((key, name)) = label { @@ -853,6 +858,10 @@ pub fn run() { let mut pro_hit = 0u64; let mut pro_miss = 0u64; let mut pro_resp = 0u64; + let mut vision = 0u64; + let mut vision_hit = 0u64; + let mut vision_miss = 0u64; + let mut vision_resp = 0u64; let mut total = 0u64; for model_usage in &day.data { let (tokens, _, hit, miss, response) = token_breakdown(&model_usage.usage); @@ -870,6 +879,12 @@ pub fn run() { pro_miss += miss; pro_resp += response; } + "deepseek-v4-flash-vision-exp" => { + vision += tokens; + vision_hit += hit; + vision_miss += miss; + vision_resp += response; + } _ => {} } } @@ -883,6 +898,10 @@ pub fn run() { pro_cache_hit: pro_hit, pro_cache_miss: pro_miss, pro_response: pro_resp, + vision_tokens: vision, + vision_cache_hit: vision_hit, + vision_cache_miss: vision_miss, + vision_response: vision_resp, total_tokens: total, total_cost: cost_by_date.get(&day.date).copied().unwrap_or(0.0), }); diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 1ae87f1..5ca128b 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "../node_modules/@tauri-apps/cli/config.schema.json", "productName": "DeepSeekMonitorWindows", - "version": "1.1.0", + "version": "1.2.0", "identifier": "com.deepseek.monitor.windows", "build": { "frontendDist": "../dist", @@ -15,9 +15,11 @@ { "label": "main", "title": "DeepSeek Monitor Windows", - "width": 356, - "height": 600, - "resizable": false, + "width": 384, + "height": 660, + "minWidth": 340, + "minHeight": 540, + "resizable": true, "fullscreen": false, "decorations": false, "transparent": true, diff --git a/src/main.tsx b/src/main.tsx index 963e14f..883daa7 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -10,6 +10,7 @@ import { CheckCircle2, Clipboard, CreditCard, + Eye, Info, KeyRound, Power, @@ -23,7 +24,7 @@ import { import "./styles.css"; type ViewName = "dashboard" | "settings" | "detail"; -type ModelName = "flash" | "pro"; +type ModelName = "flash" | "pro" | "flashvision"; type AppConfig = { apiKeyConfigured: boolean; apiKeyPreview: string | null; @@ -62,6 +63,10 @@ type UsageDay = { proCacheHit: number; proCacheMiss: number; proResponse: number; + visionTokens: number; + visionCacheHit: number; + visionCacheMiss: number; + visionResponse: number; totalTokens: number; totalCost: number; }; @@ -99,21 +104,25 @@ const recentUsageDays = (days: UsageDay[], count = 7): UsageDay[] => { const today = new Date(); return Array.from({ length: count }, (_, index) => { const date = dateKey(addDays(today, index - count + 1)); - return ( - source.get(date) ?? { - date, - flashTokens: 0, - flashCacheHit: 0, - flashCacheMiss: 0, - flashResponse: 0, - proTokens: 0, - proCacheHit: 0, - proCacheMiss: 0, - proResponse: 0, - totalTokens: 0, - totalCost: 0, - } - ); + return ( + source.get(date) ?? { + date, + flashTokens: 0, + flashCacheHit: 0, + flashCacheMiss: 0, + flashResponse: 0, + proTokens: 0, + proCacheHit: 0, + proCacheMiss: 0, + proResponse: 0, + visionTokens: 0, + visionCacheHit: 0, + visionCacheMiss: 0, + visionResponse: 0, + totalTokens: 0, + totalCost: 0, + } + ); }); }; const previousMonth = (date: Date) => { @@ -313,7 +322,8 @@ function DashboardPanel({ }; const flash = usage?.models.find((item) => item.key === "flash") ?? null; const pro = usage?.models.find((item) => item.key === "pro") ?? null; - const maxTokens = Math.max(flash?.totalTokens ?? 0, pro?.totalTokens ?? 0, 1); + const vision = usage?.models.find((item) => item.key === "flashvision") ?? null; + const maxTokens = Math.max(flash?.totalTokens ?? 0, pro?.totalTokens ?? 0, vision?.totalTokens ?? 0, 1); const today = usage?.days.find((day) => day.date === todayStr()) ?? null; const todayCost = usageState === "ok" && today ? today.totalCost : null; const monthCost = usageState === "ok" && usage ? usage.monthCost : null; @@ -364,6 +374,13 @@ function DashboardPanel({ state={usageState} onClick={() => onDetail("flash")} /> + onDetail("flashvision")} + /> void; }) { const isFlash = modelKey === "flash"; - const name = isFlash ? "V4 Flash" : "V4 Pro"; + const isVision = modelKey === "flashvision"; + const name = isFlash ? "V4 Flash" : isVision ? "V4 Flash Vision" : "V4 Pro"; const tokensText = data ? `${fmtInt(data.totalTokens)} Tokens` : state === "loading" @@ -467,19 +485,19 @@ function UsageRow({ return (
- {isFlash ? : } + {isFlash ? : isVision ? : }

{title}

diff --git a/src/styles.css b/src/styles.css index 991c184..16e2afd 100644 --- a/src/styles.css +++ b/src/styles.css @@ -7,6 +7,7 @@ --brand-light: #6b85ff; --flash: #4091ff; --pro: #da38f0; + --vision: #ff5d73; --orange: #ff9c2b; --skin-accent: #e0a838; --panel-bg: rgba(105, 86, 38, 0.82); @@ -19,6 +20,7 @@ * { box-sizing: border-box; + scroll-behavior: auto; } html, @@ -41,8 +43,6 @@ button { .stage { width: 100vw; height: 100vh; - display: grid; - place-items: start center; overflow: hidden; background: transparent; } @@ -59,17 +59,19 @@ button { } .panel { - width: min(356px, 100vw); - height: min(600px, 100vh); + width: 100%; + height: 100%; border-radius: 22px; - padding: 0 16px 16px; + padding: 0 clamp(10px, 2.4vw, 18px) clamp(10px, 2.4vh, 18px); overflow: hidden; } .dashboard-panel { display: flex; flex-direction: column; - gap: 9px; + gap: clamp(6px, 1.4vh, 10px); + min-height: 0; + overflow: hidden; } .panel-header { @@ -78,6 +80,7 @@ button { justify-content: space-between; padding-top: 18px; margin-bottom: 2px; + flex: 0 0 auto; } .title-lockup { @@ -146,6 +149,7 @@ button { } .balance-card { + flex: 0 0 auto; padding: 13px 14px 13px; } @@ -194,7 +198,7 @@ button { .balance-amount { margin-top: 14px; - font-size: 32px; + font-size: clamp(22px, 5.6vw, 34px); line-height: 1; font-weight: 900; color: #6570ff; @@ -202,7 +206,7 @@ button { } .balance-amount.balance-dim { - font-size: 22px; + font-size: clamp(18px, 4vw, 24px); color: rgba(var(--fg), 0.6); } @@ -257,12 +261,13 @@ button { .usage-stack { display: flex; flex-direction: column; - gap: 7px; + gap: clamp(5px, 1vh, 7px); + flex: 0 0 auto; } .usage-row { width: 100%; - min-height: 64px; + min-height: clamp(54px, 9.5vh, 66px); display: flex; align-items: center; gap: 12px; @@ -287,6 +292,10 @@ button { color: #c98bff; } +.cache-hit-rate.vision { + color: #ff7a8f; +} + .model-badge { display: grid; place-items: center; @@ -311,6 +320,11 @@ button { color: var(--pro); } +.model-badge.vision { + background: rgba(255, 93, 115, 0.15); + color: var(--vision); +} + .usage-main { min-width: 0; flex: 1; @@ -363,6 +377,10 @@ button { background: linear-gradient(90deg, #d93ff2, #7463d7); } +.vision-fill { + background: linear-gradient(90deg, #ff5d73, #ff8fab); +} + .usage-price { width: 55px; text-align: right; @@ -385,8 +403,8 @@ button { .chart-card { flex: 1 1 auto; height: auto; - min-height: 142px; - padding: 12px 14px 14px; + min-height: clamp(120px, 24vh, 220px); + padding: clamp(10px, 1.8vh, 14px) clamp(10px, 2vw, 16px) clamp(10px, 2vh, 16px); display: flex; flex-direction: column; overflow: visible; /* 让 hover 弹窗可溢出卡片显示 */ @@ -433,7 +451,7 @@ button { .bar-value { color: rgba(var(--fg), 0.72); - font-size: 7px; + font-size: clamp(6px, 1.5vw, 10px); font-weight: 800; } @@ -482,13 +500,13 @@ button { .bar-day { color: var(--text-faint); - font-size: 9px; + font-size: clamp(8px, 1.6vw, 11px); font-weight: 800; } .settings-panel { - width: min(420px, 100vw); - height: min(620px, 100vh); + width: 100%; + height: 100%; border-radius: 22px; overflow: hidden; } @@ -497,18 +515,9 @@ button { height: 100%; overflow-y: auto; overflow-x: hidden; - padding: 24px 20px; - scrollbar-color: rgba(var(--fg), 0.38) transparent; - scrollbar-width: thin; -} - -.settings-inner::-webkit-scrollbar { - width: 6px; -} - -.settings-inner::-webkit-scrollbar-thumb { - border-radius: 999px; - background: rgba(var(--fg), 0.38); + padding: clamp(16px, 3vh, 24px) clamp(14px, 3vw, 22px); + scroll-behavior: auto; + -webkit-overflow-scrolling: auto; } .settings-header { @@ -757,10 +766,11 @@ button:disabled { } .detail-panel { - padding: 16px; + padding: clamp(12px, 2.6vw, 18px); display: flex; flex-direction: column; - gap: 12px; + gap: clamp(8px, 1.6vh, 14px); + min-height: 0; } .floating-close { @@ -775,12 +785,13 @@ button:disabled { } .detail-hero { - margin-top: 26px; - min-height: 82px; + margin-top: clamp(18px, 4vh, 28px); + min-height: clamp(70px, 12vh, 88px); + flex: 0 0 auto; display: flex; align-items: center; gap: 14px; - padding: 14px 18px; + padding: clamp(12px, 2.2vw, 16px) clamp(14px, 3vw, 20px); border-radius: 18px; } @@ -800,13 +811,15 @@ button:disabled { .detail-metrics { display: grid; grid-template-columns: 1fr 1fr; - gap: 12px; + gap: clamp(8px, 1.6vh, 12px); + flex: 0 0 auto; } .metric-card { - min-height: 78px; + min-height: clamp(64px, 11vh, 84px); border-radius: 18px; padding: 14px 16px; + flex: 0 0 auto; } .metric-card span { @@ -835,13 +848,17 @@ button:disabled { color: var(--pro); } +.vision { + color: var(--vision); +} + .detail-chart { - flex: 1; + flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; border-radius: 18px; - padding: 14px 18px 16px; + padding: clamp(12px, 2vh, 16px) clamp(14px, 3vw, 20px) clamp(12px, 2.2vh, 18px); background: linear-gradient(180deg, rgba(26, 50, 21, 0.58), rgba(34, 41, 17, 0.62)); } @@ -1100,6 +1117,7 @@ button:disabled { --brand-light: #4d84ff; --flash: #2d7ff5; --pro: #c02fde; + --vision: #e23e5e; --orange: #ef8400; --skin-accent: #2d6cf6; } @@ -1160,6 +1178,10 @@ button:disabled { color: #9333ea; } +[data-theme="light"] .cache-hit-rate.vision { + color: #d6336c; +} + [data-theme="light"] .model-badge.flash { background: rgba(45, 127, 245, 0.18); } @@ -1167,6 +1189,10 @@ button:disabled { background: rgba(192, 47, 222, 0.18); } +[data-theme="light"] .model-badge.vision { + background: rgba(226, 62, 94, 0.16); +} + [data-theme="light"] .progress-track { background: rgba(40, 70, 100, 0.12); }