diff --git a/docs/frontend/application.md b/docs/frontend/application.md index bb535fe..4794914 100644 --- a/docs/frontend/application.md +++ b/docs/frontend/application.md @@ -6,7 +6,7 @@ 应用启动先请求 `system/status`,已初始化时再请求 `auth/me`。未初始化渲染安装页;初始化后未登录渲染公开只读 `/` 总览和 `/login` 登录页,登录后由 `BrowserRouter` 提供 `/` 总览和 `/settings` 设置,未登录访问 `/settings` 回到公开总览,未知路径回到 `/`。状态响应中的构建版本以 `v` 徽标显示在安装页、登录页、公开总览和登录后侧栏的品牌区域;登录后侧栏使用放大的品牌图标,图标、名称和版本徽标保持单行排列。这些分支只负责交互,服务端 session 才是安全边界。 -主题以服务端通用设置为持久来源,`localStorage` 仅用于首屏缓存;system 模式会跟随系统主题变化。总览先加载账号列表,再并发加载每个账号的 Dashboard,并在每轮请求完成 30 秒后重新读取;每个账号独立维护请求、加载和错误状态,校验响应账号,避免迟到响应覆盖其他账号。公开总览使用匿名只读接口并隐藏身份配置字段,隐藏手动刷新按钮和设置导航;服务器固定每 5 分钟同步全部账号,登录后总览默认只展示账号摘要。折叠摘要分别显示小时额度和周额度的剩余百分比及各自重置倒计时;仅有周额度时摘要居中显示该窗口。展开卡片后显示完整限额、统计和 Token 图。 +主题以服务端通用设置为持久来源,`localStorage` 仅用于首屏缓存;system 模式会跟随系统主题变化。总览先加载账号列表,再并发加载每个账号的 Dashboard,并在每轮请求完成 30 秒后重新读取;每个账号独立维护请求、加载和错误状态,校验响应账号,避免迟到响应覆盖其他账号。公开总览使用匿名只读接口并隐藏身份配置字段,隐藏手动刷新按钮和设置导航;服务器固定每 5 分钟同步全部账号,登录后总览默认只展示账号摘要。折叠摘要分别显示小时额度和周额度的剩余百分比及各自重置倒计时;周额度固定取 Codex 的 168 小时窗口,`gpt-reserve` 等同周期的命名附加额度不得替代它;仅有周额度时摘要居中显示该窗口。展开卡片后显示完整限额、统计和 Token 图。 桌面端使用固定侧栏,`800px` 及以下改为紧凑顶栏和固定底部主导航;GitHub、主题和退出位于顶栏辅助菜单。移动布局最低支持 320px,使用动态视口高度和 CSS safe-area 环境变量避开 iOS 浏览器工具栏与设备安全区。内容必须为底部导航保留空间,表单控件避免 iOS 聚焦缩放,主要交互保持至少 44px 触控区域。 diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx index a7ebe99..a64ed3c 100644 --- a/frontend/src/main.tsx +++ b/frontend/src/main.tsx @@ -1701,11 +1701,16 @@ const quickSummaryLimits = ( (limit) => limit.windowDurationMinutes > 0 && limit.windowDurationMinutes < 24 * 60, ); - const week = sorted.find( - (limit) => - limit.windowDurationMinutes >= 6 * 24 * 60 && - limit.windowDurationMinutes <= 8 * 24 * 60, - ); + const week = + sorted.find( + (limit) => + limit.limitId.toLowerCase() === "codex" && + limit.windowDurationMinutes === 168 * 60, + ) ?? + sorted.find( + (limit) => + limit.limitName == null && limit.windowDurationMinutes === 168 * 60, + ); const selected: Array<{ kind: QuickLimitKind; limit: Limit }> = []; if (hour) selected.push({ kind: "hour", limit: hour }); if (week && week !== hour) selected.push({ kind: "week", limit: week }); diff --git a/frontend/tests/settings-layout.spec.ts b/frontend/tests/settings-layout.spec.ts index 8d91544..5fd961f 100644 --- a/frontend/tests/settings-layout.spec.ts +++ b/frontend/tests/settings-layout.spec.ts @@ -83,6 +83,14 @@ test("shows every account as a collapsible summary on the overview", async ({ connected: true, }, limits: [ + { + limitId: "base_model_inference", + limitName: "gpt-reserve", + windowType: "primary", + usedPercent: 0, + windowDurationMinutes: 10_080, + resetsAt: 1_900_700_000, + }, { limitId: "primary", limitName: "5 小时限额", @@ -92,9 +100,9 @@ test("shows every account as a collapsible summary on the overview", async ({ resetsAt: 1_900_000_000, }, { - limitId: "secondary", - limitName: "周限额", - windowType: "weekly", + limitId: "codex", + limitName: null, + windowType: "secondary", usedPercent: 20, windowDurationMinutes: 10_080, resetsAt: 1_900_604_800, @@ -132,9 +140,9 @@ test("shows every account as a collapsible summary on the overview", async ({ }, limits: [ { - limitId: "secondary", - limitName: "周限额", - windowType: "weekly", + limitId: "codex", + limitName: null, + windowType: "secondary", usedPercent: 40, windowDurationMinutes: 10_080, resetsAt: 1_900_604_800, @@ -186,9 +194,10 @@ test("shows every account as a collapsible summary on the overview", async ({ await expect(firstSummary.locator('[data-window-kind="hour"]')).toContainText( "5 小时额度65%剩余", ); - await expect(firstSummary.locator('[data-window-kind="week"]')).toContainText( - "周额度80%剩余", - ); + const weeklySummary = firstSummary.locator('[data-window-kind="week"]'); + await expect(weeklySummary).toContainText("周额度80%剩余"); + await expect(weeklySummary).not.toContainText("100%剩余"); + await expect(weeklySummary).toHaveAttribute("aria-label", /周额度,剩余 80%/); await expect( firstSummary.locator(".account-summary-limit-reset"), ).toHaveCount(2);