mirror of
https://github.com/clash-verge-rev/clash-verge-rev
synced 2025-05-05 03:23:57 +08:00
feat: add error prompt for initial config loading to prevent switching to invalid subscription
This commit is contained in:
parent
c6477dfda4
commit
1bd503a654
@ -16,7 +16,7 @@
|
|||||||
#### 新增了:
|
#### 新增了:
|
||||||
- Clash Verge Rev 从现在开始不再强依赖系统服务和管理权限
|
- Clash Verge Rev 从现在开始不再强依赖系统服务和管理权限
|
||||||
- 支持根据用户偏好选择Sidecar(用户空间)模式或安装服务
|
- 支持根据用户偏好选择Sidecar(用户空间)模式或安装服务
|
||||||
- 增加载入初始配置文件的错误提示
|
- 增加载入初始配置文件的错误提示,防止切换到错误的订阅配置
|
||||||
|
|
||||||
#### 优化了:
|
#### 优化了:
|
||||||
- 重构了后端内核管理逻辑,更轻量化和有效的管理内核,提高了性能和稳定性
|
- 重构了后端内核管理逻辑,更轻量化和有效的管理内核,提高了性能和稳定性
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
use super::CmdResult;
|
use super::CmdResult;
|
||||||
use crate::{
|
use crate::{
|
||||||
config::*,
|
config::{Config, IProfiles, PrfItem, PrfOption},
|
||||||
core::{tray::Tray, *},
|
core::{handle, tray::Tray, CoreManager},
|
||||||
feat, logging, logging_error, ret_err,
|
feat, logging, ret_err,
|
||||||
utils::{dirs, help, logging::Type},
|
utils::{dirs, help, logging::Type},
|
||||||
wrap_err,
|
wrap_err,
|
||||||
};
|
};
|
||||||
@ -10,31 +10,16 @@ use crate::{
|
|||||||
/// 获取配置文件列表
|
/// 获取配置文件列表
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn get_profiles() -> CmdResult<IProfiles> {
|
pub fn get_profiles() -> CmdResult<IProfiles> {
|
||||||
let _ = tray::Tray::global().update_menu();
|
let _ = Tray::global().update_menu();
|
||||||
Ok(Config::profiles().data().clone())
|
Ok(Config::profiles().data().clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 增强配置文件
|
/// 增强配置文件
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn enhance_profiles() -> CmdResult {
|
pub async fn enhance_profiles() -> CmdResult {
|
||||||
match CoreManager::global().update_config().await {
|
wrap_err!(feat::enhance_profiles().await)?;
|
||||||
Ok((true, _)) => {
|
|
||||||
logging!(info, Type::Cmd, true, "配置更新成功");
|
|
||||||
logging_error!(Type::Tray, true, Tray::global().update_tooltip());
|
|
||||||
handle::Handle::refresh_clash();
|
handle::Handle::refresh_clash();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
|
||||||
Ok((false, error_msg)) => {
|
|
||||||
println!("[enhance_profiles] 配置验证失败: {}", error_msg);
|
|
||||||
handle::Handle::notice_message("config_validate::error", &error_msg);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
println!("[enhance_profiles] 更新过程发生错误: {}", e);
|
|
||||||
handle::Handle::notice_message("config_validate::process_terminated", e.to_string());
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 导入配置文件
|
/// 导入配置文件
|
||||||
@ -83,6 +68,81 @@ pub async fn patch_profiles_config(profiles: IProfiles) -> CmdResult<bool> {
|
|||||||
let current_profile = Config::profiles().latest().current.clone();
|
let current_profile = Config::profiles().latest().current.clone();
|
||||||
logging!(info, Type::Cmd, true, "当前配置: {:?}", current_profile);
|
logging!(info, Type::Cmd, true, "当前配置: {:?}", current_profile);
|
||||||
|
|
||||||
|
// 如果要切换配置,先检查目标配置文件是否有语法错误
|
||||||
|
if let Some(new_profile) = profiles.current.as_ref() {
|
||||||
|
if current_profile.as_ref() != Some(new_profile) {
|
||||||
|
logging!(info, Type::Cmd, true, "正在切换到新配置: {}", new_profile);
|
||||||
|
|
||||||
|
// 获取目标配置文件路径
|
||||||
|
let profiles_config = Config::profiles();
|
||||||
|
let profiles_data = profiles_config.latest();
|
||||||
|
let config_file_result = match profiles_data.get_item(new_profile) {
|
||||||
|
Ok(item) => {
|
||||||
|
if let Some(file) = &item.file {
|
||||||
|
let path = dirs::app_profiles_dir().map(|dir| dir.join(file));
|
||||||
|
path.ok()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
logging!(error, Type::Cmd, true, "获取目标配置信息失败: {}", e);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 如果获取到文件路径,检查YAML语法
|
||||||
|
if let Some(file_path) = config_file_result {
|
||||||
|
if !file_path.exists() {
|
||||||
|
logging!(
|
||||||
|
error,
|
||||||
|
Type::Cmd,
|
||||||
|
true,
|
||||||
|
"目标配置文件不存在: {}",
|
||||||
|
file_path.display()
|
||||||
|
);
|
||||||
|
handle::Handle::notice_message(
|
||||||
|
"config_validate::file_not_found",
|
||||||
|
&format!("{}", file_path.display()),
|
||||||
|
);
|
||||||
|
return Ok(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
match std::fs::read_to_string(&file_path) {
|
||||||
|
Ok(content) => match serde_yaml::from_str::<serde_yaml::Value>(&content) {
|
||||||
|
Ok(_) => {
|
||||||
|
logging!(info, Type::Cmd, true, "目标配置文件语法正确");
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
let error_msg = format!(" {}", err);
|
||||||
|
logging!(
|
||||||
|
error,
|
||||||
|
Type::Cmd,
|
||||||
|
true,
|
||||||
|
"目标配置文件存在YAML语法错误:{}",
|
||||||
|
error_msg
|
||||||
|
);
|
||||||
|
handle::Handle::notice_message(
|
||||||
|
"config_validate::yaml_syntax_error",
|
||||||
|
&error_msg,
|
||||||
|
);
|
||||||
|
return Ok(false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(err) => {
|
||||||
|
let error_msg = format!("无法读取目标配置文件: {}", err);
|
||||||
|
logging!(error, Type::Cmd, true, "{}", error_msg);
|
||||||
|
handle::Handle::notice_message(
|
||||||
|
"config_validate::file_read_error",
|
||||||
|
&error_msg,
|
||||||
|
);
|
||||||
|
return Ok(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 更新profiles配置
|
// 更新profiles配置
|
||||||
logging!(info, Type::Cmd, true, "正在更新配置草稿");
|
logging!(info, Type::Cmd, true, "正在更新配置草稿");
|
||||||
let _ = Config::profiles().draft().patch_config(profiles);
|
let _ = Config::profiles().draft().patch_config(profiles);
|
||||||
@ -92,7 +152,7 @@ pub async fn patch_profiles_config(profiles: IProfiles) -> CmdResult<bool> {
|
|||||||
Ok((true, _)) => {
|
Ok((true, _)) => {
|
||||||
logging!(info, Type::Cmd, true, "配置更新成功");
|
logging!(info, Type::Cmd, true, "配置更新成功");
|
||||||
handle::Handle::refresh_clash();
|
handle::Handle::refresh_clash();
|
||||||
let _ = tray::Tray::global().update_tooltip();
|
let _ = Tray::global().update_tooltip();
|
||||||
Config::profiles().apply();
|
Config::profiles().apply();
|
||||||
wrap_err!(Config::profiles().data().save_file())?;
|
wrap_err!(Config::profiles().data().save_file())?;
|
||||||
Ok(true)
|
Ok(true)
|
||||||
@ -139,6 +199,8 @@ pub async fn patch_profiles_config_by_profile_index(
|
|||||||
_app_handle: tauri::AppHandle,
|
_app_handle: tauri::AppHandle,
|
||||||
profile_index: String,
|
profile_index: String,
|
||||||
) -> CmdResult<bool> {
|
) -> CmdResult<bool> {
|
||||||
|
logging!(info, Type::Cmd, true, "切换配置到: {}", profile_index);
|
||||||
|
|
||||||
let profiles = IProfiles {
|
let profiles = IProfiles {
|
||||||
current: Some(profile_index),
|
current: Some(profile_index),
|
||||||
items: None,
|
items: None,
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
use super::{prfitem::PrfItem, PrfOption};
|
use super::{prfitem::PrfItem, PrfOption};
|
||||||
use crate::{
|
use crate::utils::{dirs, help};
|
||||||
logging,
|
|
||||||
utils::{dirs, help, logging::Type},
|
|
||||||
};
|
|
||||||
use anyhow::{bail, Context, Result};
|
use anyhow::{bail, Context, Result};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_yaml::{Mapping, Value};
|
use serde_yaml::Mapping;
|
||||||
use std::{fs, io::Write};
|
use std::{fs, io::Write};
|
||||||
|
|
||||||
/// Define the `profiles.yaml` schema
|
/// Define the `profiles.yaml` schema
|
||||||
#[derive(Default, Debug, Clone, Deserialize, Serialize)]
|
#[derive(Default, Debug, Clone, Deserialize, Serialize)]
|
||||||
pub struct IProfiles {
|
pub struct IProfiles {
|
||||||
@ -384,92 +382,16 @@ impl IProfiles {
|
|||||||
pub fn current_mapping(&self) -> Result<Mapping> {
|
pub fn current_mapping(&self) -> Result<Mapping> {
|
||||||
match (self.current.as_ref(), self.items.as_ref()) {
|
match (self.current.as_ref(), self.items.as_ref()) {
|
||||||
(Some(current), Some(items)) => {
|
(Some(current), Some(items)) => {
|
||||||
logging!(
|
|
||||||
info,
|
|
||||||
Type::Config,
|
|
||||||
true,
|
|
||||||
"开始获取当前配置文件 current_uid={}",
|
|
||||||
current
|
|
||||||
);
|
|
||||||
logging!(info, Type::Core, true, "服务可用,直接使用服务模式");
|
|
||||||
if let Some(item) = items.iter().find(|e| e.uid.as_ref() == Some(current)) {
|
if let Some(item) = items.iter().find(|e| e.uid.as_ref() == Some(current)) {
|
||||||
let file_path = match item.file.as_ref() {
|
let file_path = match item.file.as_ref() {
|
||||||
Some(file) => {
|
Some(file) => dirs::app_profiles_dir()?.join(file),
|
||||||
let path = dirs::app_profiles_dir()?.join(file);
|
None => bail!("failed to get the file field"),
|
||||||
logging!(
|
|
||||||
info,
|
|
||||||
Type::Config,
|
|
||||||
true,
|
|
||||||
"找到配置文件路径: {}",
|
|
||||||
path.display()
|
|
||||||
);
|
|
||||||
path
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
logging!(
|
|
||||||
error,
|
|
||||||
Type::Config,
|
|
||||||
true,
|
|
||||||
"配置项缺少file字段 uid={}",
|
|
||||||
current
|
|
||||||
);
|
|
||||||
bail!("failed to get the file field");
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
if !file_path.exists() {
|
return help::read_mapping(&file_path);
|
||||||
logging!(
|
|
||||||
error,
|
|
||||||
Type::Config,
|
|
||||||
true,
|
|
||||||
"配置文件不存在: {}",
|
|
||||||
file_path.display()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
match help::read_mapping(&file_path) {
|
|
||||||
Ok(mapping) => {
|
|
||||||
let key_count = mapping.len();
|
|
||||||
logging!(
|
|
||||||
info,
|
|
||||||
Type::Config,
|
|
||||||
true,
|
|
||||||
"成功读取配置文件, 包含{}个键值对",
|
|
||||||
key_count
|
|
||||||
);
|
|
||||||
// 打印主要的配置键
|
|
||||||
let important_keys = ["proxies", "proxy-groups", "rules"];
|
|
||||||
for key in important_keys.iter() {
|
|
||||||
if mapping.contains_key(&Value::from(*key)) {
|
|
||||||
logging!(info, Type::Config, true, "配置包含关键字段: {}", key);
|
|
||||||
} else {
|
|
||||||
logging!(warn, Type::Config, true, "配置缺少关键字段: {}", key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Ok(mapping);
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
logging!(error, Type::Config, true, "读取配置文件失败: {}", e);
|
|
||||||
// 将错误发送到前端显示
|
|
||||||
crate::core::handle::Handle::notice_message(
|
|
||||||
"config_validate::yaml_syntax_error",
|
|
||||||
&format!("{}", e),
|
|
||||||
);
|
|
||||||
return Err(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
logging!(
|
|
||||||
error,
|
|
||||||
Type::Config,
|
|
||||||
true,
|
|
||||||
"未找到当前配置项 uid={}",
|
|
||||||
current
|
|
||||||
);
|
|
||||||
bail!("failed to find the current profile \"uid:{current}\"");
|
bail!("failed to find the current profile \"uid:{current}\"");
|
||||||
}
|
}
|
||||||
_ => {
|
_ => Ok(Mapping::new()),
|
||||||
logging!(warn, Type::Config, true, "没有当前配置项,返回空配置");
|
|
||||||
Ok(Mapping::new())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,14 +1,24 @@
|
|||||||
use once_cell::sync::OnceCell;
|
use once_cell::sync::OnceCell;
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
use std::sync::Arc;
|
use std::{sync::Arc, time::Duration};
|
||||||
use tauri::{AppHandle, Emitter, Manager, WebviewWindow};
|
use tauri::{AppHandle, Emitter, Manager, WebviewWindow};
|
||||||
|
|
||||||
use crate::{logging_error, utils::logging::Type};
|
use crate::{logging, logging_error, utils::logging::Type};
|
||||||
|
|
||||||
|
/// 存储启动期间的错误消息
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct ErrorMessage {
|
||||||
|
status: String,
|
||||||
|
message: String,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone)]
|
#[derive(Debug, Default, Clone)]
|
||||||
pub struct Handle {
|
pub struct Handle {
|
||||||
pub app_handle: Arc<RwLock<Option<AppHandle>>>,
|
pub app_handle: Arc<RwLock<Option<AppHandle>>>,
|
||||||
pub is_exiting: Arc<RwLock<bool>>,
|
pub is_exiting: Arc<RwLock<bool>>,
|
||||||
|
/// 存储启动过程中产生的错误消息队列
|
||||||
|
startup_errors: Arc<RwLock<Vec<ErrorMessage>>>,
|
||||||
|
startup_completed: Arc<RwLock<bool>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Handle {
|
impl Handle {
|
||||||
@ -18,6 +28,8 @@ impl Handle {
|
|||||||
HANDLE.get_or_init(|| Handle {
|
HANDLE.get_or_init(|| Handle {
|
||||||
app_handle: Arc::new(RwLock::new(None)),
|
app_handle: Arc::new(RwLock::new(None)),
|
||||||
is_exiting: Arc::new(RwLock::new(false)),
|
is_exiting: Arc::new(RwLock::new(false)),
|
||||||
|
startup_errors: Arc::new(RwLock::new(Vec::new())),
|
||||||
|
startup_completed: Arc::new(RwLock::new(false)),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,16 +82,89 @@ impl Handle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 通知前端显示消息,如果在启动过程中,则将消息存入启动错误队列
|
||||||
pub fn notice_message<S: Into<String>, M: Into<String>>(status: S, msg: M) {
|
pub fn notice_message<S: Into<String>, M: Into<String>>(status: S, msg: M) {
|
||||||
if let Some(window) = Self::global().get_window() {
|
let handle = Self::global();
|
||||||
|
let status_str = status.into();
|
||||||
|
let msg_str = msg.into();
|
||||||
|
|
||||||
|
// 检查是否正在启动过程中
|
||||||
|
if !*handle.startup_completed.read() {
|
||||||
|
logging!(
|
||||||
|
info,
|
||||||
|
Type::Frontend,
|
||||||
|
true,
|
||||||
|
"启动过程中发现错误,加入消息队列: {} - {}",
|
||||||
|
status_str,
|
||||||
|
msg_str
|
||||||
|
);
|
||||||
|
|
||||||
|
// 将消息添加到启动错误队列
|
||||||
|
let mut errors = handle.startup_errors.write();
|
||||||
|
errors.push(ErrorMessage {
|
||||||
|
status: status_str,
|
||||||
|
message: msg_str,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 已经完成启动,直接发送消息
|
||||||
|
if let Some(window) = handle.get_window() {
|
||||||
logging_error!(
|
logging_error!(
|
||||||
Type::Frontend,
|
Type::Frontend,
|
||||||
true,
|
true,
|
||||||
window.emit("verge://notice-message", (status.into(), msg.into()))
|
window.emit("verge://notice-message", (status_str, msg_str))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 标记启动已完成,并发送所有启动阶段累积的错误消息
|
||||||
|
pub fn mark_startup_completed(&self) {
|
||||||
|
{
|
||||||
|
let mut completed = self.startup_completed.write();
|
||||||
|
*completed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.send_startup_errors();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 发送启动时累积的所有错误消息
|
||||||
|
fn send_startup_errors(&self) {
|
||||||
|
let errors = {
|
||||||
|
let mut errors = self.startup_errors.write();
|
||||||
|
std::mem::take(&mut *errors)
|
||||||
|
};
|
||||||
|
|
||||||
|
if errors.is_empty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
logging!(
|
||||||
|
info,
|
||||||
|
Type::Frontend,
|
||||||
|
true,
|
||||||
|
"发送{}条启动时累积的错误消息",
|
||||||
|
errors.len()
|
||||||
|
);
|
||||||
|
|
||||||
|
// 等待2秒以确保前端已完全加载,延迟发送错误通知
|
||||||
|
if let Some(window) = self.get_window() {
|
||||||
|
let window_clone = window.clone();
|
||||||
|
let errors_clone = errors.clone();
|
||||||
|
|
||||||
|
tauri::async_runtime::spawn(async move {
|
||||||
|
tokio::time::sleep(Duration::from_secs(2)).await;
|
||||||
|
|
||||||
|
for error in errors_clone {
|
||||||
|
let _ =
|
||||||
|
window_clone.emit("verge://notice-message", (error.status, error.message));
|
||||||
|
// 每条消息之间间隔500ms,避免消息堆积
|
||||||
|
tokio::time::sleep(Duration::from_millis(500)).await;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_is_exiting(&self) {
|
pub fn set_is_exiting(&self) {
|
||||||
let mut is_exiting = self.is_exiting.write();
|
let mut is_exiting = self.is_exiting.write();
|
||||||
*is_exiting = true;
|
*is_exiting = true;
|
||||||
|
@ -82,3 +82,11 @@ pub async fn update_profile(uid: String, option: Option<PrfOption>) -> Result<()
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 增强配置
|
||||||
|
pub async fn enhance_profiles() -> Result<()> {
|
||||||
|
crate::core::CoreManager::global()
|
||||||
|
.update_config()
|
||||||
|
.await
|
||||||
|
.map(|_| ())
|
||||||
|
}
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
use crate::enhance::seq::SeqMap;
|
use crate::enhance::seq::SeqMap;
|
||||||
|
use crate::logging;
|
||||||
|
use crate::utils::logging::Type;
|
||||||
use anyhow::{anyhow, bail, Context, Result};
|
use anyhow::{anyhow, bail, Context, Result};
|
||||||
use nanoid::nanoid;
|
use nanoid::nanoid;
|
||||||
use serde::{de::DeserializeOwned, Serialize};
|
use serde::{de::DeserializeOwned, Serialize};
|
||||||
use serde_yaml::{Mapping, Value};
|
use serde_yaml::Mapping;
|
||||||
use std::{fs, path::PathBuf, str::FromStr};
|
use std::{fs, path::PathBuf, str::FromStr};
|
||||||
|
|
||||||
/// read data from yaml as struct T
|
/// read data from yaml as struct T
|
||||||
@ -22,9 +24,18 @@ pub fn read_yaml<T: DeserializeOwned>(path: &PathBuf) -> Result<T> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// read mapping from yaml fix #165
|
/// read mapping from yaml
|
||||||
pub fn read_mapping(path: &PathBuf) -> Result<Mapping> {
|
pub fn read_mapping(path: &PathBuf) -> Result<Mapping> {
|
||||||
let mut val: Value = read_yaml(path)?;
|
if !path.exists() {
|
||||||
|
bail!("file not found \"{}\"", path.display());
|
||||||
|
}
|
||||||
|
|
||||||
|
let yaml_str = fs::read_to_string(path)
|
||||||
|
.with_context(|| format!("failed to read the file \"{}\"", path.display()))?;
|
||||||
|
|
||||||
|
// YAML语法检查
|
||||||
|
match serde_yaml::from_str::<serde_yaml::Value>(&yaml_str) {
|
||||||
|
Ok(mut val) => {
|
||||||
val.apply_merge()
|
val.apply_merge()
|
||||||
.with_context(|| format!("failed to apply merge \"{}\"", path.display()))?;
|
.with_context(|| format!("failed to apply merge \"{}\"", path.display()))?;
|
||||||
|
|
||||||
@ -35,6 +46,19 @@ pub fn read_mapping(path: &PathBuf) -> Result<Mapping> {
|
|||||||
path.display()
|
path.display()
|
||||||
))?
|
))?
|
||||||
.to_owned())
|
.to_owned())
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
let error_msg = format!("YAML syntax error in {}: {}", path.display(), err);
|
||||||
|
logging!(error, Type::Config, true, "{}", error_msg);
|
||||||
|
|
||||||
|
crate::core::handle::Handle::notice_message(
|
||||||
|
"config_validate::yaml_syntax_error",
|
||||||
|
&error_msg,
|
||||||
|
);
|
||||||
|
|
||||||
|
bail!("YAML syntax error: {}", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// read mapping from yaml fix #165
|
/// read mapping from yaml fix #165
|
||||||
|
@ -239,6 +239,24 @@ pub fn create_window() {
|
|||||||
|
|
||||||
// 设置窗口状态监控,实时保存窗口位置和大小
|
// 设置窗口状态监控,实时保存窗口位置和大小
|
||||||
crate::feat::setup_window_state_monitor(&app_handle);
|
crate::feat::setup_window_state_monitor(&app_handle);
|
||||||
|
|
||||||
|
// 标记前端UI已准备就绪,向前端发送启动完成事件
|
||||||
|
let app_handle_clone = app_handle.clone();
|
||||||
|
tauri::async_runtime::spawn(async move {
|
||||||
|
use tauri::Emitter;
|
||||||
|
|
||||||
|
logging!(
|
||||||
|
info,
|
||||||
|
Type::Window,
|
||||||
|
true,
|
||||||
|
"标记前端UI已准备就绪,开始处理启动错误队列"
|
||||||
|
);
|
||||||
|
handle::Handle::global().mark_startup_completed();
|
||||||
|
|
||||||
|
if let Some(window) = app_handle_clone.get_webview_window("main") {
|
||||||
|
let _ = window.emit("verge://startup-completed", ());
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
logging!(
|
logging!(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user