Stop offering private variables as new animation rows (#270)

A behavior variable switched private is hidden from the placed object
and from the Properties node, but the row dialog of an animation kept
offering it. Rows that exist already stay as they are, as decided in
the issue.

- offeredDefs in src/core/model/animation.ts is what the dialog offers
  now: what the target can animate, less private variables and less
  what another row animates already. The filter moved there from
  RowDialog.ts, so it can be tested without the editor.
- animatableDefsOf is unchanged. The label and the runtime read it, so
  a row on a variable that went private keeps playing and keeps its
  label.
- Deviation from the proposal: under Change target the row's own
  variable stays on offer, private or not. The dialog would open with
  no property and a disabled button otherwise. Other private variables
  are not offered there either.
- The command does not refuse a private variable. Only the dialog does
  not offer it.

Finding, not fixed: e2e 56 fails when it runs alone, in 56a-ii from
#257 (typing "9" did not arrive: focus is BODY). With 55 before it the
module passes, so a full run does not show it. It was not measured on
HEAD without this change.

Tests:
- Gate: 198 files, 2883 tests (2882 + 1), exit 0
- e2e 55,56: 27 checks passed, 56 has +56e
- e2e 25: 31 checks passed, it uses the same dialog
- Three unit counter-probes each fail the new test
- The browser counter-probe fails 56e, mood shows up in the dialog
- e2e total 1610 is computed, not measured in a full run

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
adminandClaude Opus 5.5 committed 2026-10-01 07:45:05 +02:00
1 parent 5e21d473a4
commit 6491888f56
5 files changed
+109 -29

No files matched your search

+34 -8
View File
@@ -1,14 +1,15 @@
import { isKindWith } from '../properties/kinds';
import { type PropertyDef, schemaFor, shaderParamRows } from '../properties/PropertySchema';
import { shaderOf } from '../properties/shaders';
import type {
AnimationAsset,
AnimationKey,
AnimationTrack,
Asset,
EasingCurve,
EasingDirection,
KeyInterpolation,
import {
type AnimationAsset,
type AnimationKey,
type AnimationTrack,
type Asset,
type EasingCurve,
type EasingDirection,
isPrivateVariable,
type KeyInterpolation,
} from './Asset';
import type { AssetType } from './AssetTypes';
import { type CurveLookup, curveValue, NO_CURVES } from './curve';
@@ -480,6 +481,31 @@ export function trackDef(
return targetDefs(project, animation, track.target)?.find((d) => d.key === track.property);
}
/**
* The rows the dialog offers for the object `target` names: what it can animate, less a behavior's
* private variables (#270) and what another row animates already. A row on a private variable keeps
* playing – only a new one is not offered. `changing` is the row being aimed elsewhere: its keys
* stay, so only properties of its kind are offered, and its own property is, private or not.
*/
export function offeredDefs(
project: Project,
animation: AnimationAsset,
target: string,
changing: AnimationTrack | null = null,
): PropertyDef[] {
const aimed = aimedAt(project, animation, target);
if (!aimed) return [];
const kind = changing ? (trackDef(project, animation, changing)?.kind ?? null) : null;
return animatableDefsOf(project, aimed.asset, aimed.bag).filter((def) => {
if (kind !== null && def.kind !== kind) return false;
const own = changing?.target === target && changing.property === def.key;
if (isPrivateVariable(aimed.asset, def.key) && !own) return false;
return !animation.tracks.some(
(t) => t.id !== changing?.id && t.target === target && t.property === def.key,
);
});
}
/** How the timeline names what a row aims at: the object (or the root itself) and the property. */
export function trackLabel(project: Project, animation: AnimationAsset, track: AnimationTrack): string {
const aimed = aimedAt(project, animation, track.target);