Skip to content

Commit 16d343d

Browse files
Merge pull request #792 from func25/fix/layer-range-seek
2 parents 9c954be + 039c2b4 commit 16d343d

3 files changed

Lines changed: 49 additions & 5 deletions

File tree

packages/studio/src/components/editor/LayersPanel.tsx

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@ import {
88
import { useStudioContext } from "../../contexts/StudioContext";
99
import { useDomEditContext } from "../../contexts/DomEditContext";
1010
import { usePlayerStore } from "../../player";
11-
import { findMatchingTimelineElementId } from "../../utils/studioHelpers";
11+
import {
12+
findMatchingTimelineElementId,
13+
resolveTimelineSelectionSeekTime,
14+
} from "../../utils/studioHelpers";
1215
import { Layers } from "../../icons/SystemIcons";
1316

1417
const TAG_ICONS: Record<string, string> = {
@@ -49,8 +52,14 @@ interface CollapsedState {
4952
}
5053

5154
export const LayersPanel = memo(function LayersPanel() {
52-
const { previewIframeRef, activeCompPath, refreshKey, compositionLoading, timelineElements } =
53-
useStudioContext();
55+
const {
56+
previewIframeRef,
57+
activeCompPath,
58+
refreshKey,
59+
compositionLoading,
60+
timelineElements,
61+
currentTime,
62+
} = useStudioContext();
5463
const { domEditSelection, applyDomSelection, updateDomEditHoverSelection } = useDomEditContext();
5564

5665
const [layers, setLayers] = useState<DomEditLayerItem[]>([]);
@@ -140,11 +149,12 @@ export const LayersPanel = memo(function LayersPanel() {
140149
if (matchedId) {
141150
const el = timelineElements.find((e) => (e.key ?? e.id) === matchedId);
142151
if (el) {
143-
usePlayerStore.getState().requestSeek(el.start + el.duration / 2);
152+
const nextTime = resolveTimelineSelectionSeekTime(currentTime, el);
153+
if (nextTime != null) usePlayerStore.getState().requestSeek(nextTime);
144154
}
145155
}
146156
},
147-
[resolveSelection, timelineElements],
157+
[currentTime, resolveSelection, timelineElements],
148158
);
149159

150160
const handleSelectLayer = useCallback(
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { describe, expect, it } from "vitest";
2+
import { resolveTimelineSelectionSeekTime } from "./studioHelpers";
3+
4+
describe("resolveTimelineSelectionSeekTime", () => {
5+
it("keeps the current time when it is already inside the clip range", () => {
6+
expect(resolveTimelineSelectionSeekTime(3, { start: 0, duration: 5 })).toBe(3);
7+
});
8+
9+
it("clamps to the clip start when current time is before the clip", () => {
10+
expect(resolveTimelineSelectionSeekTime(1, { start: 4, duration: 3 })).toBe(4);
11+
});
12+
13+
it("clamps to the clip end when current time is after the clip", () => {
14+
expect(resolveTimelineSelectionSeekTime(10, { start: 4, duration: 3 })).toBe(7);
15+
});
16+
17+
it("falls back to the clip start for invalid current time", () => {
18+
expect(resolveTimelineSelectionSeekTime(Number.NaN, { start: 2, duration: 5 })).toBe(2);
19+
});
20+
});

packages/studio/src/utils/studioHelpers.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,20 @@ export function findMatchingTimelineElementId(
158158
return null;
159159
}
160160

161+
export function resolveTimelineSelectionSeekTime(
162+
currentTime: number,
163+
element: Pick<TimelineElement, "start" | "duration"> | null | undefined,
164+
): number | null {
165+
if (!element) return null;
166+
if (!Number.isFinite(element.start) || !Number.isFinite(element.duration)) return null;
167+
168+
const start = Math.max(0, element.start);
169+
const end = Math.max(start, start + Math.max(0, element.duration));
170+
const time = Number.isFinite(currentTime) ? currentTime : start;
171+
172+
return clampNumber(time, start, end);
173+
}
174+
161175
export function clampNumber(value: number, min: number, max: number): number {
162176
if (max < min) return min;
163177
return Math.min(Math.max(value, min), max);

0 commit comments

Comments
 (0)