Bugfix-Runde Panel/Archiv: Datenverlust, Suche, Löschen, Markdown

- ProjectConfig reicht unbekannte Keys durch (serde flatten). Bisher verlor
  jeder read-modify-write die Felder neuerer Versionen; so ging archiveHome
  bei der Pool-Zuweisung durch einen älteren Build verloren.
- Archiv-Suche: Nutzereingabe wird in einen FTS5-Ausdruck übersetzt, der nicht
  scheitern kann. ai-control, C++ oder eine halbe Phrase warfen bisher einen
  Syntaxfehler — bei der Live-Suche in jedem Tippzwischenstand.
- commands_delete: Befehlstext als Identität, Indizes werden geprüft. Bisher
  Panik oder Löschen der falschen Kachel, wenn die Panel-Indizes der Datei
  hinterherhingen.
- Archivieren überschreibt keine Datei mehr: Der Stempel hat Minutenauflösung,
  bei Kollision wird -2, -3, ... angehängt.
- archive_panel prüft erst auf leeres Panel, dann wird das Archiv-Home gesetzt;
  bisher blieb ein umkonfiguriertes Home trotz Meldung "nicht archiviert".
- Kaputte .claude.json eines Pools wird nicht mehr durch eine frische ersetzt;
  nur eine fehlende Datei rechtfertigt das.
- Archiv-Formular leert beim Schließen — sonst galten Ordner und Schlagwörter
  des vorigen Dokuments unbemerkt fürs nächste.
- Markdown fürs Panel entschärft (src/markdown.ts): rohes HTML wird als Text
  ausgegeben, Link- und Bildziele auf harmlose Schemata beschränkt. Das Panel
  teilt sich den Webview mit dem Terminal, dessen term_write in die PTY
  schreibt; ohne CSP war das ein Weg von Fremdinhalt zu Befehlsausführung.

Regressionstests je Fix: 73 Rust-Tests, 35 Frontend-Tests.
This commit is contained in:
marcusH
2026-07-19 23:04:17 +02:00
parent 1345ef799c
commit b127269e89
14 changed files with 306 additions and 28 deletions
+18 -2
View File
@@ -249,8 +249,19 @@ pub fn commands_read(project: String) -> String {
/// 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 dient als Identität: 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.
#[tauri::command]
pub fn commands_delete(project: String, line: usize, entry: usize) -> Result<(), String> {
pub fn commands_delete(
project: String,
line: usize,
entry: usize,
cmd: 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
@@ -258,9 +269,14 @@ pub fn commands_delete(project: String, line: usize, entry: usize) -> Result<(),
.filter(|l| !l.trim().is_empty())
.map(|l| serde_json::from_str(l).map_err(|e| e.to_string()))
.collect::<Result<_, _>>()?;
let cmds = records[line]["commands"]
let cmds = records
.get_mut(line)
.ok_or("Befehl bereits entfernt")?["commands"]
.as_array_mut()
.ok_or("Record ohne commands")?;
if cmds.get(entry).map(|c| c["cmd"].as_str() != Some(cmd.as_str())).unwrap_or(true) {
return Err("Befehl bereits entfernt".into());
}
cmds.remove(entry);
if cmds.is_empty() {
records.remove(line);