From dc61ffc2ec58ac0d838f3c721eafb16450b436d7 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 26 Aug 2026 02:20:16 +0000 Subject: [PATCH 1/3] docs: align README and store listing with 0.4.3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Correct First Run reviewer steps, document English and Simplified Chinese side-panel language, and match Settings disconnect copy to the actual deletion and permission-cleanup behavior. Co-authored-by: 天天流 --- PRIVACY.md | 4 ++-- README.md | 2 ++ README.zh-CN.md | 6 +++--- locales/en.yml | 2 +- locales/zh_CN.yml | 2 +- scripts/publication-contract.mjs | 4 ++-- scripts/publication-contract.test.js | 4 ++-- store-assets/README.md | 17 +++++++++-------- store-assets/chrome-web-store/listing-en.md | 15 +++++++++++---- store-assets/chrome-web-store/listing-zh_CN.md | 8 +++++--- 10 files changed, 38 insertions(+), 26 deletions(-) diff --git a/PRIVACY.md b/PRIVACY.md index b49c887..55f3dba 100644 --- a/PRIVACY.md +++ b/PRIVACY.md @@ -1,6 +1,6 @@ # Privacy Policy -Last updated: August 23, 2026 +Last updated: August 26, 2026 AI Limits is a locally running Chrome extension by TiantianFlow. This policy describes version 0.4.3. @@ -162,7 +162,7 @@ metrics; raw provider responses are not persisted. ## Local storage and retention AI Limits stores one application-state record in `chrome.storage.local`. It -contains display and automatic-refresh preferences, per-instance nonsecret +contains display, language, and automatic-refresh preferences, per-instance nonsecret configuration such as a normalized configurable-provider base URL and label, provider permission state, the normalized usage fields listed above, History, and sanitized refresh-attempt metadata. It does not contain provider cookies, diff --git a/README.md b/README.md index c627e14..0a984d6 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,8 @@ consumption with elapsed time to show a pace signal. Cursor also shows weekly Grok Bot usage next to its monthly Cursor-model and Other-model limits, plus a per-model included-usage breakdown. It starts empty and reads usage only after you connect an individual provider and approve that provider's optional access. +The side panel is available in English and Simplified Chinese. It follows +Chrome's language by default, and Settings can pin one language for this panel. AI Limits is an independent project by TiantianFlow. It is not affiliated with, endorsed by, or authorized by any supported provider, its parent company, or diff --git a/README.zh-CN.md b/README.zh-CN.md index 07b17cf..19bb1e4 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -18,13 +18,13 @@ AI Limits 支持二十个服务:ChatGPT、Claude、Kimi、Cursor、Grok、Mist 服务商提供完整的重置周期信息时,还会将配额消耗与已过时间比较,显示用量节奏。 Cursor 还会在 Cursor 模型与其他模型的月度限额旁显示 Grok Bot 每周用量,并提供 按模型划分的包含用量明细。扩展初始不读取任何数据;只有当你主动连接某个服务 -并批准该服务的可选访问权限后,才会读取其用量。 +并批准该服务的可选访问权限后,才会读取其用量。侧边栏提供英文和简体中文,默认跟随 +Chrome 语言,也可在 Settings 中为侧边栏单独指定。 AI Limits 是 TiantianFlow 开发的独立项目,与任何受支持的服务商、其母公司或 关联方不存在隶属、背书或授权关系。 -> 当前扩展界面为英文。下图的中文文字是项目介绍文案;嵌入的实际扩展界面 -> 仍为英文。后续完成扩展本身的简体中文支持后,我们会重新生成截图。 +> 下图外圈介绍文案为中文;图中嵌入的侧边栏截图目前仍是英文界面。 ![AI Limits Chrome 侧边栏展示示例已用视图、订阅用量、重置时间、用量节奏和服务导航](store-assets/chrome-web-store/zh_CN/screenshot-overview-1280x800.png) diff --git a/locales/en.yml b/locales/en.yml index b2b9413..2c79e14 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -206,7 +206,7 @@ settings: freshnessMethod: "{freshness} · {method} · {readOnly}" apiKeySaved: API key saved noProviders: No providers connected. - disconnectExplanation: Disconnecting removes the provider from Overview and deletes its stored local history. + disconnectExplanation: Disconnecting permanently deletes this provider's credentials, configuration, usage, and history. Permission cleanup can be delayed or shared with another instance. renameGroup: "Rename {label}" instanceLabel: Instance label saveLabelNamed: "Save label for {label}" diff --git a/locales/zh_CN.yml b/locales/zh_CN.yml index fec3603..849ff94 100644 --- a/locales/zh_CN.yml +++ b/locales/zh_CN.yml @@ -206,7 +206,7 @@ settings: freshnessMethod: "{freshness} · {method} · {readOnly}" apiKeySaved: 已保存 API 密钥 noProviders: 尚未连接服务。 - disconnectExplanation: 断开连接会将该服务从总览中移除,并删除其本地历史。 + disconnectExplanation: 断开连接会永久删除该服务的凭据、配置、用量和历史。权限清理可能延迟,或与另一实例共享。 renameGroup: "重命名 {label}" instanceLabel: 实例标签 saveLabelNamed: "保存 {label} 的标签" diff --git a/scripts/publication-contract.mjs b/scripts/publication-contract.mjs index 7d7f5f9..e056c0f 100644 --- a/scripts/publication-contract.mjs +++ b/scripts/publication-contract.mjs @@ -181,9 +181,9 @@ const requiredVisibleElevenLabsStatements = [ { key: "listing", statement: - "confirm the side panel opens with twenty permission-required cards.", + "confirm First Run opens with twenty opt-in provider rows and no prefilled usage.", error: - "Store listing reviewer flow must expect twenty permission-required cards.", + "Store listing reviewer flow must expect twenty First Run provider rows.", }, ]; const requiredVisibleV030Statements = [ diff --git a/scripts/publication-contract.test.js b/scripts/publication-contract.test.js index a3588b0..a6c5870 100644 --- a/scripts/publication-contract.test.js +++ b/scripts/publication-contract.test.js @@ -169,9 +169,9 @@ const elevenLabsPublicationStatements = [ { key: "listing", statement: - "confirm the side panel opens with twenty permission-required cards.", + "confirm First Run opens with twenty opt-in provider rows and no prefilled usage.", error: - "Store listing reviewer flow must expect twenty permission-required cards.", + "Store listing reviewer flow must expect twenty First Run provider rows.", }, ]; const v030PublicationStatements = [ diff --git a/store-assets/README.md b/store-assets/README.md index f48749f..7884222 100644 --- a/store-assets/README.md +++ b/store-assets/README.md @@ -44,14 +44,15 @@ The complete Chrome Web Store submission packs also live in this directory: These four images are for the Simplified Chinese GitHub README and social media. Their dimensions and content are suitable for future localized Chrome -Web Store screenshots, but they are not part of the current store submission. -Chrome Web Store promotional tiles are not localized, so there is no separate -Chinese promotional tile. +Web Store screenshots, but they are not part of the current English store +submission. Chrome Web Store promotional tiles are not localized, so there is +no separate Chinese promotional tile. -The surrounding project copy in these four Chinese images is Simplified -Chinese, while the embedded production extension UI remains in English. After -runtime localization is implemented, regenerate them so both the surrounding -copy and embedded UI are Chinese. +The side panel now supports English and Simplified Chinese. The current Chinese +GitHub images still embed the English production UI; only the surrounding +project copy is Chinese. Regenerate them so the embedded UI uses the Simplified +Chinese catalog. The capture preview currently still leaves the production +Cockpit in English even when `locale=zh_CN`. ### GitHub repository artwork @@ -80,7 +81,7 @@ AI_LIMITS_CHROME_PATH=/absolute/path/to/chrome pnpm assets:store The preview uses the fixed instant `2026-08-09T14:00:00.000Z`, forces light mode, `en-US`, `America/Toronto`, and a device scale factor of 1, and creates a fresh browser context without a user profile. The `locale=zh_CN` preview option -localizes only the surrounding project copy; it intentionally leaves the +localizes only the surrounding project copy; it currently still leaves the production extension UI in English. The pacing image aligns the complete Kimi and Cursor cards below the real sticky side-panel header at production scale. The History image follows the real ChatGPT quota action into the dedicated diff --git a/store-assets/chrome-web-store/listing-en.md b/store-assets/chrome-web-store/listing-en.md index 9a922dd..d05e105 100644 --- a/store-assets/chrome-web-store/listing-en.md +++ b/store-assets/chrome-web-store/listing-en.md @@ -89,6 +89,10 @@ reported balance, and **Fireworks** shows rated spend over the last 30 days. **GroqCloud** shows five-minute usage rates, and **OpenRouter** shows credits and an optional key budget. +The side panel is available in English and Simplified Chinese. It follows +Chrome's language by default; Settings can pin one language for this panel. +Chrome's own menus and the extension name stay in Chrome's language. + AI Limits keeps everything local: your usage history stays on your device and is never sent anywhere else. No ads, no AI Limits account to create, no analytics or telemetry. @@ -101,7 +105,7 @@ parent companies, or their affiliates. ### Required permissions -- `storage`: saves display and refresh preferences, provider access state, +- `storage`: saves display, language, and refresh preferences, provider access state, normalized quota/counter/balance snapshots, up to 30 days of typed per-instance History, and sanitized refresh status in `chrome.storage.local`. It also holds temporary Kimi and Cursor tab-cleanup @@ -265,7 +269,7 @@ The full policy is in [PRIVACY.md](../../PRIVACY.md). 1. Install the submitted build, or extract the ZIP and load its root as an unpacked extension. Pin AI Limits if desired, select its toolbar action, and - confirm the side panel opens with twenty permission-required cards. + confirm First Run opens with twenty opt-in provider rows and no prefilled usage. 2. **ChatGPT:** sign in at `chatgpt.com`, click **Connect** on ChatGPT, approve only the ChatGPT origin, and confirm a plan plus available quota windows or credits appears. Use the card's **Refresh** action once. @@ -362,7 +366,10 @@ The full policy is in [PRIVACY.md](../../PRIVACY.md). 22. Use the header refresh and confirm connected providers retain their previous visible data while refreshing. Open a quota window's **History** action and confirm the dedicated screen appears, then confirm a successful refresh adds - quota history while counter/spend and balance samples remain ungraphed. In Settings, turn automatic + quota history while counter/spend and balance samples remain ungraphed. From a + provider detail screen, Disconnect asks for confirmation before deleting + credentials, configuration, usage, and history. In Settings, turn automatic refresh off and on, disconnect one provider, then use **Delete all local - data** and confirm the cards return to the permission-required state. For API-key providers, inspect extension storage after Disconnect and after + data** and confirm the cards return to the permission-required state. For + API-key providers, inspect extension storage after Disconnect and after Delete all to confirm the credential record no longer contains a key. diff --git a/store-assets/chrome-web-store/listing-zh_CN.md b/store-assets/chrome-web-store/listing-zh_CN.md index 9f854ba..c0b7e61 100644 --- a/store-assets/chrome-web-store/listing-zh_CN.md +++ b/store-assets/chrome-web-store/listing-zh_CN.md @@ -44,6 +44,8 @@ AI Limits 把你的 AI 订阅用量集中显示在同一个 Chrome 侧边栏中 **ChatGPT** 和 **Claude** 直接从你已登录的账号显示套餐、用量周期、重置时间和可用额度。**Kimi** 直接读取你的用量和订阅状态;如果会话需要刷新,会有一个简短、可见的恢复步骤。**Cursor** 会显示 Cursor 模型与其他模型的月度限额以及 Grok Bot 每周用量,并按 Cursor Models 和 Other Models 分组列出各模型的包含用量(输入、输出、费用和服务商合计)。该明细视图是通用的分服务界面;目前只有 Cursor 会填充它。**Grok** 读取你已登录的消费者版套餐和每周或每月用量池,不显示按模式返回的短窗。**Mistral** 显示本月至今支出和可用额度,**Perplexity** 显示周期性、已购买和促销额度池。**ElevenLabs** 通过你自己创建的、权限受限的只读 API 密钥连接。**New API** 用你提供的密钥连接自建或第三方 API 兼容实例。AI Limits 支持多个相互独立的 New API 实例,包括同一来源下多个分别标注的密钥。**LiteLLM**、**ClawRouter**、**sub2api** 和 **LLM Proxy** 使用你提供的密钥连接可配置实例。**DeepSeek** 和 **Moonshot** 显示开发平台余额,**DeepInfra** 显示当月支出相对上限的配额或服务商报告的余额,**Fireworks** 显示最近 30 天已计价支出。**OpenAI** 读取平台组织/开发者账单(组织支出和补全用量,并在需要时回退到额度接口),**GroqCloud** 显示五分钟用量速率,**OpenRouter** 显示额度和可选密钥预算。 +侧边栏提供英文和简体中文,默认跟随 Chrome 语言;也可以在 Settings 中为侧边栏单独指定语言。Chrome 自身的菜单和扩展名称仍跟随 Chrome 的语言。 + AI Limits 的一切数据都保存在本地:你的用量历史只留在你自己的设备上,不会发送到任何别处。没有广告,无需注册 AI Limits 账号,也没有任何分析或遥测。 AI Limits 是 TiantianFlow 独立开发的项目,与任何已连接的服务提供商及其母公司或关联公司均无关联,也未获得它们的认可或授权。 @@ -52,7 +54,7 @@ AI Limits 是 TiantianFlow 独立开发的项目,与任何已连接的服务 ### 必需权限 -- `storage`:在 `chrome.storage.local` 中保存显示和刷新偏好、服务访问状态、标准化后的配额/计数/余额快照、最多 30 天的按类型分实例 History,以及经过清理的刷新状态。它还会在 `chrome.storage.session` 中保存临时的 Kimi 和 Cursor 标签页清理租约元数据。浏览器会话 cookie 和访问凭据不会被持久化。验证通过后,用户创建的服务商 API 密钥会保存在单独的 `chrome.storage.local` 凭据记录中,其 Chrome 存储访问级别仅限于受信任的扩展上下文。该记录不受操作系统钥匙串加密保护,因此从已解锁的 Chrome 配置文件、扩展 DevTools 或配置文件文件中仍可能被检查到。 +- `storage`:在 `chrome.storage.local` 中保存显示、语言和刷新偏好、服务访问状态、标准化后的配额/计数/余额快照、最多 30 天的按类型分实例 History,以及经过清理的刷新状态。它还会在 `chrome.storage.session` 中保存临时的 Kimi 和 Cursor 标签页清理租约元数据。浏览器会话 cookie 和访问凭据不会被持久化。验证通过后,用户创建的服务商 API 密钥会保存在单独的 `chrome.storage.local` 凭据记录中,其 Chrome 存储访问级别仅限于受信任的扩展上下文。该记录不受操作系统钥匙串加密保护,因此从已解锁的 Chrome 配置文件、扩展 DevTools 或配置文件文件中仍可能被检查到。 - `alarms`:仅在启用自动刷新且至少有一个服务已连接时,安排大约每 15 分钟一次的刷新周期。 - `sidePanel`:在 Chrome 侧边栏中显示本扩展的唯一应用界面。 @@ -105,7 +107,7 @@ AI Limits 是 TiantianFlow 独立开发的项目,与任何已连接的服务 ## 二十个服务的测试流程 -1. 安装提交的构建,或解压 ZIP 并将其根目录作为未打包扩展加载。如需要可固定 AI Limits,选择其工具栏操作,并确认侧边栏打开后显示二十张需要权限的卡片。 +1. 安装提交的构建,或解压 ZIP 并将其根目录作为未打包扩展加载。如需要可固定 AI Limits,选择其工具栏操作,并确认首次运行界面列出二十个可选服务行,且没有预填用量。 2. **ChatGPT:** 在 `chatgpt.com` 登录,在 ChatGPT 上点击 **连接**,只批准 ChatGPT 来源,并确认出现套餐以及可用的配额窗口或额度。使用该卡片的 **刷新** 操作一次。 3. **Claude:** 在 `claude.ai` 登录,在 Claude 上点击 **连接**,只批准 Claude 来源,并确认出现组织套餐以及可用的用量窗口或额外用量额度。使用该卡片的 **刷新** 操作一次。 4. **Kimi:** 在 `www.kimi.com` 登录,在 Kimi 上点击 **连接**,批准 Kimi 来源以及 `cookies` 和 `scripting`,并确认出现可用用量和订阅数据。如果在交互式刷新期间需要会话恢复,可能会出现一个不活动的 Kimi 标签页。确认恢复流程在 10 秒后停止等待凭据并尝试尽力清理;不要把关闭过程或浏览器 API 错误期间的延迟清理当作保证被违反。 @@ -126,4 +128,4 @@ AI Limits 是 TiantianFlow 独立开发的项目,与任何已连接的服务 19. **OpenAI:** 连接临时密钥,只批准 `https://api.openai.com/*`,并确认显示组织支出/补全用量或旧版可用额度回退。记录所需组织或项目范围。 20. **GroqCloud:** 连接临时密钥,只批准 `https://api.groq.com/*`。具备所需范围时确认显示 Prometheus 速率;标准密钥返回 404 时应显示需要更高范围。 21. **OpenRouter:** 连接临时密钥,只批准 `https://openrouter.ai/*`,并确认显示额度余额,以及服务报告时的密钥预算。 -22. 使用顶部刷新,并确认已连接的服务在刷新期间保留之前的可见数据。打开某个配额窗口的 **历史** 操作,确认出现独立界面,然后确认一次成功刷新会新增配额历史,而计数/支出和余额样本仍不会被绘图。在**设置**中关闭再打开自动刷新,断开一个服务,然后使用 **删除全部本地数据**,并确认卡片回到需要权限的状态。对于 API 密钥服务,在断开连接之后以及删除全部之后检查扩展存储,确认其凭据记录中不再包含密钥。 +22. 使用顶部刷新,并确认已连接的服务在刷新期间保留之前的可见数据。打开某个配额窗口的 **历史** 操作,确认出现独立界面,然后确认一次成功刷新会新增配额历史,而计数/支出和余额样本仍不会被绘图。在服务详情页,断开连接会先要求确认,然后再删除凭据、配置、用量和历史。在**设置**中关闭再打开自动刷新,断开一个服务,然后使用 **删除全部本地数据**,并确认卡片回到需要权限的状态。对于 API 密钥服务,在断开连接之后以及删除全部之后检查扩展存储,确认其凭据记录中不再包含密钥。 From 73d0ab3bfad2b9db0a4327fe00a6cb108a8c21a0 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 26 Aug 2026 02:34:25 +0000 Subject: [PATCH 2/3] fix: confirm Settings disconnect before deleting local data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Settings Disconnect now uses the same inline confirmation as the detail screen, so credentials, configuration, usage, and history are not deleted on the first click. Co-authored-by: 天天流 --- entrypoints/sidepanel/App.test.tsx | 14 +++ entrypoints/sidepanel/Cockpit.test.tsx | 41 ++++++++ entrypoints/sidepanel/styles.css | 6 +- entrypoints/sidepanel/views/SettingsView.tsx | 97 +++++++++++++++++-- store-assets/chrome-web-store/listing-en.md | 10 +- .../chrome-web-store/listing-zh_CN.md | 4 +- 6 files changed, 154 insertions(+), 18 deletions(-) diff --git a/entrypoints/sidepanel/App.test.tsx b/entrypoints/sidepanel/App.test.tsx index 65fc7ae..a7ebf49 100644 --- a/entrypoints/sidepanel/App.test.tsx +++ b/entrypoints/sidepanel/App.test.tsx @@ -1255,6 +1255,11 @@ describe("side-panel App", () => { name: "Disconnect relay.example · 22222222", }), ); + fireEvent.click( + screen.getByRole("button", { + name: "Confirm disconnect relay.example · 22222222", + }), + ); expect(await screen.findByRole("status")).toHaveTextContent( "Couldn’t disconnect relay.example · 22222222.", ); @@ -1555,6 +1560,9 @@ describe("side-panel App", () => { await screen.findByText("ChatGPT"); fireEvent.click(screen.getByRole("button", { name: "Settings" })); fireEvent.click(screen.getByRole("button", { name: "Disconnect ChatGPT" })); + fireEvent.click( + screen.getByRole("button", { name: "Confirm disconnect ChatGPT" }), + ); expect(await screen.findByRole("status")).toHaveTextContent( "Disconnected ChatGPT and deleted its stored usage.", @@ -1584,6 +1592,9 @@ describe("side-panel App", () => { await screen.findByText("ChatGPT"); fireEvent.click(screen.getByRole("button", { name: "Settings" })); fireEvent.click(screen.getByRole("button", { name: "Disconnect ChatGPT" })); + fireEvent.click( + screen.getByRole("button", { name: "Confirm disconnect ChatGPT" }), + ); expect(await screen.findByRole("status")).toHaveTextContent( "Deleted ChatGPT’s local usage. Browser access could not be removed.", @@ -1615,6 +1626,9 @@ describe("side-panel App", () => { fireEvent.click(screen.getByRole("button", { name: "Settings" })); fireEvent.click(screen.getByRole("switch", { name: "Automatic refresh" })); fireEvent.click(screen.getByRole("button", { name: "Disconnect ChatGPT" })); + fireEvent.click( + screen.getByRole("button", { name: "Confirm disconnect ChatGPT" }), + ); fireEvent.click(screen.getByRole("button", { name: "Delete all local data" })); fireEvent.click(screen.getByRole("button", { name: "Confirm delete all local data" })); diff --git a/entrypoints/sidepanel/Cockpit.test.tsx b/entrypoints/sidepanel/Cockpit.test.tsx index 776e32e..e83c370 100644 --- a/entrypoints/sidepanel/Cockpit.test.tsx +++ b/entrypoints/sidepanel/Cockpit.test.tsx @@ -483,6 +483,10 @@ describe("Cockpit", () => { ); fireEvent.click(screen.getByRole("button", { name: "Settings" })); fireEvent.click(screen.getByRole("button", { name: "Disconnect Work relay" })); + expect(onDisconnectInstance).not.toHaveBeenCalled(); + fireEvent.click( + screen.getByRole("button", { name: "Confirm disconnect Work relay" }), + ); expect(onDisconnectInstance).toHaveBeenCalledWith(WORK_NEW_API_ID); expect(onDisconnectInstance).not.toHaveBeenCalledWith(PERSONAL_NEW_API_ID); }); @@ -1488,6 +1492,10 @@ describe("Cockpit", () => { ); fireEvent.click(screen.getByRole("button", { name: "Settings for ChatGPT" })); fireEvent.click(screen.getByRole("button", { name: "Disconnect ChatGPT" })); + expect(onDisconnectInstance).not.toHaveBeenCalled(); + fireEvent.click( + screen.getByRole("button", { name: "Confirm disconnect ChatGPT" }), + ); expect(onDisconnectInstance).toHaveBeenCalledWith("chatgpt:default"); expect( @@ -1505,6 +1513,35 @@ describe("Cockpit", () => { ).not.toBeInTheDocument(); }); + it("keeps Settings open when disconnect is cancelled", () => { + const onDisconnectInstance = vi.fn(); + render( + , + ); + + fireEvent.click(screen.getByRole("button", { name: "Settings" })); + fireEvent.click(screen.getByRole("button", { name: "Disconnect ChatGPT" })); + fireEvent.click( + screen.getByRole("button", { name: "Cancel disconnect ChatGPT" }), + ); + + expect(onDisconnectInstance).not.toHaveBeenCalled(); + expect( + screen.getByRole("region", { name: "Provider settings" }), + ).toBeVisible(); + expect( + screen.queryByRole("group", { name: "Confirm disconnect" }), + ).not.toBeInTheDocument(); + expect(screen.getByRole("button", { name: "Disconnect ChatGPT" })).toHaveFocus(); + }); + it("shows a truthful unavailable screen if the active provider disconnects", () => { const state = createFixtureState(NOW); const view = render( @@ -2908,6 +2945,10 @@ describe("Cockpit", () => { expect(action.parentElement).toBe(providerActions); } fireEvent.click(screen.getByRole("button", { name: "Disconnect Claude" })); + expect(onDisconnectProvider).not.toHaveBeenCalled(); + fireEvent.click( + screen.getByRole("button", { name: "Confirm disconnect Claude" }), + ); expect(onDisconnectProvider).toHaveBeenCalledWith("claude"); }); diff --git a/entrypoints/sidepanel/styles.css b/entrypoints/sidepanel/styles.css index d0af819..1aa0cd7 100644 --- a/entrypoints/sidepanel/styles.css +++ b/entrypoints/sidepanel/styles.css @@ -2396,8 +2396,8 @@ section[aria-labelledby="overview-title"] .provider-list { align-items: flex-start; } -.settings-provider-row--renaming .settings-provider-actions { - justify-content: flex-start; +.settings-provider-row--disconnecting .settings-disconnect-confirm { + min-width: 0; } .settings-provider-actions > button { @@ -2421,7 +2421,7 @@ section[aria-labelledby="overview-title"] .provider-list { background: color-mix(in srgb, var(--surface-muted) 55%, var(--surface)); } -.settings-provider-actions > button:last-child { +.settings-provider-actions > button.settings-provider-actions__disconnect { color: #be123c; } diff --git a/entrypoints/sidepanel/views/SettingsView.tsx b/entrypoints/sidepanel/views/SettingsView.tsx index 8ade6e7..af0e440 100644 --- a/entrypoints/sidepanel/views/SettingsView.tsx +++ b/entrypoints/sidepanel/views/SettingsView.tsx @@ -105,6 +105,14 @@ export function SettingsView({ const restoreRenameFocus = useRef(undefined); const renameInputRef = useRef(null); const restoreRenameInputFocus = useRef(false); + const [confirmDisconnectId, setConfirmDisconnectId] = + useState(); + const disconnectTriggerRefs = useRef< + Partial> + >({}); + const restoreDisconnectFocus = useRef( + undefined, + ); const labelsByInstance = instanceLabels(instances); useEffect(() => { @@ -129,6 +137,23 @@ export function SettingsView({ } }, [renameError, renamePending]); + useEffect(() => { + if ( + confirmDisconnectId && + !instances.some((instance) => instance.id === confirmDisconnectId) + ) { + setConfirmDisconnectId(undefined); + } + }, [confirmDisconnectId, instances]); + + useEffect(() => { + if (!confirmDisconnectId && restoreDisconnectFocus.current) { + const instanceId = restoreDisconnectFocus.current; + restoreDisconnectFocus.current = undefined; + disconnectTriggerRefs.current[instanceId]?.focus(); + } + }, [confirmDisconnectId]); + return (
@@ -365,6 +397,7 @@ export function SettingsView({ restoreRenameInputFocus.current = false; setRenameError(""); setLabelDraft(instance.userLabel ?? ""); + setConfirmDisconnectId(undefined); setRenamingInstanceId(instance.id); }} > @@ -398,15 +431,61 @@ export function SettingsView({ ) : null} - + {confirmingDisconnect ? null : ( + + )} + {confirmingDisconnect ? ( +
+

{l10n.t("providerDetail.disconnectConfirmWarning")}

+
+ + +
+
+ ) : null} ); diff --git a/store-assets/chrome-web-store/listing-en.md b/store-assets/chrome-web-store/listing-en.md index d05e105..3223e6e 100644 --- a/store-assets/chrome-web-store/listing-en.md +++ b/store-assets/chrome-web-store/listing-en.md @@ -89,8 +89,9 @@ reported balance, and **Fireworks** shows rated spend over the last 30 days. **GroqCloud** shows five-minute usage rates, and **OpenRouter** shows credits and an optional key budget. -The side panel is available in English and Simplified Chinese. It follows -Chrome's language by default; Settings can pin one language for this panel. +The side panel supports English and Simplified Chinese. It follows Chrome when +Chrome is set to one of those languages, and falls back to English for other +Chrome languages. Settings can pin English or Simplified Chinese for this panel. Chrome's own menus and the extension name stay in Chrome's language. AI Limits keeps everything local: your usage history stays on your device and @@ -369,7 +370,8 @@ The full policy is in [PRIVACY.md](../../PRIVACY.md). quota history while counter/spend and balance samples remain ungraphed. From a provider detail screen, Disconnect asks for confirmation before deleting credentials, configuration, usage, and history. In Settings, turn automatic - refresh off and on, disconnect one provider, then use **Delete all local - data** and confirm the cards return to the permission-required state. For + refresh off and on, disconnect one provider after confirming the same + deletion, then use **Delete all local data** and confirm the cards return to + the permission-required state. For API-key providers, inspect extension storage after Disconnect and after Delete all to confirm the credential record no longer contains a key. diff --git a/store-assets/chrome-web-store/listing-zh_CN.md b/store-assets/chrome-web-store/listing-zh_CN.md index c0b7e61..c54c709 100644 --- a/store-assets/chrome-web-store/listing-zh_CN.md +++ b/store-assets/chrome-web-store/listing-zh_CN.md @@ -44,7 +44,7 @@ AI Limits 把你的 AI 订阅用量集中显示在同一个 Chrome 侧边栏中 **ChatGPT** 和 **Claude** 直接从你已登录的账号显示套餐、用量周期、重置时间和可用额度。**Kimi** 直接读取你的用量和订阅状态;如果会话需要刷新,会有一个简短、可见的恢复步骤。**Cursor** 会显示 Cursor 模型与其他模型的月度限额以及 Grok Bot 每周用量,并按 Cursor Models 和 Other Models 分组列出各模型的包含用量(输入、输出、费用和服务商合计)。该明细视图是通用的分服务界面;目前只有 Cursor 会填充它。**Grok** 读取你已登录的消费者版套餐和每周或每月用量池,不显示按模式返回的短窗。**Mistral** 显示本月至今支出和可用额度,**Perplexity** 显示周期性、已购买和促销额度池。**ElevenLabs** 通过你自己创建的、权限受限的只读 API 密钥连接。**New API** 用你提供的密钥连接自建或第三方 API 兼容实例。AI Limits 支持多个相互独立的 New API 实例,包括同一来源下多个分别标注的密钥。**LiteLLM**、**ClawRouter**、**sub2api** 和 **LLM Proxy** 使用你提供的密钥连接可配置实例。**DeepSeek** 和 **Moonshot** 显示开发平台余额,**DeepInfra** 显示当月支出相对上限的配额或服务商报告的余额,**Fireworks** 显示最近 30 天已计价支出。**OpenAI** 读取平台组织/开发者账单(组织支出和补全用量,并在需要时回退到额度接口),**GroqCloud** 显示五分钟用量速率,**OpenRouter** 显示额度和可选密钥预算。 -侧边栏提供英文和简体中文,默认跟随 Chrome 语言;也可以在 Settings 中为侧边栏单独指定语言。Chrome 自身的菜单和扩展名称仍跟随 Chrome 的语言。 +侧边栏仅支持英文和简体中文。跟随 Chrome 时,仅在 Chrome 为英文或简体中文时使用对应语言,其他 Chrome 语言会回退到英文。也可以在 Settings 中为侧边栏固定英文或简体中文。Chrome 自身的菜单和扩展名称仍跟随 Chrome 的语言。 AI Limits 的一切数据都保存在本地:你的用量历史只留在你自己的设备上,不会发送到任何别处。没有广告,无需注册 AI Limits 账号,也没有任何分析或遥测。 @@ -128,4 +128,4 @@ AI Limits 是 TiantianFlow 独立开发的项目,与任何已连接的服务 19. **OpenAI:** 连接临时密钥,只批准 `https://api.openai.com/*`,并确认显示组织支出/补全用量或旧版可用额度回退。记录所需组织或项目范围。 20. **GroqCloud:** 连接临时密钥,只批准 `https://api.groq.com/*`。具备所需范围时确认显示 Prometheus 速率;标准密钥返回 404 时应显示需要更高范围。 21. **OpenRouter:** 连接临时密钥,只批准 `https://openrouter.ai/*`,并确认显示额度余额,以及服务报告时的密钥预算。 -22. 使用顶部刷新,并确认已连接的服务在刷新期间保留之前的可见数据。打开某个配额窗口的 **历史** 操作,确认出现独立界面,然后确认一次成功刷新会新增配额历史,而计数/支出和余额样本仍不会被绘图。在服务详情页,断开连接会先要求确认,然后再删除凭据、配置、用量和历史。在**设置**中关闭再打开自动刷新,断开一个服务,然后使用 **删除全部本地数据**,并确认卡片回到需要权限的状态。对于 API 密钥服务,在断开连接之后以及删除全部之后检查扩展存储,确认其凭据记录中不再包含密钥。 +22. 使用顶部刷新,并确认已连接的服务在刷新期间保留之前的可见数据。打开某个配额窗口的 **历史** 操作,确认出现独立界面,然后确认一次成功刷新会新增配额历史,而计数/支出和余额样本仍不会被绘图。在服务详情页,断开连接会先要求确认,然后再删除凭据、配置、用量和历史。在**设置**中关闭再打开自动刷新,断开一个服务时同样需要确认该删除,然后使用 **删除全部本地数据**,并确认卡片回到需要权限的状态。对于 API 密钥服务,在断开连接之后以及删除全部之后检查扩展存储,确认其凭据记录中不再包含密钥。 From ef7fc604797e5212896ddff232d466a79e7aed65 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 26 Aug 2026 02:34:34 +0000 Subject: [PATCH 3/3] docs: say unsupported Chrome languages fall back to English MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follow Chrome only uses English or Simplified Chinese; every other Chrome language falls back to English rather than implying full Chrome localization. Co-authored-by: 天天流 --- README.md | 6 ++++-- README.zh-CN.md | 5 +++-- locales/en.yml | 2 +- locales/zh_CN.yml | 2 +- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 0a984d6..31df610 100644 --- a/README.md +++ b/README.md @@ -22,8 +22,10 @@ consumption with elapsed time to show a pace signal. Cursor also shows weekly Grok Bot usage next to its monthly Cursor-model and Other-model limits, plus a per-model included-usage breakdown. It starts empty and reads usage only after you connect an individual provider and approve that provider's optional access. -The side panel is available in English and Simplified Chinese. It follows -Chrome's language by default, and Settings can pin one language for this panel. +The side panel supports English and Simplified Chinese. Follow Chrome uses those +languages when Chrome is set to one of them, and falls back to English for every +other Chrome language. Settings can pin English or Simplified Chinese for this +panel. AI Limits is an independent project by TiantianFlow. It is not affiliated with, endorsed by, or authorized by any supported provider, its parent company, or diff --git a/README.zh-CN.md b/README.zh-CN.md index 19bb1e4..30ab6ce 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -18,8 +18,9 @@ AI Limits 支持二十个服务:ChatGPT、Claude、Kimi、Cursor、Grok、Mist 服务商提供完整的重置周期信息时,还会将配额消耗与已过时间比较,显示用量节奏。 Cursor 还会在 Cursor 模型与其他模型的月度限额旁显示 Grok Bot 每周用量,并提供 按模型划分的包含用量明细。扩展初始不读取任何数据;只有当你主动连接某个服务 -并批准该服务的可选访问权限后,才会读取其用量。侧边栏提供英文和简体中文,默认跟随 -Chrome 语言,也可在 Settings 中为侧边栏单独指定。 +并批准该服务的可选访问权限后,才会读取其用量。侧边栏仅支持英文和简体中文;跟随 +Chrome 时,仅在 Chrome 为英文或简体中文时使用对应语言,其他 Chrome 语言会回退到英文。 +也可在 Settings 中为侧边栏固定英文或简体中文。 AI Limits 是 TiantianFlow 开发的独立项目,与任何受支持的服务商、其母公司或 关联方不存在隶属、背书或授权关系。 diff --git a/locales/en.yml b/locales/en.yml index 2c79e14..a0599fa 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -198,7 +198,7 @@ settings: automaticOn: On · about every 15 minutes automaticOff: Off · refresh manually from the header language: Language - languageDescription: Applies to this side panel. Chrome’s own menus and the extension name stay in Chrome’s language. + languageDescription: English and Simplified Chinese only. Other Chrome languages fall back to English. Chrome’s own menus and the extension name stay in Chrome’s language. languageFollowChrome: Follow Chrome connectedProviders: Connected providers instanceSettings: "{label} settings" diff --git a/locales/zh_CN.yml b/locales/zh_CN.yml index 849ff94..eb3c841 100644 --- a/locales/zh_CN.yml +++ b/locales/zh_CN.yml @@ -198,7 +198,7 @@ settings: automaticOn: 开 · 大约每 15 分钟 automaticOff: 关 · 请从顶部手动刷新 language: 语言 - languageDescription: 仅作用于本侧边栏。Chrome 自身的菜单和扩展名称仍跟随 Chrome 的语言。 + languageDescription: 仅支持英文和简体中文。其他 Chrome 语言会回退到英文。Chrome 自身的菜单和扩展名称仍跟随 Chrome 的语言。 languageFollowChrome: 跟随 Chrome connectedProviders: 已连接的服务 instanceSettings: "{label} 设置"