Skip to content

Commit c4fa689

Browse files
author
李杰
committed
release: v0.21.4
1 parent a7df49e commit c4fa689

50 files changed

Lines changed: 1100 additions & 85 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,22 @@ All notable changes to Cockpit Tools will be documented in this file.
66

77
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
88

9+
---
10+
## [0.21.4] - 2026-04-16
11+
12+
### Added
13+
- **Codex account export now supports Cockpit Tools, sub2api, and CPA formats**: the export dialog can switch formats before preview, copy, or save so Codex credentials can be moved directly into each target tool.
14+
- **Instance account pickers now support tag filtering while binding accounts**: instance dropdowns can search and narrow accounts by tags, making large account pools easier to bind without memorizing email addresses.
15+
- **Account pages now support keyboard refresh shortcuts**: `Cmd/Ctrl + R` and `F5` on Windows trigger the visible page refresh action without clicking the toolbar button.
16+
17+
### Changed
18+
- **Tag-grouped account views now surface the default group first and keep scroll position after saving tags**: newly added untagged accounts are easier to find, and editing tags no longer jumps long lists back to the top.
19+
- **Codex and GitHub Copilot table layouts are refined for large screens**: subscription badges stay on one line, sticky action columns blend with row backgrounds, and 2K-width tables read more cleanly.
20+
- **Floating card startup is now disabled by default for new configs**: fresh installs no longer auto-open the floating card window on launch unless users opt in.
21+
22+
### Fixed
23+
- **GitHub Copilot OAuth account import now always issues a fresh device code for each new attempt**: retrying after a failed authorization no longer reuses an expired 8-digit code or require an app restart.
24+
925
---
1026
## [0.21.3] - 2026-04-13
1127

CHANGELOG.zh-CN.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,22 @@
66

77
格式参考 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.0.0/)
88

9+
---
10+
## [0.21.4] - 2026-04-16
11+
12+
### 新增
13+
- **Codex 账号导出现已支持 Cockpit Tools、sub2api 和 CPA 三种格式**:导出弹框可在预览、复制和保存前切换目标格式,便于把 Codex 凭证直接迁移到对应工具。
14+
- **实例页账号选择器现已支持按标签筛选**:绑定账号时可在下拉内先搜索、再按标签收窄账号范围,大账号池里不再需要只靠邮箱硬找。
15+
- **账号页现已支持键盘刷新快捷键**`Cmd/Ctrl + R` 以及 Windows 下的 `F5` 会直接触发当前页面可见的刷新操作,无需再点工具栏按钮。
16+
17+
### 变更
18+
- **按标签分组的账号视图现已把默认组放到最前,并在保存标签后保留滚动位置**:新加但尚未打标签的账号更容易被第一时间看到,编辑标签后也不会再把长列表强制拉回顶部。
19+
- **Codex 与 GitHub Copilot 表格现已针对大屏做布局收敛**:订阅标签固定单行展示,粘性操作列与行背景保持一致,2K 宽度下的表格阅读更稳定。
20+
- **悬浮卡片现已改为新配置默认不开机展示**:新安装或首次生成配置时不会再自动弹出悬浮卡片窗口,只有用户主动开启后才会在启动时显示。
21+
22+
### 修复
23+
- **GitHub Copilot OAuth 导入现已在每次重试时重新申请新的设备码**:授权失败后重试不再复用已失效的 8 位码,也不再需要重启应用才能继续添加账号。
24+
925
---
1026
## [0.21.3] - 2026-04-13
1127

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "cockpit-tools",
33
"private": true,
4-
"version": "0.21.3",
4+
"version": "0.21.4",
55
"type": "module",
66
"scripts": {
77
"dev": "vite",

src-tauri/Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src-tauri/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "cockpit-tools"
3-
version = "0.21.3"
3+
version = "0.21.4"
44
description = "Cockpit Tools"
55
authors = ["jlcodes"]
66
license = "CC-BY-NC-SA-4.0"

src-tauri/src/modules/config.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -470,7 +470,7 @@ fn default_hide_dock_icon() -> bool {
470470
false
471471
}
472472
fn default_floating_card_show_on_startup() -> bool {
473-
true
473+
false
474474
}
475475
fn default_floating_card_always_on_top() -> bool {
476476
false

src-tauri/src/modules/github_copilot_oauth.rs

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -194,13 +194,10 @@ async fn request_device_code() -> Result<DeviceCodeResponse, String> {
194194

195195
pub async fn start_login() -> Result<GitHubCopilotOAuthStartResponse, String> {
196196
if let Some(existing) = get_pending_login() {
197-
if existing.expires_at > now_timestamp() {
198-
logger::log_info(&format!(
199-
"GitHub Copilot OAuth 复用登录会话: login_id={}",
200-
existing.login_id
201-
));
202-
return Ok(to_start_response(&existing));
203-
}
197+
logger::log_info(&format!(
198+
"GitHub Copilot OAuth 发现进行中的登录会话,将创建新会话并覆盖旧会话: login_id={}",
199+
existing.login_id
200+
));
204201
}
205202

206203
let payload = request_device_code().await?;

src-tauri/src/modules/trae_account.rs

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1869,18 +1869,20 @@ fn ensure_auth_raw_for_inject(account: &TraeAccount, existing_auth_raw: Option<&
18691869

18701870
let user_id = resolve_account_user_id_for_auth_object(account, &roots);
18711871

1872-
let username = pick_string_multi(
1873-
&roots,
1874-
&[
1875-
&["ScreenName"],
1876-
&["nickname"],
1877-
&["name"],
1878-
&["displayName"],
1879-
&["account", "username"],
1880-
],
1881-
)
1882-
.or_else(|| normalize_non_empty(account.nickname.as_deref()))
1883-
.unwrap_or_else(|| account.email.clone());
1872+
let username = normalize_non_empty(account.nickname.as_deref())
1873+
.or_else(|| {
1874+
pick_string_multi(
1875+
&roots,
1876+
&[
1877+
&["ScreenName"],
1878+
&["nickname"],
1879+
&["name"],
1880+
&["displayName"],
1881+
&["account", "username"],
1882+
],
1883+
)
1884+
})
1885+
.unwrap_or_else(|| account.email.clone());
18841886

18851887
let email = normalize_email(
18861888
pick_string_multi(
@@ -1963,8 +1965,9 @@ fn ensure_auth_raw_for_inject(account: &TraeAccount, existing_auth_raw: Option<&
19631965
.map(|value| to_store_region(value.as_str()))
19641966
.unwrap_or_else(|| "UNKNOWN".to_string());
19651967

1968+
let ai_region_roots = [profile_root, server_raw, auth_raw];
19661969
let ai_region = pick_string_multi(
1967-
&roots,
1970+
&ai_region_roots,
19681971
&[
19691972
&["AIRegion"],
19701973
&["userRegion", "_aiRegion"],

src-tauri/tauri.conf.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"$schema": "https://schema.tauri.app/config/2",
33
"productName": "Cockpit Tools",
4-
"version": "0.21.3",
4+
"version": "0.21.4",
55
"identifier": "com.jlcodes.cockpit-tools",
66
"build": {
77
"beforeDevCommand": "npm run dev",

src/App.tsx

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,36 @@ function getQuotaAlertQuickSettingsType(platform: QuotaAlertPlatform): QuickSett
387387
}
388388
}
389389

390+
function isElementVisible(element: HTMLElement): boolean {
391+
return element.getClientRects().length > 0;
392+
}
393+
394+
function triggerPageRefreshButton(): boolean {
395+
const buttons = Array.from(
396+
document.querySelectorAll<HTMLButtonElement>('button.btn.btn-secondary.icon-only:not(:disabled)'),
397+
);
398+
399+
const target = buttons.find((button) => {
400+
if (!isElementVisible(button)) {
401+
return false;
402+
}
403+
return !!button.querySelector('svg.lucide-refresh-cw');
404+
});
405+
406+
if (!target) {
407+
return false;
408+
}
409+
410+
target.click();
411+
return true;
412+
}
413+
414+
function isWindowsPlatform(): boolean {
415+
const navWithUAData = navigator as Navigator & { userAgentData?: { platform?: string } };
416+
const platform = navWithUAData.userAgentData?.platform || navigator.platform || '';
417+
return platform.toLowerCase().includes('win');
418+
}
419+
390420
function MainApp() {
391421
const { t } = useTranslation();
392422
const sideNavLayoutMode = useSideNavLayoutStore((state) => state.mode);
@@ -501,6 +531,27 @@ function MainApp() {
501531
// 启用自动刷新 hook
502532
useAutoRefresh();
503533

534+
useEffect(() => {
535+
const handleRefreshShortcut = (event: KeyboardEvent) => {
536+
const isRefreshKey = event.key.toLowerCase() === 'r';
537+
const isWindowsF5 = isWindowsPlatform() && event.key === 'F5';
538+
const hasMainModifier = event.metaKey || event.ctrlKey;
539+
const matchMainRefresh = isRefreshKey && hasMainModifier && !event.altKey && !event.shiftKey;
540+
const matchWindowsRefresh = isWindowsF5 && !event.metaKey && !event.ctrlKey && !event.altKey && !event.shiftKey;
541+
if ((!matchMainRefresh && !matchWindowsRefresh) || event.repeat) {
542+
return;
543+
}
544+
event.preventDefault();
545+
event.stopPropagation();
546+
triggerPageRefreshButton();
547+
};
548+
549+
window.addEventListener('keydown', handleRefreshShortcut, true);
550+
return () => {
551+
window.removeEventListener('keydown', handleRefreshShortcut, true);
552+
};
553+
}, []);
554+
504555
useEffect(() => {
505556
void fetchTopRightAdState();
506557
}, [fetchTopRightAdState]);

0 commit comments

Comments
 (0)