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
+23 -8
View File
@@ -6,7 +6,9 @@ use std::sync::Mutex;
use tauri::{AppHandle, Emitter, Manager, State, WebviewUrl, WebviewWindowBuilder};
use crate::domain::paths::{commands_file, panel_file, search_file, wiki_file, Paths};
use crate::domain::project::{project_pool_dir, terminal_config, TerminalConfig};
use crate::domain::project::{
project_config, project_pool_dir, verify_project_dir_in, ProjectConfig,
};
use crate::domain::registry::project_dir;
use crate::domain::settings::claude_command;
@@ -27,7 +29,10 @@ pub struct Terminals(pub Mutex<HashMap<String, Session>>);
/// das Terminal-Fenster hinter der aktiven App.
#[tauri::command]
pub fn open_terminal(app: AppHandle, project: String) -> Result<(), String> {
let dir = project_dir(&Paths::real(), &project)?;
// Prüft zugleich die Projekt-Identität: die config.json im registrierten
// Ordner muss diese Projekt-ID tragen (verschobene/ersetzte Ordner fallen
// hier auf, statt eine fremde Session zu starten).
let dir = verify_project_dir_in(&Paths::real(), &project)?;
if !dir.is_dir() {
return Err(format!("Projektordner fehlt: {}", dir.display()));
}
@@ -61,10 +66,15 @@ fn theme_background(theme: &str) -> (u8, u8, u8) {
pub fn build_window(
app: &AppHandle,
project: &str,
cfg: &TerminalConfig,
cfg: &ProjectConfig,
) -> tauri::Result<()> {
let (r, g, b) = theme_background(cfg.theme.as_deref().unwrap_or_default());
let title = cfg.title.as_deref().unwrap_or(project);
let (r, g, b) = theme_background(cfg.terminal.theme.as_deref().unwrap_or_default());
let title = cfg
.terminal
.title
.as_deref()
.or(cfg.name.as_deref())
.unwrap_or(project);
let builder = WebviewWindowBuilder::new(
app,
format!("term-{project}"),
@@ -369,9 +379,14 @@ pub async fn open_panel_window(app: AppHandle, project: String) -> Result<(), St
let _ = w.set_focus();
return Ok(());
}
let cfg = terminal_config(&project)?;
let (r, g, b) = theme_background(cfg.theme.as_deref().unwrap_or_default());
let title = cfg.title.as_deref().unwrap_or(project.as_str());
let cfg = project_config(&project)?;
let (r, g, b) = theme_background(cfg.terminal.theme.as_deref().unwrap_or_default());
let title = cfg
.terminal
.title
.as_deref()
.or(cfg.name.as_deref())
.unwrap_or(project.as_str());
let builder = WebviewWindowBuilder::new(
&app,
&label,