- 新增用户版 CONVENTIONS 存储层(app_data_dir/conventions/CONVENTIONS.md) - OnceLock 初始化目录,effective_conventions_content() 用户版优先 fallback 内置 - invalidate_effective_cache() 写入/重置后清除缓存 - 新增三个 Tauri command - get_user_conventions_status:返回来源(builtin/user)、版本、内容 - save_user_conventions:解析 frontmatter version,写入用户版,自动同步所有项目 - reset_conventions_to_builtin:删除用户版,自动同步回内置版 - sync_blueprint_rules / build_init_prompt / build_sync_prompt / generate_muhe_prompt 全部切换为 effective_conventions_content(),规则更新无需发版 - GovernancePanel 新增规则编辑器 UI - 来源 badge(内置基线 / 用户自定义) - 「应用梦核建议」区域:粘贴新内容 + 确认写入 + 同步进度提示 - 「重置为基线」按钮(仅用户版存在时显示)
278 lines
10 KiB
Rust
278 lines
10 KiB
Rust
mod commands;
|
||
mod db;
|
||
mod mcp_inject;
|
||
mod mcp_server;
|
||
mod models;
|
||
use git2;
|
||
|
||
/// 创建不弹 CMD 窗口的子进程(Windows 专用 CREATE_NO_WINDOW 标志)。
|
||
/// 所有后台静默命令都应通过此函数创建,避免在 Windows 上频繁闪出黑框。
|
||
pub(crate) fn silent_cmd(program: &str) -> std::process::Command {
|
||
let mut cmd = std::process::Command::new(program);
|
||
#[cfg(target_os = "windows")]
|
||
{
|
||
use std::os::windows::process::CommandExt;
|
||
const CREATE_NO_WINDOW: u32 = 0x0800_0000;
|
||
cmd.creation_flags(CREATE_NO_WINDOW);
|
||
}
|
||
cmd
|
||
}
|
||
|
||
use commands::{activity::*, blueprint::*, buff::{apply_blueprint_buff, remove_blueprint_buff, get_buff_status, list_buffed_projects}, canvas::*, cicd::*, devtools::*, git_ops::*, git_repos::*, github::*, groups::*, mentor::{add_project_note, get_group_mentor_report, get_mentor_context, get_project_notes, get_startup_alerts, run_startup_health_scan}, network::{get_wsl_ip, sync_wsl_proxy, get_wsl_proxy_status, clear_wsl_proxy, test_wsl_proxy_ip, test_win_proxy_ip}, project_scan::*, project_tools::*, projects::{batch_create_projects, create_project, delete_project, get_project, get_projects, scan_subfolders, update_project, update_project_tags}, proxy::*, publish::*, settings::{check_mcp_health, detect_editors, get_settings, run_health_check, search_editor_path, update_settings}, shell::*, spaces::*, tags::*, templates::*};
|
||
|
||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||
pub fn run() {
|
||
tauri::Builder::default()
|
||
.plugin(tauri_plugin_single_instance::init(|app, _argv, _cwd| {
|
||
// 第二个实例启动时,激活已有窗口
|
||
use tauri::Manager;
|
||
if let Some(w) = app.get_webview_window("main") {
|
||
let _ = w.show();
|
||
let _ = w.unminimize();
|
||
let _ = w.set_focus();
|
||
}
|
||
}))
|
||
.plugin(tauri_plugin_http::init())
|
||
.plugin(tauri_plugin_opener::init())
|
||
.plugin(tauri_plugin_updater::Builder::new().build())
|
||
.plugin(tauri_plugin_process::init())
|
||
.plugin(tauri_plugin_dialog::init())
|
||
.plugin(tauri_plugin_shell::init())
|
||
.setup(|app| {
|
||
use tauri::{
|
||
menu::{Menu, MenuItem},
|
||
tray::{MouseButton, MouseButtonState, TrayIconBuilder, TrayIconEvent},
|
||
Manager,
|
||
};
|
||
|
||
let data_dir = app.path().app_data_dir().expect("no app data dir");
|
||
db::init(data_dir.clone());
|
||
commands::blueprint::init_user_conventions_dir(data_dir);
|
||
// 禁用 libgit2 目录所有者验证,允许访问 WSL UNC 路径(\\wsl.localhost\...)
|
||
unsafe {
|
||
let _ = git2::opts::set_verify_owner_validation(false);
|
||
}
|
||
// 种植内置模板(首次启动时执行)
|
||
commands::templates::seed_builtin_templates();
|
||
|
||
// ── 启动 MCP Server ───────────────────────────────────────
|
||
let mcp_port = mcp_server::read_port();
|
||
tauri::async_runtime::spawn(mcp_server::start(mcp_port));
|
||
|
||
// ── 启动健康扫描(异步,不阻塞窗口渲染)────────────────
|
||
tauri::async_runtime::spawn(async {
|
||
tokio::task::spawn_blocking(run_startup_health_scan).await.ok();
|
||
});
|
||
|
||
// ── 启动蓝图 Buff 后台轮询 ────────────────────────────
|
||
commands::buff::start_blueprint_watchers(app.handle().clone());
|
||
|
||
// ── 系统托盘 ──────────────────────────────────────────────
|
||
let show_i = MenuItem::with_id(app, "show", "显示窗口", true, None::<&str>)?;
|
||
let quit_i = MenuItem::with_id(app, "quit", "退出炼境", true, None::<&str>)?;
|
||
let menu = Menu::with_items(app, &[&show_i, &quit_i])?;
|
||
|
||
TrayIconBuilder::new()
|
||
.icon(app.default_window_icon().unwrap().clone())
|
||
.menu(&menu)
|
||
.show_menu_on_left_click(false)
|
||
.tooltip("炼境")
|
||
.on_menu_event(|app, event| match event.id.as_ref() {
|
||
"quit" => app.exit(0),
|
||
"show" => {
|
||
if let Some(w) = app.get_webview_window("main") {
|
||
let _ = w.show();
|
||
let _ = w.set_focus();
|
||
}
|
||
}
|
||
_ => {}
|
||
})
|
||
.on_tray_icon_event(|tray, event| {
|
||
// 左键单击托盘图标 → 显示/聚焦窗口
|
||
if let TrayIconEvent::Click {
|
||
button: MouseButton::Left,
|
||
button_state: MouseButtonState::Up,
|
||
..
|
||
} = event
|
||
{
|
||
let app = tray.app_handle();
|
||
if let Some(w) = app.get_webview_window("main") {
|
||
let _ = w.show();
|
||
let _ = w.set_focus();
|
||
}
|
||
}
|
||
})
|
||
.build(app)?;
|
||
|
||
// ── 关闭窗口 → 最小化到托盘 ──────────────────────────────
|
||
if let Some(window) = app.get_webview_window("main") {
|
||
let win = window.clone();
|
||
window.on_window_event(move |event| {
|
||
if let tauri::WindowEvent::CloseRequested { api, .. } = event {
|
||
api.prevent_close();
|
||
let _ = win.hide();
|
||
}
|
||
});
|
||
}
|
||
|
||
Ok(())
|
||
})
|
||
.invoke_handler(tauri::generate_handler![
|
||
// projects
|
||
get_projects,
|
||
get_project,
|
||
create_project,
|
||
update_project,
|
||
update_project_tags,
|
||
delete_project,
|
||
scan_subfolders,
|
||
batch_create_projects,
|
||
// activity
|
||
get_activity,
|
||
add_activity,
|
||
// groups
|
||
get_groups,
|
||
create_group,
|
||
update_group,
|
||
delete_group,
|
||
reorder_groups,
|
||
get_group_maps,
|
||
add_group_member,
|
||
remove_group_member,
|
||
// settings
|
||
get_settings,
|
||
update_settings,
|
||
detect_editors,
|
||
search_editor_path,
|
||
check_mcp_health,
|
||
run_health_check,
|
||
// project tools
|
||
get_project_tools,
|
||
add_project_tool,
|
||
update_project_tool,
|
||
remove_project_tool,
|
||
launch_tool,
|
||
// global quick launch
|
||
get_global_tools,
|
||
add_global_tool,
|
||
update_global_tool,
|
||
remove_global_tool,
|
||
// devtools
|
||
scan_dev_tools,
|
||
scan_wsl_dev_tools,
|
||
get_dev_tools,
|
||
save_dev_tools,
|
||
delete_dev_tool,
|
||
upsert_dev_tool,
|
||
// wsl proxy
|
||
get_wsl_ip,
|
||
sync_wsl_proxy,
|
||
get_wsl_proxy_status,
|
||
clear_wsl_proxy,
|
||
test_wsl_proxy_ip,
|
||
test_win_proxy_ip,
|
||
// v2rayn import
|
||
scan_v2rayn_nodes,
|
||
detect_v2rayn_db_path,
|
||
get_active_v2rayn_node,
|
||
// project env scan
|
||
scan_project_env,
|
||
get_project_env_tools,
|
||
save_project_env_tools,
|
||
// shell
|
||
open_editor,
|
||
open_terminal,
|
||
// git ops
|
||
git_clone,
|
||
git_status,
|
||
git_fetch,
|
||
git_pull,
|
||
git_push,
|
||
git_head_commit,
|
||
git_upstream_behind,
|
||
git_create_branch,
|
||
git_list_branches,
|
||
git_checkout_branch,
|
||
// github
|
||
github_save_account,
|
||
github_get_account,
|
||
github_get_token,
|
||
github_get_client_id,
|
||
github_logout,
|
||
github_start_oauth,
|
||
github_poll_oauth,
|
||
github_open_login_window,
|
||
// spaces
|
||
get_spaces,
|
||
create_space,
|
||
update_space,
|
||
delete_space,
|
||
set_current_space,
|
||
get_deploy_commands,
|
||
add_deploy_command,
|
||
remove_deploy_command,
|
||
execute_deploy,
|
||
// git repos registry
|
||
get_git_repos,
|
||
import_git_repos,
|
||
update_git_repo_deploy,
|
||
mount_repo,
|
||
delete_git_repo,
|
||
// publish to github
|
||
scan_local_git,
|
||
github_create_repo,
|
||
git_init_and_commit,
|
||
git_remote_set,
|
||
git_push_to_github,
|
||
register_and_link_repo,
|
||
write_gitignore,
|
||
resolve_wsl_path,
|
||
get_wsl_distros,
|
||
// project templates
|
||
list_templates,
|
||
get_template,
|
||
save_template,
|
||
delete_template,
|
||
create_project_from_template,
|
||
// tags
|
||
get_tags,
|
||
create_tag,
|
||
update_tag,
|
||
delete_tag,
|
||
get_project_tags,
|
||
set_project_tags,
|
||
get_all_project_tag_maps,
|
||
// cicd
|
||
get_cicd_config,
|
||
save_cicd_config,
|
||
generate_workflow,
|
||
// blueprint
|
||
get_blueprint,
|
||
generate_blueprint_prompt,
|
||
get_blueprint_status,
|
||
sync_blueprint_rules,
|
||
get_flywheel_stats,
|
||
generate_muhe_prompt,
|
||
archive_project_docs,
|
||
get_user_conventions_status,
|
||
save_user_conventions,
|
||
reset_conventions_to_builtin,
|
||
// canvas state
|
||
get_canvas_state,
|
||
save_canvas_state,
|
||
// blueprint buff
|
||
apply_blueprint_buff,
|
||
remove_blueprint_buff,
|
||
get_buff_status,
|
||
list_buffed_projects,
|
||
// mentor
|
||
get_mentor_context,
|
||
get_project_notes,
|
||
add_project_note,
|
||
get_group_mentor_report,
|
||
get_startup_alerts,
|
||
])
|
||
.run(tauri::generate_context!())
|
||
.expect("error while running tauri application");
|
||
}
|