feat: auto-install the bundled kernel version and add an X button to dismiss the launch popup

This commit is contained in:
lzt404 committed 2026-07-24 16:03:12 +08:00
1 parent 462766d0d2
commit 2ea7080a7e
3 files changed
+92

No files matched your search

+48
View File
@@ -5627,6 +5627,33 @@ fn install_bundled_core(
result
}
fn core_needs_bundled_bootstrap(install_dir: &Path) -> bool {
find_core_binary(install_dir).is_none()
}
fn auto_install_bundled_core_if_missing(app: &tauri::AppHandle) -> Result<bool, String> {
let install_dir = core_install_dir()?;
if !core_needs_bundled_bootstrap(&install_dir) {
return Ok(false);
}
let (info, archive_path) = bundled_core_archive()?
.ok_or_else(|| "未检测到 CPA 内核,且当前发行包没有匹配的离线内核".to_string())?;
let window = app
.get_webview_window("main")
.map(|webview| webview.as_ref().window())
.ok_or_else(|| "无法获取主窗口,不能自动安装离线内核".to_string())?;
let state = app.state::<CoreDownloadState>();
state.start(CancellationToken::new(), Some(info.version.clone()))?;
let result = install_bundled_core_inner(&window, state.inner(), &info, &archive_path);
if result.is_err() {
let _ = cleanup_core_work_dirs();
}
state.finish(&window, result.clone());
result?;
Ok(true)
}
#[tauri::command]
fn cancel_core_install(state: tauri::State<'_, CoreDownloadState>) {
state.cancel();
@@ -10545,6 +10572,12 @@ fn main() {
return;
};
match auto_install_bundled_core_if_missing(&core_app) {
Ok(true) => eprintln!("未检测到 CPA 内核,已自动安装内置离线版本"),
Ok(false) => {}
Err(error) => eprintln!("自动安装 CPA 离线内核失败: {error}"),
}
if config.run_on_startup {
if let Err(error) = start_core_process_inner(process_state.inner(), &config) {
eprintln!("自动启动 CPA 内核失败: {error}");
@@ -12612,6 +12645,21 @@ mod tests {
fs::remove_dir_all(root).unwrap();
}
#[test]
fn bundled_core_bootstrap_runs_only_when_no_core_binary_exists() {
let root = agent_test_home("bundled-bootstrap-detection");
let install_dir = root.join("cpa-core");
assert!(core_needs_bundled_bootstrap(&install_dir));
let existing_version = install_dir.join("existing-version");
fs::create_dir_all(&existing_version).unwrap();
fs::write(existing_version.join(core_binary_name()), b"existing core").unwrap();
assert!(!core_needs_bundled_bootstrap(&install_dir));
fs::remove_dir_all(root).unwrap();
}
#[test]
fn bundled_core_locations_include_macos_app_resources() {
let contents_dir = agent_test_home("bundled-macos-resources")
+11
View File
@@ -18,6 +18,7 @@ import {
PackageOpen,
ServerCog,
Settings,
X,
} from 'lucide-react';
import appLogo from './assets/logo.jpg';
import { CoreRuntimeProvider, useCoreRuntime } from './coreRuntime';
@@ -387,6 +388,16 @@ function AppContent() {
}
}}
>
<button
type="button"
className="close-dialog-dismiss"
aria-label={t('common.cancel')}
title={t('common.cancel')}
disabled={windowsClosePrompt.resolvingAction !== null}
onClick={() => setWindowsClosePrompt(null)}
>
<X size={17} aria-hidden="true" />
</button>
<div className="close-dialog-heading">
<span>{t('app.close.eyebrow')}</span>
<h2 id="close-dialog-title">{t('app.close.title')}</h2>
+33
View File
@@ -1542,6 +1542,7 @@ input:focus-visible {
}
.close-dialog {
position: relative;
display: grid;
gap: 18px;
width: min(430px, 100%);
@@ -1556,6 +1557,38 @@ input:focus-visible {
.close-dialog-heading {
display: grid;
gap: 5px;
padding-right: 34px;
}
.close-dialog-dismiss {
position: absolute;
top: 12px;
right: 12px;
display: grid;
width: 32px;
height: 32px;
padding: 0;
place-items: center;
border: 1px solid transparent;
border-radius: 7px;
background: transparent;
color: #7a746c;
}
.close-dialog-dismiss:hover:not(:disabled) {
border-color: #d5d2cb;
background: #f0ede6;
color: #2d2a26;
}
.close-dialog-dismiss:focus-visible {
outline: 3px solid rgba(66, 99, 127, 0.3);
outline-offset: 2px;
}
.close-dialog-dismiss:disabled {
cursor: not-allowed;
opacity: 0.45;
}
.close-dialog-heading span {