Panel: Clobber-Schutz, Content-Editor, Archiv-Reveal/Guard, Rechtschreib-Sprache
- register_mcp_server schreibt .claude.json nur bei fehlendem/abweichendem Eintrag (claudes Live-State nicht unnötig ueberschreiben). - Content-Editor im Panel (Rohtext-Textarea): Cmd/Ctrl+Enter speichert via panel_set, Esc verwirft; eingehende Updates werden waehrend der Bearbeitung gepuffert (Button "geaendert"), Speichern gewinnt. - Archivieren zeigt die Datei danach im Dateimanager (reveal_path_cmd, cfg pro OS). Breadth-Guard: Archiv-Ordner darf kein Home-Vorfahre sein, muss absolut sein; expand_home loest bare ~ auf. - Rechtschreibpruefung: Default in settings.json (spellcheckLang "de"), pro Text per Selector ueberschreibbar.
This commit is contained in:
+68
-14
@@ -88,6 +88,17 @@ pub(crate) fn project_archive_dir(project: &str) -> Option<PathBuf> {
|
||||
pub(crate) fn set_project_archive_dir(project: &str, dir: &str) -> Result<(), String> {
|
||||
let paths = Paths::real();
|
||||
let expanded = expand_home(&paths, dir);
|
||||
if !expanded.is_absolute() {
|
||||
return Err(format!("Archiv-Ordner muss ein absoluter Pfad sein: {}", expanded.display()));
|
||||
}
|
||||
// Zu weit gefasst? Ein Vorfahre des Home (inkl. Home selbst und Root) würde
|
||||
// claude über additionalDirectories weiten Zugriff geben — das lehnen wir ab.
|
||||
if paths.home.starts_with(&expanded) {
|
||||
return Err(format!(
|
||||
"Archiv-Ordner zu weit gefasst: {}. Bitte einen spezifischen Unterordner wählen.",
|
||||
expanded.display()
|
||||
));
|
||||
}
|
||||
fs::create_dir_all(&expanded).map_err(|e| format!("{}: {e}", expanded.display()))?;
|
||||
let contracted = contract_home(&paths, &expanded);
|
||||
let mut cfg = read_project_config_in(&paths, project)?;
|
||||
@@ -638,8 +649,11 @@ fn set_todo_in(paths: &Paths, name: &str, enabled: bool) -> Result<(), String> {
|
||||
fs::write(&sp, raw + "\n").map_err(|e| format!("{}: {e}", sp.display()))
|
||||
}
|
||||
|
||||
/// "~/x" relativ zum Home auflösen; alles andere unverändert.
|
||||
/// "~" bzw. "~/x" relativ zum Home auflösen; alles andere unverändert.
|
||||
fn expand_home(paths: &Paths, p: &str) -> PathBuf {
|
||||
if p == "~" {
|
||||
return paths.home.clone();
|
||||
}
|
||||
match p.strip_prefix("~/") {
|
||||
Some(rest) => paths.home.join(rest),
|
||||
None => PathBuf::from(p),
|
||||
@@ -1460,6 +1474,14 @@ fn register_mcp_server(pool_dir: &std::path::Path) {
|
||||
let Ok(exe) = std::env::current_exe() else {
|
||||
return;
|
||||
};
|
||||
// Tool nicht deferren, sonst sieht das Modell write_panel nicht und schreibt
|
||||
// den Entwurf in den Chat statt ins Panel.
|
||||
let desired = serde_json::json!({
|
||||
"type": "stdio",
|
||||
"command": exe.to_string_lossy(),
|
||||
"args": ["--mcp-panel"],
|
||||
"alwaysLoad": true,
|
||||
});
|
||||
let cfg = pool_dir.join(".claude.json");
|
||||
let mut v = fs::read_to_string(&cfg)
|
||||
.ok()
|
||||
@@ -1473,18 +1495,13 @@ fn register_mcp_server(pool_dir: &std::path::Path) {
|
||||
else {
|
||||
return;
|
||||
};
|
||||
// Schon korrekt und kein Altkey -> nichts schreiben. `.claude.json` ist
|
||||
// claudes Live-State; wir fassen sie nur an, wenn der Eintrag fehlt/abweicht.
|
||||
if servers.get("aicontrol").is_none() && servers.get(PANEL_MCP_SERVER) == Some(&desired) {
|
||||
return;
|
||||
}
|
||||
servers.remove("aicontrol"); // alter Key vor Umbenennung
|
||||
servers.insert(
|
||||
PANEL_MCP_SERVER.into(),
|
||||
serde_json::json!({
|
||||
"type": "stdio",
|
||||
"command": exe.to_string_lossy(),
|
||||
"args": ["--mcp-panel"],
|
||||
// Tool nicht deferren, sonst sieht das Modell write_panel nicht und
|
||||
// schreibt den Entwurf in den Chat statt ins Panel.
|
||||
"alwaysLoad": true,
|
||||
}),
|
||||
);
|
||||
servers.insert(PANEL_MCP_SERVER.into(), desired);
|
||||
if let Ok(out) = serde_json::to_string_pretty(&v) {
|
||||
let _ = fs::write(&cfg, out + "\n");
|
||||
}
|
||||
@@ -2137,6 +2154,17 @@ fn terminal_font_size() -> u32 {
|
||||
.unwrap_or(13)
|
||||
}
|
||||
|
||||
/// Standard-Sprache der Rechtschreibprüfung (settings.json: spellcheckLang),
|
||||
/// Default „de". Pro Text im Panel überschreibbar.
|
||||
#[tauri::command]
|
||||
fn spellcheck_lang() -> String {
|
||||
fs::read_to_string(Paths::real().config_dir().join(APP_SETTINGS_FILE))
|
||||
.ok()
|
||||
.and_then(|s| serde_json::from_str::<serde_json::Value>(&s).ok())
|
||||
.and_then(|v| v["spellcheckLang"].as_str().map(str::to_string))
|
||||
.unwrap_or_else(|| "de".to_string())
|
||||
}
|
||||
|
||||
/// Setzt die Schriftgröße; erhält übrige App-settings.
|
||||
#[tauri::command]
|
||||
fn set_terminal_font_size(size: u32) -> Result<(), String> {
|
||||
@@ -2269,6 +2297,30 @@ fn panel_archive_cmd(project: String, dir: Option<String>) -> Result<String, Str
|
||||
archive_panel_content(&project, dir.as_deref()).map(|p| p.display().to_string())
|
||||
}
|
||||
|
||||
/// Zeigt einen Pfad im Dateimanager (nach dem Archivieren „im Finder zeigen").
|
||||
#[tauri::command]
|
||||
fn reveal_path_cmd(path: String) {
|
||||
reveal_path(std::path::Path::new(&path));
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
fn reveal_path(path: &std::path::Path) {
|
||||
let _ = Command::new("open").arg("-R").arg(path).spawn();
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn reveal_path(path: &std::path::Path) {
|
||||
let target = path.parent().unwrap_or(path);
|
||||
let _ = Command::new("xdg-open").arg(target).spawn();
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
fn reveal_path(path: &std::path::Path) {
|
||||
let _ = Command::new("explorer")
|
||||
.arg(format!("/select,{}", path.display()))
|
||||
.spawn();
|
||||
}
|
||||
|
||||
/// D-Bus-Relay für die GNOME-Extension: Show()/Hide() steuern das Popup-Fenster.
|
||||
/// Die Extension ruft Show() beim Panel-Klick und positioniert das Fenster selbst.
|
||||
#[cfg(target_os = "linux")]
|
||||
@@ -2513,7 +2565,7 @@ fn terminal_builder(project: String) -> tauri::Builder<tauri::Wry> {
|
||||
if let tauri::WindowEvent::Destroyed = event {
|
||||
if window.label().starts_with("panel-") {
|
||||
use tauri::{Emitter, Manager};
|
||||
let _ = window.app_handle().emit("panel-attached", ());
|
||||
let _ = window.app_handle().emit("panel-window-closed", ());
|
||||
} else {
|
||||
terminal::close(window);
|
||||
}
|
||||
@@ -2529,12 +2581,14 @@ fn terminal_builder(project: String) -> tauri::Builder<tauri::Wry> {
|
||||
terminal::open_panel_window,
|
||||
panel_archive_dir_cmd,
|
||||
panel_archive_cmd,
|
||||
reveal_path_cmd,
|
||||
// Header im Terminal-Prozess: Projektliste, Icon und Pool-Name.
|
||||
list_projects,
|
||||
project_icon,
|
||||
pool_label,
|
||||
terminal_font_size,
|
||||
set_terminal_font_size
|
||||
set_terminal_font_size,
|
||||
spellcheck_lang
|
||||
])
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user