Refine state mapping editor behavior

Co-authored-by: Luke The Dev <iamlukethedev@users.noreply.github.com>
This commit is contained in:
Cursor Agent
2026-04-26 03:42:35 +00:00
co-authored by Luke The Dev
parent d91aa35d22
commit 08fdab643a
3 changed files with 79 additions and 9 deletions
@@ -35,6 +35,13 @@ const EFFECT_LABELS: Record<OfficeStateEffectId, string> = {
const createMappingId = (sourceState: string, index: number) =>
`state-${sourceState}-${Date.now()}-${index}`;
const labelFromSourceState = (sourceState: string) =>
sourceState
.replace(/[_-]+/g, " ")
.replace(/\s+/g, " ")
.trim()
.replace(/\b\w/g, (letter) => letter.toUpperCase());
const createMapping = (
sourceState: string,
animationTarget: OfficeStateAnimationTarget,
@@ -42,7 +49,7 @@ const createMapping = (
): OfficeStateAnimationMapping => ({
id: createMappingId(sourceState, params.priority ?? 50),
sourceState,
label: params.label ?? sourceState.replace(/_/g, " "),
label: params.label ?? labelFromSourceState(sourceState),
animationTarget,
effect: params.effect ?? "none",
soundCueId: params.soundCueId ?? null,
@@ -76,9 +83,17 @@ export function StateAnimationMappingsEditor({
patch: Partial<OfficeStateAnimationMapping>,
) => {
onChange(
mappings.map((mapping) =>
mapping.id === id ? { ...mapping, ...patch } : mapping,
),
mappings.map((mapping) => {
if (mapping.id !== id) return mapping;
const next = { ...mapping, ...patch };
if (
typeof patch.sourceState === "string" &&
mapping.label === labelFromSourceState(mapping.sourceState)
) {
next.label = labelFromSourceState(patch.sourceState);
}
return next;
}),
);
};
+59 -4
View File
@@ -3293,6 +3293,61 @@ export function OfficeScreen({
textMessageByAgentId,
workingUntilByAgentId,
} = officeAnimationState;
const configuredWorkflowHoldMaps = useMemo(
() =>
stateAnimationMappings.length > 0
? buildOfficeStateAnimationMappingResult({
agents: state.agents,
animationState: officeAnimationState,
mappings: stateAnimationMappings,
nowMs: animationNowMs,
})
: null,
[animationNowMs, officeAnimationState, state.agents, stateAnimationMappings],
);
const workflowGithubHoldByAgentId = useMemo(
() => {
const configuredGithubHolds =
configuredWorkflowHoldMaps?.githubHoldByAgentId ?? {};
const skillGithubHolds = buildOfficeSkillTriggerHoldMaps(
skillTriggers.movementTargetByAgentId,
).githubHoldByAgentId;
return {
...Object.fromEntries(
Object.entries(officeAnimationState.githubHoldByAgentId).filter(
([agentId]) => !configuredGithubHolds[agentId],
),
),
...skillGithubHolds,
};
},
[
configuredWorkflowHoldMaps?.githubHoldByAgentId,
officeAnimationState.githubHoldByAgentId,
skillTriggers.movementTargetByAgentId,
],
);
const workflowQaHoldByAgentId = useMemo(
() => {
const configuredQaHolds = configuredWorkflowHoldMaps?.qaHoldByAgentId ?? {};
const skillQaHolds = buildOfficeSkillTriggerHoldMaps(
skillTriggers.movementTargetByAgentId,
).qaHoldByAgentId;
return {
...Object.fromEntries(
Object.entries(officeAnimationState.qaHoldByAgentId).filter(
([agentId]) => !configuredQaHolds[agentId],
),
),
...skillQaHolds,
};
},
[
configuredWorkflowHoldMaps?.qaHoldByAgentId,
officeAnimationState.qaHoldByAgentId,
skillTriggers.movementTargetByAgentId,
],
);
const immediateGymHoldByAgentId = useMemo(
() => ({
...marketplaceGymHoldByAgentId,
@@ -3345,15 +3400,15 @@ export function OfficeScreen({
const activeGithubReviewAgentId = useMemo(
() =>
state.agents.find((agent) => githubHoldByAgentId[agent.agentId])
state.agents.find((agent) => workflowGithubHoldByAgentId[agent.agentId])
?.agentId ?? null,
[githubHoldByAgentId, state.agents],
[workflowGithubHoldByAgentId, state.agents],
);
const activeQaTestingAgentId = useMemo(
() =>
state.agents.find((agent) => qaHoldByAgentId[agent.agentId])?.agentId ??
state.agents.find((agent) => workflowQaHoldByAgentId[agent.agentId])?.agentId ??
null,
[qaHoldByAgentId, state.agents],
[workflowQaHoldByAgentId, state.agents],
);
useEffect(() => {
setGithubReviewAgentId(activeGithubReviewAgentId);
@@ -61,7 +61,7 @@ describe("StateAnimationMappingsEditor", () => {
target: { value: "syncing" },
});
expect(onChange).toHaveBeenLastCalledWith([
expect.objectContaining({ sourceState: "syncing" }),
expect.objectContaining({ sourceState: "syncing", label: "Syncing" }),
]);
fireEvent.change(screen.getByLabelText("Target"), {