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:
@@ -79,7 +79,7 @@ describe("initCommandsView", () => {
|
||||
JSON.stringify({ ts: 1700000000, session: true }),
|
||||
JSON.stringify({
|
||||
ts: 1700000100,
|
||||
commands: [{ cmd: "ls -la", note: "Liste" }, { cmd: "pwd" }],
|
||||
commands: [{ cmd: "ls -la", note: "Liste", id: "id-1" }, { cmd: "pwd", id: "id-2" }],
|
||||
}),
|
||||
].join("\n");
|
||||
|
||||
@@ -94,14 +94,14 @@ describe("initCommandsView", () => {
|
||||
expect(view.empty()).toBe(true);
|
||||
});
|
||||
|
||||
/// Der Befehlstext geht mit, weil die Indizes der Datei hinterherhinken
|
||||
/// können; das Backend gleicht damit ab, bevor es löscht.
|
||||
it("meldet Löschen mit Record-Index, Eintrags-Index und Befehl", () => {
|
||||
/// Gelöscht wird über die stabile ID aus write_commands — keine Indizes,
|
||||
/// die der Datei hinterherhinken könnten.
|
||||
it("meldet Löschen mit der Eintrags-ID", () => {
|
||||
const onDelete = vi.fn();
|
||||
const view = initCommandsView(document.getElementById("c")!, onDelete);
|
||||
view.set(jsonl);
|
||||
const dels = document.querySelectorAll<HTMLElement>(".cmd-del");
|
||||
dels[1].click(); // zweite Kachel im Record 1
|
||||
expect(onDelete).toHaveBeenCalledWith(1, 1, "pwd");
|
||||
expect(onDelete).toHaveBeenCalledWith("id-2");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/// Kachel-Ansicht der Command-History: rendert die JSONL-Datei
|
||||
/// (write_commands im MCP-Server) als kopierbare Kacheln, Neuestes oben,
|
||||
/// mit Zeitmarken und Session-Trennern. Löschen einer Kachel geht als
|
||||
/// onDelete(line, entry) an den Aufrufer; der neue Stand kommt über den
|
||||
/// onDelete(id) an den Aufrufer; der neue Stand kommt über den
|
||||
/// Watcher zurück. DOM wird per createElement gebaut — Befehle sind
|
||||
/// Fremdtext und gehen nie durch innerHTML.
|
||||
|
||||
@@ -17,6 +17,8 @@ function stripInvisibles(s: string): string {
|
||||
interface CommandEntry {
|
||||
cmd: string;
|
||||
note?: string;
|
||||
/// Stabile ID aus write_commands; Grundlage fürs Löschen.
|
||||
id?: string;
|
||||
}
|
||||
|
||||
interface Record {
|
||||
@@ -79,7 +81,7 @@ function deleteBtn(onClick: () => void): HTMLButtonElement {
|
||||
|
||||
export function initCommandsView(
|
||||
container: HTMLElement,
|
||||
onDelete: (line: number, entry: number, cmd: string) => void,
|
||||
onDelete: (id: string) => void,
|
||||
): CommandsView {
|
||||
let count = 0;
|
||||
|
||||
@@ -120,7 +122,7 @@ export function initCommandsView(
|
||||
}
|
||||
block.append(head);
|
||||
|
||||
cmds.forEach((entry, j) => {
|
||||
cmds.forEach((entry) => {
|
||||
// Unsichtbare Steuerzeichen (Bidi, Zero-Width) aus Anzeige UND
|
||||
// Kopie halten — sonst sieht der Nutzer einen anderen Befehl, als
|
||||
// die Zwischenablage enthält. Der Lösch-Abgleich läuft weiter über
|
||||
@@ -140,7 +142,7 @@ export function initCommandsView(
|
||||
note.textContent = entry.note;
|
||||
body.append(note);
|
||||
}
|
||||
tile.append(body, copyBtn(() => visible), deleteBtn(() => onDelete(i, j, entry.cmd)));
|
||||
tile.append(body, copyBtn(() => visible), deleteBtn(() => onDelete(entry.id ?? "")));
|
||||
block.append(tile);
|
||||
});
|
||||
container.append(block);
|
||||
|
||||
+1
-1
@@ -77,7 +77,7 @@ export async function wirePanel(
|
||||
});
|
||||
const cmdView = initCommandsView(
|
||||
document.getElementById("commands-content")!,
|
||||
(line, entry, cmd) => invoke("commands_delete", { project, line, entry, cmd }),
|
||||
(id) => invoke("commands_delete", { project, id }),
|
||||
);
|
||||
// Treffer-Klick lädt das Dokument in den Dokument-Tab (dort editier- und
|
||||
// archivierbar); der Sprung ins Wiki geht von dort aus.
|
||||
|
||||
Reference in New Issue
Block a user