mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-03 04:38:33 +00:00
fix(studio): compare the reveal request across the id-space boundary
The previous commit's store guard was silently never true. A reveal request carries the BARE dom id — `revealElementId = runtimeAudioId(keyframeClip)`, because the panel and the runtime speak that — while the store's ids are `sourceFile#domId`. So `elementKey === id` compared "music-bed" against "index.html#music-bed" and the request was still cleared by its own selection. This is the id-space boundary the branch's own handoff names as the trap most likely to be re-broken, and it fails exactly as documented: no error, the feature just never matches. Caught only by driving the real UI — the store said `selected: "index.html#music-bed"` and `revealSurvived: null` in the same read. `revealTargetsSelection` now splits the key with the existing `splitTimelineElementKey` and is a named function in `playerStoreSelection.ts` so the crossing is stated once, where it happens. **Verified in the browser, both halves, which had never been watched working:** - clip NOT selected: clicking a lane label selects the clip, the rack opens, and the carve module expands (correct — that lane's node is a carve band). The request is consumed and retired to null by the new unmount clear. - clip ALREADY selected: collapsing the module and clicking the same lane again reopens it, which the old value-equality consumption could not do. studio player: 103 files, 1355 tests. fallow clean.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { create } from "zustand";
|
||||
import { attachPlayerStoreDevHandle } from "./playerStoreDevHandle";
|
||||
import { nextSelectionSet } from "./playerStoreSelection";
|
||||
import { nextSelectionSet, revealTargetsSelection } from "./playerStoreSelection";
|
||||
import type { MusicBeatAnalysis } from "@hyperframes/core/beats";
|
||||
import type { GsapAnimation } from "@hyperframes/core/gsap-parser";
|
||||
import type { BeatEditState } from "../../utils/beatEditing";
|
||||
@@ -541,7 +541,13 @@ export const usePlayerStore = create<PlayerState>((set, get) => ({
|
||||
// selection lands afterwards and used to clear the very request that
|
||||
// caused it — the panel then read null and the section never opened.
|
||||
// Any OTHER selection still drops it: a request aimed elsewhere is stale.
|
||||
const revealSurvives = s.revealedAudioFxTarget?.elementKey === id;
|
||||
//
|
||||
// Compared across the ID-SPACE BOUNDARY, which is why this needs saying:
|
||||
// a request carries the BARE dom id (`runtimeAudioId`, because the panel
|
||||
// and the runtime speak that), while this store's ids are
|
||||
// `sourceFile#domId`. A direct `===` was silently never true — the exact
|
||||
// shape of failure the id-space split produces.
|
||||
const revealSurvives = revealTargetsSelection(s.revealedAudioFxTarget, id);
|
||||
return id !== s.selectedElementId
|
||||
? {
|
||||
selectedElementId: id,
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
/**
|
||||
* Selection-set arithmetic for the player store.
|
||||
* Selection-set arithmetic for the player store, and the reveal request's
|
||||
* cross-id-space match.
|
||||
*
|
||||
* Its own module so `playerStore.ts` stays under the studio's 600-line cap.
|
||||
*/
|
||||
|
||||
import { splitTimelineElementKey } from "../lib/timelineElementHelpers";
|
||||
|
||||
/**
|
||||
* The id set a selection change leaves behind.
|
||||
*
|
||||
@@ -19,3 +22,20 @@ export function nextSelectionSet(
|
||||
if (preserveSet) return id && current.has(id) ? new Set(current) : new Set<string>();
|
||||
return id ? new Set([id]) : new Set<string>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Is a pending reveal request aimed at the element being selected?
|
||||
*
|
||||
* Compared across the ID-SPACE BOUNDARY, which is why it is a named function:
|
||||
* a request carries the BARE dom id (`runtimeAudioId` — the panel and the
|
||||
* runtime speak that), while the store's ids are `sourceFile#domId`. A direct
|
||||
* `===` between the two is silently never true, which is the exact shape of
|
||||
* failure the split produces and how the reveal came to be dead.
|
||||
*/
|
||||
export function revealTargetsSelection(
|
||||
request: { elementKey: string } | null,
|
||||
id: string | null,
|
||||
): boolean {
|
||||
if (!request || id === null) return false;
|
||||
return request.elementKey === splitTimelineElementKey(id).domId;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user