b9d006184a
- Projekt-Config zieht von ai-control.json nach .ai-control/config.json um, das Icon liegt daneben und synct mit dem Projekt. - Jedes Projekt trägt eine UUID (id) und den Anzeigenamen (name) in der Config; die UUID ist Registry-Schlüssel und project-Parameter aller Commands, der Name reine Darstellung. - Pool-Zuordnung ist maschinenlokal und liegt in der projects.json-Registry, nicht mehr in der syncbaren Projekt-Config. - open_terminal prüft die ID im Ordner gegen die Registry (verschobene oder ersetzte Ordner scheitern laut). - Import übernimmt eine mitgebrachte ID, legt Arbeitsordner und Archiv-Home an und zieht die Archiv-Permission nach. - Migration beim App-Start: Alt-Datei umziehen, Pool in die Registry, Icons aus ~/.config/ai-control/icons ins Projekt, Registry auf UUIDs. - ProjectConfig reicht unbekannte Keys per serde(flatten) durch den Round-Trip (Regressionstest config_roundtrip_erhaelt_fremde_keys).
109 lines
4.1 KiB
Rust
109 lines
4.1 KiB
Rust
//! Einstieg: Argument-Dispatch auf die drei Prozessrollen eines Binaries —
|
|
//! Haupt-App (Tray), Terminal-Prozess (`--terminal <projekt>`) und
|
|
//! MCP-stdio-Server (`--mcp-panel`). Fachlogik liegt in domain/, OS-Aufrufe
|
|
//! in platform/, Tauri-Verdrahtung in app.rs/commands.rs.
|
|
|
|
mod app;
|
|
mod commands;
|
|
mod domain;
|
|
mod mcp;
|
|
mod platform;
|
|
mod terminal;
|
|
|
|
/// Panic-Tracer: erzwingt bei jedem Panic einen vollständigen Backtrace
|
|
/// (unabhängig von RUST_BACKTRACE), gibt ihn klar abgegrenzt auf stderr aus und
|
|
/// schreibt ihn zusätzlich in eine kopierbare Datei (Pfad pro Prozess, damit
|
|
/// Haupt- und Terminal-Prozesse sich nicht überschreiben).
|
|
fn install_panic_tracer() {
|
|
std::panic::set_hook(Box::new(|info| {
|
|
let bt = std::backtrace::Backtrace::force_capture();
|
|
let dump = format!(
|
|
"\n========== ai-control PANIC (pid {}) ==========\n{info}\n\n{bt}\n========== END ==========\n",
|
|
std::process::id()
|
|
);
|
|
eprintln!("{dump}");
|
|
let path =
|
|
std::env::temp_dir().join(format!("ai-control-panic-{}.log", std::process::id()));
|
|
if std::fs::write(&path, &dump).is_ok() {
|
|
eprintln!("Backtrace kopierbar in: {}", path.display());
|
|
}
|
|
}));
|
|
}
|
|
|
|
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
|
pub fn run() {
|
|
install_panic_tracer();
|
|
|
|
let mut args = std::env::args().skip(1);
|
|
let first = args.next();
|
|
|
|
// `app --mcp-panel`: reiner MCP-stdio-Server (write_panel), kein Tauri/GTK.
|
|
// Von claude als Tool-Server gestartet.
|
|
if first.as_deref() == Some("--mcp-panel") {
|
|
mcp::run_mcp_panel();
|
|
return;
|
|
}
|
|
|
|
// AppImage ist installationsfrei und kann die GNOME-Popup-Extension nicht
|
|
// ausrollen/aktivieren. Unter GNOME daher auf das deb-/rpm-Paket verweisen und
|
|
// beenden. KDE/XFCE zeigen das Tray ohne Extension — dort läuft die AppImage.
|
|
#[cfg(target_os = "linux")]
|
|
if std::env::var_os("APPIMAGE").is_some() && platform::is_gnome() {
|
|
rfd::MessageDialog::new()
|
|
.set_level(rfd::MessageLevel::Warning)
|
|
.set_title("ai-control")
|
|
.set_description(
|
|
"Die AppImage-Fassung wird unter GNOME nicht unterstützt: Das Tray-Icon \
|
|
braucht eine GNOME-Shell-Extension, die nur das deb-/rpm-Paket \
|
|
installiert. Bitte das deb- bzw. rpm-Paket verwenden.",
|
|
)
|
|
.set_buttons(rfd::MessageButtons::Ok)
|
|
.show();
|
|
std::process::exit(1);
|
|
}
|
|
|
|
// generate_context! darf pro Crate nur einmal expandieren (_EMBED_INFO_PLIST).
|
|
let context = tauri::generate_context!();
|
|
match first.as_deref() {
|
|
// `app --terminal <projekt>`: eigener Prozess pro Terminal-Fenster,
|
|
// damit jedes Terminal ein eigenes Dock-Icon bekommt.
|
|
Some("--terminal") => {
|
|
let project = args.next().expect("--terminal braucht einen Projektnamen");
|
|
// Eigene Wayland-app_id pro Terminal-Prozess -> eigener Dock-Eintrag
|
|
// (muss vor dem GTK-Init stehen; No-op außerhalb Linux).
|
|
platform::set_app_id(&format!("aicontrol-{project}"));
|
|
let icon = domain::project::project_config(&project)
|
|
.expect("Projekt-Config nicht lesbar")
|
|
.terminal
|
|
.icon
|
|
.map(|i| {
|
|
domain::project::resolve_icon_path(&domain::paths::Paths::real(), &project, &i)
|
|
.expect("Projekt nicht registriert")
|
|
.to_string_lossy()
|
|
.into_owned()
|
|
});
|
|
app::terminal_builder(project)
|
|
.build(context)
|
|
.expect("error while building tauri application")
|
|
// Das Dock-Icon erst nach dem App-Start setzen: in setup() gesetzt
|
|
// überschreibt macOS es beim Anlegen des Dock-Tiles wieder.
|
|
.run(move |app, event| {
|
|
if let tauri::RunEvent::Ready = event {
|
|
if let Some(icon) = icon.as_deref() {
|
|
platform::set_dock_icon(icon);
|
|
}
|
|
platform::activate_self(app);
|
|
}
|
|
});
|
|
}
|
|
_ => {
|
|
// Feste Wayland-app_id fürs Hauptfenster (vor GTK-Init) — GNOME ordnet
|
|
// dem offenen Fenster über ai-control.desktop das App-Icon zu.
|
|
platform::set_app_id("ai-control");
|
|
app::main_builder()
|
|
.run(context)
|
|
.expect("error while running tauri application");
|
|
}
|
|
}
|
|
}
|