refactor: implement base/modifier separation for variable reactivity
Introduce a distinction between base values (set via `/set` or declarations) and active modifiers (applied via tags). This ensures tag transitions and numeric modifiers are calculated correctly during cascades. - Refactor `VariableView` to use a flat list with hover popups for declarations and active modifiers. - Update `command-dispatcher` to use `getCombined` for accurate value lookups and `setBase` for variable updates. - Update `var-reactivity` to manage `baseValues`, `activeMods`, and `sourceActivations` separately. - Export new utility functions for accessing combined values, modifiers, and declaration expressions.
This commit is contained in:
@@ -11,7 +11,7 @@ import { parseInput } from "./command-parser";
|
||||
import { resolveRollPayload } from "./types/roll";
|
||||
import { resolveSparkPayload } from "./types/spark";
|
||||
import { evaluateExpression, expressionIsTag } from "./variable-expression";
|
||||
import { computeCascade } from "./var-reactivity";
|
||||
import { computeCascade, getCombined, setBase } from "./var-reactivity";
|
||||
import type { VarDeclaration, TagModifier } from "./declare-parser";
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -140,7 +140,9 @@ function dispatchSet(
|
||||
}
|
||||
|
||||
const key = p.key;
|
||||
const oldValue = ctx.variables[key] ?? undefined;
|
||||
// Use getCombined for old value so tag transitions are detected correctly
|
||||
// even when the variable has active mods.
|
||||
const oldValue = getCombined(key);
|
||||
let newValue: string;
|
||||
|
||||
try {
|
||||
@@ -152,11 +154,12 @@ function dispatchSet(
|
||||
// Bare tag value
|
||||
newValue = p.expr.trim();
|
||||
} else if (p.expr) {
|
||||
// Numeric expression — evaluate
|
||||
// Numeric expression — evaluate using combined values
|
||||
const result = evaluateExpression(p.expr, {
|
||||
lookup: (name: string) => {
|
||||
const k = "$" + name;
|
||||
return ctx.variables[k] ?? undefined;
|
||||
// Use getCombined for accurate values including active mods
|
||||
return k === key ? undefined : getCombined(k);
|
||||
},
|
||||
});
|
||||
newValue = String(result.value);
|
||||
@@ -170,12 +173,11 @@ function dispatchSet(
|
||||
};
|
||||
}
|
||||
|
||||
// Send the direct set
|
||||
const r1 = sendMessage("var", { action: "set", key, value: newValue });
|
||||
const u1 = unwrap(r1);
|
||||
if (!u1.ok) return u1;
|
||||
// Update base value so getCombined returns the correct new value
|
||||
// during cascade computation
|
||||
setBase(key, newValue);
|
||||
|
||||
// Build working variable store for cascade
|
||||
// Build working variable store for cascade (includes the new value)
|
||||
const workingVars = { ...ctx.variables, [key]: newValue };
|
||||
|
||||
// Compute cascade (tag activation/deactivation + declaration re-eval)
|
||||
@@ -189,6 +191,14 @@ function dispatchSet(
|
||||
console.warn("[dispatch] cascade error:", e);
|
||||
}
|
||||
|
||||
// Send the direct set last so cascade messages arrive first.
|
||||
// Use getCombined so the stream store receives the correct value
|
||||
// (base + active mods) rather than just the raw base.
|
||||
const combinedValue = getCombined(key);
|
||||
const r1 = sendMessage("var", { action: "set", key, value: combinedValue });
|
||||
const u1 = unwrap(r1);
|
||||
if (!u1.ok) return u1;
|
||||
|
||||
return { ok: true };
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user