Command-History: stabile ID je Eintrag statt Index+Text-Abgleich

write_commands vergibt jedem Eintrag eine UUID; commands_delete löscht
darüber. Doppelklick oder ein zweites Fenster laufen ins 'bereits
entfernt' statt auf verschobene Indizes.
This commit is contained in:
marcus hinz
2026-07-20 13:37:02 +02:00
parent ab1c561d6c
commit 6682040cc0
5 changed files with 38 additions and 41 deletions
+15 -30
View File
@@ -270,27 +270,12 @@ pub fn commands_read(project: String) -> String {
}
/// Entfernt einen Befehl aus der Command-History (Löschen einer Kachel im
/// Panel). `line` ist der Index der nicht-leeren JSONL-Zeile, `entry` der
/// Index im commands-Array des Records; ein leer gewordener Record fällt mit
/// weg. Der Watcher meldet den neuen Stand als `commands-update`.
///
/// `cmd` ist der angezeigte Befehlstext und wird vor dem Löschen abgeglichen:
/// Die Indizes des Panels sind der Datei um bis zu ein Watcher-Intervall
/// hinterher, und ein leer gewordener Record verschiebt alle folgenden. Ohne
/// Abgleich löscht ein Doppelklick — oder das zweite Fenster auf derselben
/// Liste — die falsche Kachel oder greift ins Leere.
///
/// Bewusst ein Provisorium, keine echte Identität: Zwei gleiche Befehle im
/// selben Record sind so nicht unterscheidbar. Sauber wäre eine stabile ID, die
/// beim Schreiben der JSONL vergeben wird, statt Positionen über die
/// Fenstergrenze zu reichen.
/// Panel) über seine stabile ID, die write_commands beim Schreiben vergibt.
/// Ein leer gewordener Record fällt mit weg; der Watcher meldet den neuen
/// Stand als `commands-update`. Doppelklick oder ein zweites Fenster auf
/// derselben Liste laufen ins „bereits entfernt" statt auf falsche Indizes.
#[tauri::command]
pub fn commands_delete(
project: String,
line: usize,
entry: usize,
cmd: String,
) -> Result<(), String> {
pub fn commands_delete(project: String, id: String) -> Result<(), String> {
let path = commands_file(&project);
let text = std::fs::read_to_string(&path).map_err(|e| e.to_string())?;
let mut records: Vec<serde_json::Value> = text
@@ -298,18 +283,18 @@ pub fn commands_delete(
.filter(|l| !l.trim().is_empty())
.map(|l| serde_json::from_str(l).map_err(|e| e.to_string()))
.collect::<Result<_, _>>()?;
let cmds = records
.get_mut(line)
.ok_or("Befehl bereits entfernt")?["commands"]
.as_array_mut()
.ok_or("Record ohne commands")?;
if cmds.get(entry).and_then(|c| c["cmd"].as_str()) != Some(cmd.as_str()) {
let mut found = false;
for rec in records.iter_mut() {
if let Some(cmds) = rec["commands"].as_array_mut() {
let before = cmds.len();
cmds.retain(|c| c["id"].as_str() != Some(id.as_str()));
found = found || cmds.len() != before;
}
}
if !found {
return Err("Befehl bereits entfernt".into());
}
cmds.remove(entry);
if cmds.is_empty() {
records.remove(line);
}
records.retain(|r| r["commands"].as_array().is_none_or(|c| !c.is_empty()));
let mut out = String::new();
for rec in &records {
out.push_str(&rec.to_string());