fix(ui): hide engine-internal presets from user authoring surface
The Modifier Profile editor's Presets tab surfaced the plumbing preset '__modifier-profile-integration__' — an internal hook bundle that wires modifier-profile facts (CaptureFlags, DirectionAdditions, DamageResistance, on-damage / on-capture / aura triggers) into the engine runtime. ChessEngine auto-activates it when a profile is supplied; toggling it from a profile has no meaningful effect and confuses users who see 'Modifier Profile Integration' as a bundle-able rule variant. Filter any preset id matching the '__x__' double-underscore naming convention out of: - PresetPanel's allPresets list (the Presets tab in the editor) - LayoutPicker's suggestedPresets chip row (in case a layout ever accidentally lists one) Generalized so future internal presets following the same convention are auto-hidden without maintaining an exclusion list. New unit test (PresetPanel.filter.test.ts) pins the contract: registry contains the internal preset, filter yields a non-empty user-safe list, known public preset (extinction-chess) survives.
This commit is contained in:
parent
ef730eefbe
commit
e5594b0d3c
3 changed files with 71 additions and 2 deletions
|
|
@ -144,7 +144,14 @@ export function LayoutPicker({
|
|||
value.suggestedPresets.length > 0 && (
|
||||
<div className="pt-2 flex flex-wrap items-center gap-1.5">
|
||||
<span className="text-xs text-neutral-400 italic mr-1">Suggested rules:</span>
|
||||
{value.suggestedPresets.map((presetId) => {
|
||||
{value.suggestedPresets
|
||||
// Never surface engine-internal plumbing presets
|
||||
// (__x__ naming convention) even if a layout
|
||||
// accidentally lists one — they auto-activate via
|
||||
// the engine constructor and can't be meaningfully
|
||||
// toggled by the user.
|
||||
.filter((id) => !(id.startsWith('__') && id.endsWith('__')))
|
||||
.map((presetId) => {
|
||||
const presetDef = PRESET_REGISTRY.get(presetId);
|
||||
const label = presetDef?.name ?? presetId;
|
||||
const isActive = activations.some(a => a.id === presetId);
|
||||
|
|
|
|||
46
packages/chess/src/ui/PresetPanel.filter.test.ts
Normal file
46
packages/chess/src/ui/PresetPanel.filter.test.ts
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
import { describe, it, expect } from "vitest";
|
||||
import { PRESET_REGISTRY } from "../presets/registry.js";
|
||||
import "../presets/index.js";
|
||||
// Side-effect import: registers the __modifier-profile-integration__
|
||||
// pseudo-preset (top-level register call in apply.ts). The modifier
|
||||
// barrel doesn't pull this in, so we import directly to mirror the
|
||||
// engine constructor's boot ordering.
|
||||
import "../modifiers/apply.js";
|
||||
|
||||
/**
|
||||
* Regression guard — the Modifier Profile editor's Presets tab used
|
||||
* to surface the engine-internal `__modifier-profile-integration__`
|
||||
* preset because it calls PRESET_REGISTRY.getAll() directly. That
|
||||
* preset is plumbing (it wires modifier facts into move-gen / damage
|
||||
* pipelines) and is auto-activated by the ChessEngine constructor,
|
||||
* so it should never appear as something a user can toggle on a
|
||||
* profile.
|
||||
*
|
||||
* The panel now filters any id matching the `__x__` double-underscore
|
||||
* convention. This test pins that contract.
|
||||
*/
|
||||
describe("Preset authoring surface excludes internal presets", () => {
|
||||
const isInternal = (id: string) => id.startsWith("__") && id.endsWith("__");
|
||||
|
||||
it("registry contains the modifier-profile-integration internal preset", () => {
|
||||
const all = PRESET_REGISTRY.getAll();
|
||||
const internal = all.filter((p) => isInternal(p.id));
|
||||
expect(internal.length).toBeGreaterThan(0);
|
||||
expect(internal.map((p) => p.id)).toContain(
|
||||
"__modifier-profile-integration__",
|
||||
);
|
||||
});
|
||||
|
||||
it("filtering internal presets yields a user-authoring-safe list", () => {
|
||||
const all = PRESET_REGISTRY.getAll();
|
||||
const userFacing = all.filter((p) => !isInternal(p.id));
|
||||
expect(userFacing.length).toBeGreaterThan(0);
|
||||
for (const p of userFacing) {
|
||||
expect(isInternal(p.id)).toBe(false);
|
||||
}
|
||||
// Sanity: at least one well-known user-facing preset survives
|
||||
// the filter. extinction-chess is a stable long-standing id so
|
||||
// this assertion won't churn with preset-registry shuffling.
|
||||
expect(userFacing.map((p) => p.id)).toContain("extinction-chess");
|
||||
});
|
||||
});
|
||||
|
|
@ -121,7 +121,23 @@ export function PresetPanel({
|
|||
onChange,
|
||||
boundLayout,
|
||||
}: PresetPanelProps) {
|
||||
const allPresets = useMemo(() => PRESET_REGISTRY.getAll(), []);
|
||||
// Filter engine-internal presets out of the authoring surface.
|
||||
// Convention: any id wrapped in double-underscores (`__x__`) is
|
||||
// reserved for plumbing presets the engine auto-activates — e.g.
|
||||
// `__modifier-profile-integration__` wires modifier facts into
|
||||
// move-gen / damage pipelines. Those should never appear in a
|
||||
// profile's bundled activations because they can't be meaningfully
|
||||
// toggled; they either flip on via the engine constructor or they
|
||||
// don't. Same guard protects against any future internal presets
|
||||
// that follow the naming convention without our having to
|
||||
// hand-maintain an exclusion list.
|
||||
const allPresets = useMemo(
|
||||
() =>
|
||||
PRESET_REGISTRY.getAll().filter(
|
||||
(p) => !(p.id.startsWith('__') && p.id.endsWith('__')),
|
||||
),
|
||||
[],
|
||||
);
|
||||
const activeById = useMemo(() => {
|
||||
const m = new Map<string, PresetActivation>();
|
||||
for (const a of activations) m.set(a.id, a);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue