Compare commits

...
5 Commits
Author SHA1 Message Date
yyyyyynnn 69e0a46430 Fix Intel macOS runner 2026-07-04 19:41:30 +08:00
yyyyyynnn ab19b2ef33 Add Intel macOS and Windows portable builds 2026-07-04 19:32:40 +08:00
yyyyyynnn ff34902641 Add star history chart 2026-07-04 16:28:20 +08:00
yyyyyynnn 07e4968ade Add thanks section 2026-07-04 16:26:37 +08:00
yyyyyynnn ba5e90e24f Polish session page and external links 2026-07-04 15:44:25 +08:00
11 changed files with 105 additions and 34 deletions
+27 -3
View File
@@ -21,10 +21,16 @@ jobs:
fail-fast: false
matrix:
include:
- name: macOS
- name: macOS Apple Silicon
platform: macos-latest
args: --bundles dmg
artifact: codex-x-macos
artifact: codex-x-macos-arm64
paths: |
apps/desktop/src-tauri/target/release/bundle/dmg/*.dmg
- name: macOS Intel
platform: macos-15-intel
args: --bundles dmg
artifact: codex-x-macos-x64
paths: |
apps/desktop/src-tauri/target/release/bundle/dmg/*.dmg
- name: Linux
@@ -40,6 +46,7 @@ jobs:
artifact: codex-x-windows
paths: |
apps/desktop/src-tauri/target/release/bundle/msi/*.msi
apps/desktop/src-tauri/Codex-X_*_windows_x64_portable.zip
steps:
- name: Checkout
@@ -81,6 +88,23 @@ jobs:
- name: Build Tauri bundles
run: pnpm --dir apps/desktop tauri build ${{ matrix.args }}
- name: Create Windows portable zip
if: matrix.platform == 'windows-latest'
run: |
set -euo pipefail
version=$(node -p "require('./apps/desktop/package.json').version")
portable_dir="apps/desktop/src-tauri/portable/Codex-X_${version}_windows_x64_portable"
rm -rf "apps/desktop/src-tauri/portable"
mkdir -p "$portable_dir"
cp apps/desktop/src-tauri/target/release/codex-x.exe "$portable_dir/Codex-X.exe"
cat > "$portable_dir/README-portable.txt" <<'EOF'
Codex-X Windows Portable
Double click Codex-X.exe to run. No MSI installation is required.
Microsoft Edge WebView2 Runtime is required; Windows 10/11 usually already includes it.
EOF
powershell -NoProfile -Command "Compress-Archive -Path 'apps/desktop/src-tauri/portable/Codex-X_${version}_windows_x64_portable/*' -DestinationPath 'apps/desktop/src-tauri/Codex-X_${version}_windows_x64_portable.zip' -Force"
- name: Upload bundle artifacts
uses: actions/upload-artifact@v4
with:
@@ -139,7 +163,7 @@ jobs:
else
gh release create "$tag" --title "$title" --notes-file release-body.md --latest
fi
mapfile -t assets < <(find release-assets -type f \( -name '*.dmg' -o -name '*.msi' -o -name '*.deb' -o -name '*.rpm' \) -print | sort)
mapfile -t assets < <(find release-assets -type f \( -name '*.dmg' -o -name '*.msi' -o -name '*.deb' -o -name '*.rpm' -o -name '*.zip' \) -print | sort)
if [ "${#assets[@]}" -eq 0 ]; then
echo "No release assets found" >&2
exit 1
+19
View File
@@ -2,6 +2,25 @@
All notable changes to Codex-X will be documented here.
## [v0.2.17] - 2026-07-04
- 修复 macOS Intel Release 构建 runner:从已不可用/长时间排队的 `macos-13` 切换为 `macos-15-intel`。
- 继续保留 macOS Apple Silicon / macOS Intel / Windows MSI / Windows portable ZIP / Linux deb/rpm 多平台产物。
## [v0.2.16] - 2026-07-04
- Release 新增 macOS Intel 构建,Intel Mac 用户可下载 x64 DMG。
- macOS Release 现在同时提供 Apple Silicon 与 Intel 两种 DMG。
- Windows Release 新增 portable ZIP,包含可直接运行的 `Codex-X.exe`,无需 MSI 安装。
- 发布流程支持上传 `.zip` portable 产物,并更新 README 下载说明。
## [v0.2.15] - 2026-07-04
- 会话管理页移除 CODEX_HOME 与 Provider Sync 备份位置展示,页面信息更简洁。
- 优化 Windows 打开下载页体验:后端改为后台线程 spawn 浏览器,不再等待 `cmd /C start`,避免 WebView 卡顿 2-3 秒。
- 统一所有外部链接按钮走异步打开逻辑,打开项目主页、反馈页、下载页都不会阻塞界面。
- 修复 macOS 顶部 toast 被 Overlay 标题栏遮挡的问题,toast 自动下移到标题栏安全区下方。
## [v0.2.14] - 2026-07-04
- 修复 macOS Overlay 标题栏遮挡内容的问题,为红黄绿窗口按钮预留完整安全区。
+13 -2
View File
@@ -129,7 +129,7 @@ Codex-X 不是普通的配置文件编辑器,而是一个面向 Codex CLI 的
</tr>
<tr>
<td align="center">📦 跨平台发布</td>
<td>提供 macOS <code>.dmg</code>、Windows <code>.msi</code>、Linux <code>.deb</code> / <code>.rpm</code>,支持应用内检测更新。</td>
<td>提供 macOS Apple Silicon / Intel <code>.dmg</code>、Windows <code>.msi</code> / portable <code>.zip</code>、Linux <code>.deb</code> / <code>.rpm</code>,支持应用内检测更新。</td>
</tr>
</table>
</div>
@@ -230,8 +230,10 @@ Codex-X 可以读取 Codex 本地会话数据:
### 6. 跨平台桌面软件
- macOS `.dmg`
- macOS Apple Silicon `.dmg`
- macOS Intel `.dmg`
- Windows `.msi`
- Windows Portable `.zip`
- Linux `.deb` / `.rpm`
- GitHub Releases 自动构建发布
- 应用内检查更新
@@ -303,3 +305,12 @@ xattr -dr com.apple.quarantine /Applications/Codex-X.app
## 许可证
本项目基于 [MIT License](https://github.com/yynxxxxx/Codex-X/blob/main/LICENSE) 开源。
## 致谢 / Thanks
感谢 [LINUX DO 论坛](https://linux.do/) 社区的关注、反馈与支持。
## Star History
[![Star History Chart](https://api.star-history.com/svg?repos=yynxxxxx/Codex-X&type=Date)](https://www.star-history.com/#yynxxxxx/Codex-X&Date)
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "codex-x",
"version": "0.2.14",
"version": "0.2.17",
"private": true,
"description": "Codex Switch & Instruct desktop manager",
"type": "module",
+1 -1
View File
@@ -337,7 +337,7 @@ dependencies = [
[[package]]
name = "codex-x"
version = "0.2.14"
version = "0.2.17"
dependencies = [
"chrono",
"dirs 5.0.1",
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "codex-x"
version = "0.2.14"
version = "0.2.17"
description = "Codex Switch & Instruct desktop manager"
authors = ["yynxxxxx"]
license = "MIT"
+26 -15
View File
@@ -2288,26 +2288,37 @@ fn restore_backup(config_dir: Option<String>, backup_id: String) -> Result<Actio
#[tauri::command]
fn open_url(url: String) -> std::result::Result<(), String> {
let trimmed = url.trim();
let trimmed = url.trim().to_string();
if trimmed.is_empty() {
return Err("URL 为空".to_string());
}
let status = if cfg!(target_os = "macos") {
Command::new("open").arg(trimmed).status()
} else if cfg!(target_os = "windows") {
Command::new("cmd")
.args(["/C", "start", "", trimmed])
.status()
} else {
Command::new("xdg-open").arg(trimmed).status()
};
// Do not wait for the browser process. On Windows, waiting for `cmd /C start` can
// visibly freeze the WebView for a few seconds before the default browser appears.
std::thread::spawn(move || {
#[cfg(target_os = "macos")]
{
let _ = Command::new("open").arg(&trimmed).spawn();
}
match status {
Ok(s) if s.success() => Ok(()),
Ok(s) => Err(format!("打开失败,退出码: {:?}", s.code())),
Err(e) => Err(format!("打开失败: {e}")),
}
#[cfg(target_os = "windows")]
{
use std::os::windows::process::CommandExt;
const CREATE_NO_WINDOW: u32 = 0x08000000;
let _ = Command::new("cmd")
.creation_flags(CREATE_NO_WINDOW)
.args(["/C", "start", ""])
.arg(&trimmed)
.spawn();
}
#[cfg(all(not(target_os = "macos"), not(target_os = "windows")))]
{
let _ = Command::new("xdg-open").arg(&trimmed).spawn();
}
});
Ok(())
}
pub fn run() {
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "Codex-X",
"version": "0.2.14",
"version": "0.2.17",
"identifier": "com.yynxxxxx.codexx",
"build": {
"frontendDist": "../dist",
+14 -9
View File
@@ -1040,6 +1040,15 @@ function App() {
const restoreBackup = (backupId: string) =>
call(() => invoke<ActionResult>("restore_backup", { configDir: configDir || null, backupId }), handleActionResult);
const openExternalUrl = React.useCallback((url?: string | null) => {
if (!url) return;
window.setTimeout(() => {
void invoke("open_url", { url }).catch(() => {
setToast(lang === "zh" ? "打开浏览器失败" : "Failed to open browser");
});
}, 0);
}, [lang]);
const checkForUpdates = React.useCallback(async ({ quiet = false }: { quiet?: boolean } = {}) => {
const repo = aboutInfo?.githubRepo || FALLBACK_GITHUB_REPO;
const appVersion = aboutInfo?.appVersion || "0.0.0";
@@ -1289,7 +1298,7 @@ function App() {
<div className="update-actions">
<button className="primary-btn" onClick={() => {
setUpdatePromptOpen(false);
if (releaseInfo.htmlUrl) void invoke("open_url", { url: releaseInfo.htmlUrl });
openExternalUrl(releaseInfo.htmlUrl);
}}>
<Download size={16} /> {lang === "zh" ? "现在下载" : "Download now"}
</button>
@@ -1317,7 +1326,7 @@ function App() {
<strong>{lang === "zh" ? "发现新版本" : "New version found"}</strong>
<p>{lang === "zh" ? `Codex-X ${releaseInfo.latestVersion || ""} 已发布` : `Codex-X ${releaseInfo.latestVersion || ""} is available`}</p>
</div>
<button className="secondary-btn small" onClick={() => releaseInfo.htmlUrl && void invoke("open_url", { url: releaseInfo.htmlUrl })}>
<button className="secondary-btn small" onClick={() => openExternalUrl(releaseInfo.htmlUrl)}>
{lang === "zh" ? "查看更新" : "View"}
</button>
</div>
@@ -1561,10 +1570,6 @@ function App() {
<StatCard icon={<Code2 size={20} />} label={lang === "zh" ? "SQLite 数据库" : "SQLite DBs"} value={sessionStatus?.sqliteDbs ?? "-"} ok />
</div>
<div className="session-info-list">
<div><span>CODEX_HOME</span><code>{sessionStatus?.codexDir || state.codexDir}</code></div>
<div><span>{lang === "zh" ? "备份位置" : "Backup"}</span><code>{sessionStatus?.backupDir || `${state.codexDir}/backups_state/provider-sync`}</code></div>
</div>
<div className="session-list-card">
<div className="session-list-head">
@@ -1742,8 +1747,8 @@ function App() {
<div><span>{lang === "zh" ? "项目地址" : "Project"}</span><code>{aboutInfo?.projectUrl || `https://github.com/${FALLBACK_GITHUB_REPO}`}</code></div>
</div>
<div className="about-actions">
<button className="secondary-btn" onClick={() => void invoke("open_url", { url: aboutInfo?.projectUrl || `https://github.com/${FALLBACK_GITHUB_REPO}` })}><ExternalLink size={16} /> {lang === "zh" ? "打开项目主页" : "Open project"}</button>
<button className="ghost-btn" onClick={() => void invoke("open_url", { url: `${aboutInfo?.projectUrl || `https://github.com/${FALLBACK_GITHUB_REPO}`}/issues` })}><ExternalLink size={16} /> {lang === "zh" ? "反馈问题" : "Issues"}</button>
<button className="secondary-btn" onClick={() => openExternalUrl(aboutInfo?.projectUrl || `https://github.com/${FALLBACK_GITHUB_REPO}`)}><ExternalLink size={16} /> {lang === "zh" ? "打开项目主页" : "Open project"}</button>
<button className="ghost-btn" onClick={() => openExternalUrl(`${aboutInfo?.projectUrl || `https://github.com/${FALLBACK_GITHUB_REPO}`}/issues`)}><ExternalLink size={16} /> {lang === "zh" ? "反馈问题" : "Issues"}</button>
</div>
</section>
@@ -1758,7 +1763,7 @@ function App() {
</div>
<div className="about-actions">
<button className="primary-btn" onClick={() => void checkForUpdates()} disabled={releaseInfo.status === "checking"}><RefreshCw size={16} className={cx(releaseInfo.status === "checking" && "spin")} /> {lang === "zh" ? "检查更新" : "Check updates"}</button>
<button className="secondary-btn" onClick={() => releaseInfo.htmlUrl && void invoke("open_url", { url: releaseInfo.htmlUrl })} disabled={!releaseInfo.htmlUrl}><Download size={16} /> {lang === "zh" ? "打开下载页" : "Open releases"}</button>
<button className="secondary-btn" onClick={() => openExternalUrl(releaseInfo.htmlUrl)} disabled={!releaseInfo.htmlUrl}><Download size={16} /> {lang === "zh" ? "打开下载页" : "Open releases"}</button>
</div>
</section>
</section>
+1
View File
@@ -201,6 +201,7 @@ input, select, textarea {
}
.toast.ok { color: #dbfff3; background: rgba(12, 112, 81, .88); border: 1px solid rgba(31,242,178,.3); }
.toast.error { color: #fff; background: rgba(157, 38, 63, .92); border: 1px solid rgba(255,120,145,.35); animation: none; }
.app-shell.mac-shell .toast { top: calc(24px + var(--mac-titlebar-safe)); }
@keyframes toast { 0% { opacity: 0; transform: translateY(-8px); } 12%, 84% { opacity: 1; transform: translateY(0); } 100% { opacity: 0; transform: translateY(-8px); } }
.update-mask {
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "codex-x-workspace",
"private": true,
"version": "0.2.14",
"version": "0.2.17",
"scripts": {
"dev": "pnpm --dir apps/desktop tauri dev",
"build": "pnpm --dir apps/desktop tauri build",