mirror of
https://github.com/yynxxxxx/Codex-X.git
synced 2026-10-18 05:59:23 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
03cde5c341 | ||
|
|
2ba23e5e77 |
@@ -2,6 +2,19 @@
|
||||
|
||||
All notable changes to Codex-X will be documented here.
|
||||
|
||||
## [v0.2.21] - 2026-07-06
|
||||
|
||||
- 进一步降低 UI 卡顿风险:将启动检测、Codex 状态读取、Provider/官方配置/备份/cc-switch 导入等剩余同步命令迁移到后台 worker。
|
||||
- 优化前端串行请求:提示词启用后的备份/提示词刷新改为并发,保存并启用自定义提示词时不再重复拉取列表。
|
||||
- 完善【技能和 MCP】导入已有:读取 cc-switch `mcp_servers` 数据库,按 `enabled_codex` 纳管并同步到 Codex `config.toml [mcp_servers]`。
|
||||
- 增强 Skills 检查更新:对带有 cc-switch 仓库元数据的 Skill 拉取 GitHub 仓库 ZIP 计算远程 hash,显示“有新版本 / 已是最新 / 远程检查失败”等状态。
|
||||
|
||||
## [v0.2.20] - 2026-07-06
|
||||
|
||||
- 继续优化指令提示词页性能:提示词列表/状态读取、保存、导入、启用、禁用等后端操作改为后台 worker,避免切页或启用模板时阻塞 UI。
|
||||
- 修复外部自定义提示词重复保存问题:切换到内置模板前会按文件名与内容双重去重,并自动清理历史 `external-*` 重复项。
|
||||
- 优化启动加载体验:启动页增加最短展示、退出淡出与动态过渡,避免从欢迎页突然跳到主页。
|
||||
|
||||
## [v0.2.19] - 2026-07-06
|
||||
|
||||
- 新增【技能和 MCP】页面:展示 Codex 当前已安装 Skills 与 MCP,支持导入已有、从 ZIP 安装 Skill、启用/禁用 Skill、启用/禁用 MCP。
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "codex-x",
|
||||
"version": "0.2.19",
|
||||
"version": "0.2.21",
|
||||
"private": true,
|
||||
"description": "Codex Switch & Instruct desktop manager",
|
||||
"type": "module",
|
||||
|
||||
Generated
+1
-1
@@ -346,7 +346,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "codex-x"
|
||||
version = "0.2.19"
|
||||
version = "0.2.21"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"dirs 5.0.1",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "codex-x"
|
||||
version = "0.2.19"
|
||||
version = "0.2.21"
|
||||
description = "Codex Switch & Instruct desktop manager"
|
||||
authors = ["yynxxxxx"]
|
||||
license = "MIT"
|
||||
|
||||
+798
-159
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://schema.tauri.app/config/2",
|
||||
"productName": "Codex-X",
|
||||
"version": "0.2.19",
|
||||
"version": "0.2.21",
|
||||
"identifier": "com.yynxxxxx.codexx",
|
||||
"build": {
|
||||
"frontendDist": "../dist",
|
||||
|
||||
@@ -816,6 +816,8 @@ function App() {
|
||||
const [backups, setBackups] = React.useState<BackupEntry[]>([]);
|
||||
const [configDir, setConfigDir] = React.useState("");
|
||||
const [loading, setLoading] = React.useState(false);
|
||||
const [bootVisible, setBootVisible] = React.useState(true);
|
||||
const [bootLeaving, setBootLeaving] = React.useState(false);
|
||||
const [toast, setToast] = React.useState<string>("");
|
||||
const [error, setError] = React.useState<string>("");
|
||||
const [providerForm, setProviderForm] = React.useState<SavedProvider>(defaultProviderForm);
|
||||
@@ -830,6 +832,7 @@ function App() {
|
||||
const skillZipImportRef = React.useRef<HTMLInputElement | null>(null);
|
||||
const promptUpdateCheckedRef = React.useRef(false);
|
||||
const skillsMcpLoadedRef = React.useRef(false);
|
||||
const bootStartedAtRef = React.useRef(Date.now());
|
||||
const providerTomlPreview = React.useMemo(() => buildProviderTomlPreview(providerForm, state), [providerForm, state]);
|
||||
const providerAuthPreview = React.useMemo(() => buildProviderAuthPreview(providerForm), [providerForm]);
|
||||
const currentInstructionId = instructionIdFromPath(state?.instructionFile);
|
||||
@@ -852,6 +855,18 @@ function App() {
|
||||
}
|
||||
}, [providerMode, providerTomlDirty, providerTomlPreview]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!state || !bootVisible || bootLeaving) return undefined;
|
||||
const elapsed = Date.now() - bootStartedAtRef.current;
|
||||
const minBootMs = 920;
|
||||
const leaveDelay = Math.max(120, minBootMs - elapsed);
|
||||
const leaveTimer = window.setTimeout(() => {
|
||||
setBootLeaving(true);
|
||||
window.setTimeout(() => setBootVisible(false), 300);
|
||||
}, leaveDelay);
|
||||
return () => window.clearTimeout(leaveTimer);
|
||||
}, [bootLeaving, bootVisible, state]);
|
||||
|
||||
|
||||
const currentProvider = state?.providers.find((p) => p.isCurrent);
|
||||
const detectedRows = React.useMemo(() => {
|
||||
@@ -993,8 +1008,15 @@ function App() {
|
||||
const handleActionResult = (result: ActionResult) => {
|
||||
setState(result.state);
|
||||
setToast(result.message);
|
||||
invoke<BackupEntry[]>("list_backups").then(setBackups).catch(() => undefined);
|
||||
invoke<SavedPrompt[]>("list_saved_prompts").then(setSavedPrompts).catch(() => undefined);
|
||||
void Promise.all([
|
||||
invoke<BackupEntry[]>("list_backups"),
|
||||
invoke<SavedPrompt[]>("list_saved_prompts"),
|
||||
])
|
||||
.then(([backupList, promptList]) => {
|
||||
setBackups(backupList);
|
||||
setSavedPrompts(promptList);
|
||||
})
|
||||
.catch(() => undefined);
|
||||
};
|
||||
|
||||
const enableInstruction = () =>
|
||||
@@ -1051,11 +1073,9 @@ function App() {
|
||||
async () => {
|
||||
const saved = await invoke<SavedPrompt>("save_prompt", { prompt: normalizedPromptForm() });
|
||||
const result = await invoke<ActionResult>("enable_saved_prompt", { configDir: configDir || null, id: saved.id });
|
||||
const promptList = await invoke<SavedPrompt[]>("list_saved_prompts");
|
||||
return { result, promptList };
|
||||
return result;
|
||||
},
|
||||
({ result, promptList }) => {
|
||||
setSavedPrompts(promptList);
|
||||
(result) => {
|
||||
setInstructionMode("list");
|
||||
setEditingPromptId(null);
|
||||
handleActionResult(result);
|
||||
@@ -1702,8 +1722,8 @@ function App() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!state ? (
|
||||
<div className="panel glass center-panel boot-panel">
|
||||
{(!state || bootVisible) ? (
|
||||
<div className={cx("panel glass center-panel boot-panel", bootLeaving && "leaving")}>
|
||||
<div className="boot-logo-wrap">
|
||||
<div className="boot-logo">Codex-X</div>
|
||||
<div className="boot-orbit" />
|
||||
|
||||
@@ -2010,6 +2010,10 @@ html, body, #root {
|
||||
overflow: hidden;
|
||||
animation: boot-panel-in .42s cubic-bezier(.2,.8,.2,1) both;
|
||||
}
|
||||
.boot-panel.leaving {
|
||||
pointer-events: none;
|
||||
animation: boot-panel-out .28s ease-in both;
|
||||
}
|
||||
.boot-panel::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
@@ -2091,6 +2095,10 @@ html, body, #root {
|
||||
from { opacity: 0; transform: translateY(10px) scale(.985); }
|
||||
to { opacity: 1; transform: translateY(0) scale(1); }
|
||||
}
|
||||
@keyframes boot-panel-out {
|
||||
from { opacity: 1; transform: translateY(0) scale(1); filter: blur(0); }
|
||||
to { opacity: 0; transform: translateY(-8px) scale(.992); filter: blur(4px); }
|
||||
}
|
||||
@keyframes boot-logo-float {
|
||||
0%, 100% { transform: translateY(0); filter: brightness(1); }
|
||||
50% { transform: translateY(-7px); filter: brightness(1.08); }
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "codex-x-workspace",
|
||||
"private": true,
|
||||
"version": "0.2.19",
|
||||
"version": "0.2.21",
|
||||
"scripts": {
|
||||
"dev": "pnpm --dir apps/desktop tauri dev",
|
||||
"build": "pnpm --dir apps/desktop tauri build",
|
||||
|
||||
Reference in New Issue
Block a user