fix: Beute-Editor und Durchsicht nach Browser-Prüfung

- Umbauen: Fokus nach Rückgängig/Wiederholen am Ende auf den anderen
  Verlaufsknopf, nach Entfernen auf die Nachbarzeile (sonst Überschrift)
- Umbauen: Hinweis „Beute wurde inzwischen geändert“ zuverlässig, der
  Stand wird bei Fokus/Sichtbarkeit und alle 30 s neu geladen, solange
  ein Entwurf besteht; Editor-Wurzel ist <main>
- Verlauf: Futtertaschen-Füllstand als „Flüssigfutter 1,5 l“, Entfernen
  als „Futtertasche entfernt“; Gliederung ohne leere Werte („Boden · /“)
- Durchsicht: Rückgängig der letzten Änderung behält den Schalter
  „Liste“ und setzt den Fokus auf die Überschrift der Änderungen

Refs #21 #22

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
Nick SzaunigandClaude Opus 5.5 committed 2026-10-16 09:12:17 +02:00
1 parent 7a07e1cac4
commit 8166e7c621
12 files changed
+253 -36

No files matched your search

+14 -1
View File
@@ -1,5 +1,5 @@
import { describe, expect, it } from "vitest";
import { describeHiveOps } from "./describe";
import { describeHiveOps, partsAddedBy } from "./describe";
import { inlineCatalog, part, templateState } from "./test-utils";
import type { HiveOp, HiveState } from "./types";
@@ -20,6 +20,19 @@ describe("describeHiveOps", () => {
expect(describeHiveOps(ops, stateWithInsert(), catalog)).toBe("Bausperre entfernt, Zarge hinzugefügt");
});
it("names parts that were added by earlier changes and are gone now", () => {
const pocket = part("p1", "frame-feeder", { slot: 1, state: { syrupLiters: 1 } });
const earlier: HiveOp[] = [{ op: "addInsert", hostId: "b", part: pocket }];
const seen = partsAddedBy(earlier);
const later: HiveOp[] = [
{ op: "setState", id: "p1", key: "syrupLiters", value: 1.5 },
{ op: "removePart", id: "p1" },
];
expect(describeHiveOps(later, templateState(), catalog, seen)).toBe("Flüssigfutter 1,5 l, Futtertasche entfernt");
expect(describeHiveOps(later, templateState(), catalog)).toContain("Teil entfernt");
});
it("uses the label of a part when it has one", () => {
const state = templateState();
state.stack[1].label = "Honigraum 1";
+13 -1
View File
@@ -63,13 +63,25 @@ function describeOne(op: HiveOp, known: Map<string, Named>, catalog: HiveCatalog
}
}
/** Parts (and their inserts) that ops put into the stack, so later texts can still name them. */
export function partsAddedBy(ops: HiveOp[]): HivePart[] {
return ops.flatMap((op) => (op.op === "addPart" || op.op === "addInsert" ? [op.part] : []));
}
/**
* German one-line summary of a change, e.g. „Bausperre entfernt, Zarge hinzugefügt“.
* Names come from `before` plus parts the same batch adds; more than three ops
* are shortened to the first three and „und n weitere“.
*/
export function describeHiveOps(ops: HiveOp[], before: HiveState, catalog: HiveCatalog): string {
export function describeHiveOps(
ops: HiveOp[],
before: HiveState,
catalog: HiveCatalog,
/** Parts from other changes that no longer exist in `before` (see `partsAddedBy`). */
seen: HivePart[] = [],
): string {
const known = new Map<string, Named>();
for (const part of seen) collect(part, catalog, known);
for (const part of before.stack) collect(part, catalog, known);
const texts = ops.map((op) => describeOne(op, known, catalog));
if (texts.length <= MAX_SHOWN_OPS) return texts.join(", ");