diff --git a/packages/studio/src/hooks/useMusicBeatAnalysis.ts b/packages/studio/src/hooks/useMusicBeatAnalysis.ts index 078def68f3..8d0f2dc585 100644 --- a/packages/studio/src/hooks/useMusicBeatAnalysis.ts +++ b/packages/studio/src/hooks/useMusicBeatAnalysis.ts @@ -4,6 +4,8 @@ import { resolveBeatSourceTrack } from "../utils/timelineInspector"; import { analyzeMusicFromUrl } from "@hyperframes/core/beats"; import { useFileManagerContextOptional } from "../contexts/FileManagerContext"; import { mergeUserBeats } from "../utils/beatEditing"; +import { getTimelineElementIndexes } from "../player/lib/timelineElementIndexes"; +import { remapBeatAnalysisToComposition } from "../utils/beatEditActions"; import { audioRelPathForSrc, beatFilePathForSrc, @@ -90,6 +92,17 @@ async function loadBeatAnalysis( } } +/** The current beat analysis, remapped onto the composition's edited beat grid. */ +export function useAdjustedBeatAnalysis() { + const beatAnalysis = usePlayerStore((s) => s.beatAnalysis); + const musicElement = usePlayerStore((s) => getTimelineElementIndexes(s.elements).musicElement); + const beatEdits = usePlayerStore((s) => s.beatEdits); + return useMemo( + () => remapBeatAnalysisToComposition(beatAnalysis, musicElement, beatEdits), + [beatAnalysis, musicElement, beatEdits], + ); +} + export function useMusicBeatAnalysis(): void { const elements = usePlayerStore((s) => s.elements); const setBeatAnalysis = usePlayerStore((s) => s.setBeatAnalysis); diff --git a/packages/studio/src/player/components/LayerDisclosureRow.tsx b/packages/studio/src/player/components/LayerDisclosureRow.tsx index 4dd05436a7..1dc27408dc 100644 --- a/packages/studio/src/player/components/LayerDisclosureRow.tsx +++ b/packages/studio/src/player/components/LayerDisclosureRow.tsx @@ -1,9 +1,11 @@ -import { CaretRight } from "@phosphor-icons/react"; import { TRACK_H } from "./timelineLayout"; import { TrackClipCount } from "./TrackClipCount"; -// Layer row (Figma order: disclosure ▸/▾, diamond, name) — the disclosure lives -// here, not on the clip bar, and re-expands a collapsed layer. +// Layer row (Figma order: disclosure ∿, diamond, name) — the disclosure lives +// here, not on the clip bar, and re-expands a collapsed layer. `∿` (not a +// caret) because a group's own row keeps the caret for its structural +// disclosure (member rows) — this button only ever means "show this row's +// lanes", so it needs its own distinct glyph. export function LayerDisclosureRow({ name, clipCount, @@ -49,23 +51,20 @@ export function LayerDisclosureRow({ tabIndex={-1} aria-expanded={isExpanded} aria-controls={lanesId} - aria-label={`${isExpanded ? "Collapse" : "Expand"} ${name} keyframes`} - title={`${isExpanded ? "Collapse" : "Expand"} keyframe lanes`} - // h-6 w-6 = the 24x24 WCAG 2.2 minimum target. The caret glyph stays 11px; + aria-label={`${isExpanded ? "Hide" : "Show"} ${name} lanes`} + title={`${isExpanded ? "Hide" : "Show"} lanes`} + // h-6 w-6 = the 24x24 WCAG 2.2 minimum target. The glyph stays 11px; // only the hit box grows. - className="flex h-6 w-6 shrink-0 items-center justify-center rounded border-0 bg-transparent p-0 text-white/55 hover:text-white focus-visible:outline focus-visible:outline-1 focus-visible:outline-[#3CE6AC]" + className={`flex h-6 w-6 shrink-0 items-center justify-center rounded border-0 bg-transparent p-0 text-[11px] leading-none focus-visible:outline focus-visible:outline-1 focus-visible:outline-[#3CE6AC] ${ + isExpanded ? "text-[#3CE6AC]" : "text-white/55 hover:text-white" + }`} onPointerDown={(event) => event.stopPropagation()} onClick={(event) => { event.stopPropagation(); onToggleClipExpanded(); }} > -