fix(toggles): keep toggle negatives in the Assembler's preview layering (#383)

The Assembler's injector layer no longer clears a toggle's own negative from the preview.
This commit is contained in:
DumiFlex
2026-11-12 07:00:50 +01:00
committed by GitHub
parent 54d48f88f5
commit be71b6391b
3 changed files with 33 additions and 2 deletions
+17
View File
@@ -277,6 +277,23 @@ describe("collectUpstreamProducers", () => {
});
});
it("collectUpstreamInjectorNegatives: a toggle's own negative, or null when it has none", () => {
const rows = [
{ id: "a", kind: "toggle", name: "Rain", var: "rain", on: true, pin: false, out: 0, style: "switch", value: "in the rain", negative: "sunny sky" },
{ id: "b", kind: "toggle", name: "Fog", var: "fog", on: false, pin: false, out: 0, style: "switch", off_negative: "clear air" },
{ id: "c", kind: "toggle", name: "Wind", var: "wind", on: true, pin: false, out: 0, style: "switch" },
];
const tg: LiteNodeLike = {
id: 1,
type: "WP_Toggles",
outputs: [{ name: "context", links: [100], type: "PIPELINE_CONTEXT" }],
widgets: [{ name: "wp_toggles", value: JSON.stringify({ version: 1, mode: "any", group_var: "", rows }) }],
};
const pov = ctxWriting(2, "Other", "unused", 100);
const g = chain([tg, pov], { 100: { origin_id: 1, target_id: 2 } });
expect(collectUpstreamInjectorNegatives(g, pov)).toEqual({ rain: "sunny sky", fog: "clear air", wind: null });
});
it("attributes the loop's iteration vars, including the _total pair", () => {
const loop: LiteNodeLike = {
id: 1,
+3 -2
View File
@@ -19,7 +19,7 @@ import {
cacheVersion as previewCacheVersion,
} from "./preview-resolver";
import { assignCodenames, baseCodename } from "./node-codename";
import { TOGGLES_NODE, staticToggleInternals, staticToggleValues, togglesPreviewModules } from "./toggles";
import { TOGGLES_NODE, staticToggleInternals, staticToggleNegatives, staticToggleValues, togglesPreviewModules } from "./toggles";
import { MODEL_INFO_NODE, MODEL_VARS, internalModelVars, modelInfoPreviewModules, staticModelValues } from "./model-info";
import { clauseActions, derivationTargets, evalConditionTree, isNegativeMode } from "./derivation-conditions";
import type { SeedStrategy } from "../components/shared/seed-derive";
@@ -760,7 +760,8 @@ export function collectUpstreamInjectorNegatives(
continue;
}
if (n.type === TOGGLES_NODE && !isSkippedMode(n)) {
for (const v of Object.keys(staticToggleValues(n))) out[v] = null;
// A write replaces the variable's negatives with the toggle's own.
for (const [v, neg] of Object.entries(staticToggleNegatives(n))) out[v] = neg || null;
continue;
}
if (n.type !== "WP_ContextInjector" || isSkippedMode(n)) continue;
+13
View File
@@ -574,6 +574,19 @@ export function staticToggleValues(node: TogglesNodeLike): Record<string, string
return resolveToggles(nodeTogglesConfig(node));
}
/** Each variable the node writes → the negative it carries now ("" for none):
* its Negative while on, Off negative while off, and the pick's negative on
* the Pick one variable. */
export function staticToggleNegatives(node: TogglesNodeLike): Record<string, string> {
const cfg = nodeTogglesConfig(node);
const own = ownToggleNegatives(cfg);
const picked = pickedToggleNegative(cfg);
if (picked && cfg.group_var) own[cfg.group_var] = picked;
const out: Record<string, string> = {};
for (const v of Object.keys(resolveToggles(cfg))) out[v] = own[v] ?? "";
return out;
}
/** The node's variables that stay out of the prompt (graph walkers). */
export function staticToggleInternals(node: TogglesNodeLike): Set<string> {
return internalToggleVars(nodeTogglesConfig(node));