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
+36 -1
View File
@@ -39,6 +39,12 @@ pub(crate) struct ProjectConfig {
skip_serializing_if = "Option::is_none"
)]
pub(crate) archive_home: Option<String>,
/// Alle Keys, die dieser Build nicht kennt — unverändert durchgereicht.
/// Ohne das verliert jeder read-modify-write (Pool-Zuweisung, Terminal-
/// Einstellungen) still die Felder neuerer Versionen: serde verwirft
/// Unbekanntes beim Deserialisieren, und geschrieben wird die ganze Datei.
#[serde(flatten)]
pub(crate) rest: serde_json::Map<String, serde_json::Value>,
}
#[derive(Serialize, Deserialize, Default, Clone)]
@@ -226,7 +232,11 @@ pub(crate) fn create_project_full_in(
reg.insert(name.to_string(), dir.clone());
save_registry(paths, &reg)?;
let cfg = ProjectConfig { pool: pool.map(str::to_string), terminal, archive_home: None };
let cfg = ProjectConfig {
pool: pool.map(str::to_string),
terminal,
..ProjectConfig::default()
};
if cfg.pool.is_some() || !cfg.terminal.is_empty() {
write_project_config_in(paths, name, &cfg)?;
}
@@ -489,6 +499,31 @@ mod tests {
use crate::domain::testutil::{create_project, make_apikey_pool, make_oauth_pool, map_store, tmp_paths};
use crate::domain::todo::TODO_FILE;
/// Der read-modify-write darf keine Keys verlieren, die dieser Build nicht
/// kennt. Genau daran ging `archiveHome` verloren: Ein älterer Build ohne das
/// Feld hat bei der Pool-Zuweisung die ganze Datei neu geschrieben.
#[test]
fn pool_zuweisen_erhaelt_fremde_keys() {
let p = tmp_paths();
let pool = make_apikey_pool(&p, &map_store(), "kunde", "sk-1");
create_project(&p, "proj").unwrap();
let cfg_path = project_config_path(&p, "proj").unwrap();
fs::write(
&cfg_path,
r#"{"archiveHome":"~/archiv","zukunftsfeld":{"a":1},"terminal":{"theme":"monokai"}}"#,
)
.unwrap();
assign_pool_in(&p, "proj", &pool).unwrap();
let raw = fs::read_to_string(&cfg_path).unwrap();
let v: serde_json::Value = serde_json::from_str(&raw).unwrap();
assert_eq!(v["archiveHome"], "~/archiv");
assert_eq!(v["zukunftsfeld"]["a"], 1);
assert_eq!(v["terminal"]["theme"], "monokai");
assert_eq!(v["pool"], serde_json::Value::String(pool));
}
#[test]
fn projekt_wizard_scaffold() {
let p = tmp_paths();