Projekt-Identität und -Config neu: .ai-control/config.json mit UUID und Name

- 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).
This commit is contained in:
marcus hinz
2026-07-20 10:55:21 +02:00
parent 6d44f77671
commit b9d006184a
20 changed files with 830 additions and 346 deletions
+73 -69
View File
@@ -4,7 +4,7 @@
use crate::domain::paths::Paths;
use crate::domain::pool::provision_pools_for_panel;
use crate::domain::project::terminal_config;
use crate::domain::project::project_config;
use crate::domain::watcher::spawn_session_watcher;
use crate::platform::Anchor;
use crate::{commands, terminal};
@@ -118,6 +118,10 @@ pub(crate) fn main_builder() -> tauri::Builder<tauri::Wry> {
#[cfg(target_os = "macos")]
app.set_activation_policy(tauri::ActivationPolicy::Accessory);
// Alt-Layout migrieren: ai-control.json → .ai-control/config.json,
// Pool-Zuordnung in die Registry, Icons in den Projekt-Config-Ordner.
crate::domain::project::migrate_layout_in(&Paths::real())?;
// Session-Watcher: synct bei Session-Ende (Prozess verschwindet).
spawn_session_watcher();
@@ -232,44 +236,72 @@ pub(crate) fn main_builder() -> tauri::Builder<tauri::Wry> {
}
}
})
.invoke_handler(tauri::generate_handler![
commands::list_projects,
commands::create_project_full,
commands::add_project,
commands::remove_project,
commands::delete_project,
commands::project_work_dirs,
commands::set_project_dir,
commands::add_work_dir,
commands::remove_work_dir,
commands::list_pools,
commands::create_oauth_pool,
commands::create_apikey_pool,
commands::rename_pool,
commands::delete_pool,
commands::assign_pool,
commands::unassign_pool,
commands::set_terminal_config,
commands::project_icon,
commands::pool_label,
commands::todo_state,
commands::set_todo,
commands::usage_stats,
commands::stop_project,
commands::restart_project,
commands::start_or_focus_cmd,
commands::open_main_window,
commands::quit_app,
commands::sync_setting,
commands::set_sync_setting,
commands::terminal_font_size,
commands::set_terminal_font_size,
commands::link_pool_runtime,
commands::oauth_login,
commands::keychain_status,
commands::set_apikey,
terminal::open_terminal
])
.invoke_handler(invoke_handlers())
}
/// Eine Kommando-Liste für beide Prozesse (Hauptfenster und Terminal).
/// Zwei getrennte Listen hatten den Settings-Dialog gebrochen, weil die
/// Archiv-Kommandos nur im Terminal-Prozess registriert waren.
fn invoke_handlers() -> impl Fn(tauri::ipc::Invoke<tauri::Wry>) -> bool + Send + Sync + 'static {
tauri::generate_handler![
commands::list_projects,
commands::create_project_full,
commands::add_project,
commands::remove_project,
commands::delete_project,
commands::project_work_dirs,
commands::set_project_dir,
commands::add_work_dir,
commands::remove_work_dir,
commands::list_pools,
commands::create_oauth_pool,
commands::create_apikey_pool,
commands::rename_pool,
commands::delete_pool,
commands::assign_pool,
commands::unassign_pool,
commands::set_terminal_config,
commands::project_icon,
commands::pool_label,
commands::todo_state,
commands::set_todo,
commands::usage_stats,
commands::stop_project,
commands::restart_project,
commands::start_or_focus_cmd,
commands::open_main_window,
commands::quit_app,
commands::sync_setting,
commands::set_sync_setting,
commands::terminal_font_size,
commands::set_terminal_font_size,
commands::link_pool_runtime,
commands::oauth_login,
commands::keychain_status,
commands::set_apikey,
commands::panel_archive_dir_cmd,
commands::panel_archive_cmd,
commands::set_archive_home_cmd,
commands::clear_archive_home_cmd,
commands::archive_docs_cmd,
commands::reveal_path_cmd,
commands::spellcheck_lang,
terminal::open_terminal,
terminal::term_start,
terminal::term_log,
terminal::term_write,
terminal::term_resize,
terminal::panel_read,
terminal::commands_read,
terminal::commands_delete,
terminal::panel_set,
terminal::search_read,
terminal::search_run,
terminal::panel_load,
terminal::wiki_read,
terminal::wiki_open,
terminal::open_panel_window
]
}
/// Terminal-Prozess: ein Fenster mit eigener PTY; Activation-Policy bleibt
@@ -280,7 +312,7 @@ pub(crate) fn terminal_builder(project: String) -> tauri::Builder<tauri::Wry> {
.plugin(tauri_plugin_clipboard_manager::init())
.manage(terminal::Terminals::default())
.setup(move |app| {
let cfg = terminal_config(&project)?;
let cfg = project_config(&project)?;
terminal::build_window(app.handle(), &project, &cfg)?;
Ok(())
})
@@ -297,33 +329,5 @@ pub(crate) fn terminal_builder(project: String) -> tauri::Builder<tauri::Wry> {
}
}
})
.invoke_handler(tauri::generate_handler![
terminal::term_start,
terminal::term_log,
terminal::term_write,
terminal::term_resize,
terminal::panel_read,
terminal::commands_read,
terminal::commands_delete,
terminal::panel_set,
terminal::search_read,
terminal::search_run,
terminal::panel_load,
terminal::wiki_read,
terminal::wiki_open,
terminal::open_panel_window,
commands::panel_archive_dir_cmd,
commands::panel_archive_cmd,
commands::set_archive_home_cmd,
commands::clear_archive_home_cmd,
commands::archive_docs_cmd,
commands::reveal_path_cmd,
// Header im Terminal-Prozess: Projektliste, Icon und Pool-Name.
commands::list_projects,
commands::project_icon,
commands::pool_label,
commands::terminal_font_size,
commands::set_terminal_font_size,
commands::spellcheck_lang
])
.invoke_handler(invoke_handlers())
}