Spaces:
Sleeping
Sleeping
Sync from GitHub via hub-sync
Browse files
src/app/[org]/[dataset]/[episode]/episode-viewer.tsx
CHANGED
|
@@ -335,7 +335,7 @@ function EpisodeViewerInner({
|
|
| 335 |
};
|
| 336 |
|
| 337 |
// Use context for time sync
|
| 338 |
-
const { currentTime,
|
| 339 |
|
| 340 |
// URDFViewer episode changer and play toggle — populated by URDFViewer on mount
|
| 341 |
const urdfChangerRef = useRef<((ep: number) => void) | undefined>(undefined);
|
|
@@ -383,10 +383,10 @@ function EpisodeViewerInner({
|
|
| 383 |
if (timeParam) {
|
| 384 |
const timeValue = parseFloat(timeParam);
|
| 385 |
if (!isNaN(timeValue)) {
|
| 386 |
-
|
| 387 |
}
|
| 388 |
}
|
| 389 |
-
}, [searchParams,
|
| 390 |
|
| 391 |
// sync with parent window hf.co/spaces
|
| 392 |
useEffect(() => {
|
|
|
|
| 335 |
};
|
| 336 |
|
| 337 |
// Use context for time sync
|
| 338 |
+
const { currentTime, seek, setIsPlaying, isPlaying } = useTime();
|
| 339 |
|
| 340 |
// URDFViewer episode changer and play toggle — populated by URDFViewer on mount
|
| 341 |
const urdfChangerRef = useRef<((ep: number) => void) | undefined>(undefined);
|
|
|
|
| 383 |
if (timeParam) {
|
| 384 |
const timeValue = parseFloat(timeParam);
|
| 385 |
if (!isNaN(timeValue)) {
|
| 386 |
+
seek(timeValue);
|
| 387 |
}
|
| 388 |
}
|
| 389 |
+
}, [searchParams, seek]);
|
| 390 |
|
| 391 |
// sync with parent window hf.co/spaces
|
| 392 |
useEffect(() => {
|
src/components/data-recharts.tsx
CHANGED
|
@@ -155,7 +155,7 @@ const SingleDataGraph = React.memo(
|
|
| 155 |
setHoveredTime: (t: number | null) => void;
|
| 156 |
tall?: boolean;
|
| 157 |
}) => {
|
| 158 |
-
const { currentTime,
|
| 159 |
const flattenRow = useCallback(
|
| 160 |
(row: Record<string, number | Record<string, number>>, prefix = "") => {
|
| 161 |
const result: Record<string, number> = {};
|
|
@@ -246,7 +246,7 @@ const SingleDataGraph = React.memo(
|
|
| 246 |
data: { activePayload?: { payload: { timestamp: number } }[] } | null,
|
| 247 |
) => {
|
| 248 |
if (data?.activePayload?.length) {
|
| 249 |
-
|
| 250 |
}
|
| 251 |
};
|
| 252 |
|
|
|
|
| 155 |
setHoveredTime: (t: number | null) => void;
|
| 156 |
tall?: boolean;
|
| 157 |
}) => {
|
| 158 |
+
const { currentTime, seek } = useTime();
|
| 159 |
const flattenRow = useCallback(
|
| 160 |
(row: Record<string, number | Record<string, number>>, prefix = "") => {
|
| 161 |
const result: Record<string, number> = {};
|
|
|
|
| 246 |
data: { activePayload?: { payload: { timestamp: number } }[] } | null,
|
| 247 |
) => {
|
| 248 |
if (data?.activePayload?.length) {
|
| 249 |
+
seek(data.activePayload[0].payload.timestamp);
|
| 250 |
}
|
| 251 |
};
|
| 252 |
|
src/components/playback-bar.tsx
CHANGED
|
@@ -11,8 +11,7 @@ import {
|
|
| 11 |
} from "react-icons/fa";
|
| 12 |
|
| 13 |
const PlaybackBar: React.FC = () => {
|
| 14 |
-
const { duration, isPlaying, setIsPlaying, currentTime,
|
| 15 |
-
useTime();
|
| 16 |
|
| 17 |
const sliderActiveRef = React.useRef(false);
|
| 18 |
const wasPlayingRef = React.useRef(false);
|
|
@@ -29,7 +28,7 @@ const PlaybackBar: React.FC = () => {
|
|
| 29 |
const t = Number(e.target.value);
|
| 30 |
setSliderValue(t);
|
| 31 |
// Seek videos immediately while dragging (no debounce)
|
| 32 |
-
|
| 33 |
};
|
| 34 |
|
| 35 |
const handleSliderMouseDown = () => {
|
|
@@ -41,7 +40,7 @@ const PlaybackBar: React.FC = () => {
|
|
| 41 |
const handleSliderMouseUp = () => {
|
| 42 |
sliderActiveRef.current = false;
|
| 43 |
// Final seek to exact slider position
|
| 44 |
-
|
| 45 |
if (wasPlayingRef.current) {
|
| 46 |
setIsPlaying(true);
|
| 47 |
}
|
|
@@ -51,7 +50,7 @@ const PlaybackBar: React.FC = () => {
|
|
| 51 |
<div className="sticky bottom-0 mt-auto w-full max-w-4xl mx-auto flex items-center gap-3 panel-raised bg-[var(--surface-0)]/90 backdrop-blur px-3 py-2">
|
| 52 |
<button
|
| 53 |
title="Jump backward 5 seconds"
|
| 54 |
-
onClick={() =>
|
| 55 |
className="hidden md:flex h-8 w-8 items-center justify-center rounded-md text-slate-400 hover:text-slate-100 hover:bg-white/5 transition-colors"
|
| 56 |
>
|
| 57 |
<FaBackward size={14} />
|
|
@@ -67,14 +66,14 @@ const PlaybackBar: React.FC = () => {
|
|
| 67 |
</button>
|
| 68 |
<button
|
| 69 |
title="Jump forward 5 seconds"
|
| 70 |
-
onClick={() =>
|
| 71 |
className="hidden md:flex h-8 w-8 items-center justify-center rounded-md text-slate-400 hover:text-slate-100 hover:bg-white/5 transition-colors"
|
| 72 |
>
|
| 73 |
<FaForward size={14} />
|
| 74 |
</button>
|
| 75 |
<button
|
| 76 |
title="Rewind from start"
|
| 77 |
-
onClick={() =>
|
| 78 |
className="hidden md:flex h-8 w-8 items-center justify-center rounded-md text-slate-400 hover:text-slate-100 hover:bg-white/5 transition-colors"
|
| 79 |
>
|
| 80 |
<FaUndoAlt size={14} />
|
|
|
|
| 11 |
} from "react-icons/fa";
|
| 12 |
|
| 13 |
const PlaybackBar: React.FC = () => {
|
| 14 |
+
const { duration, isPlaying, setIsPlaying, currentTime, seek } = useTime();
|
|
|
|
| 15 |
|
| 16 |
const sliderActiveRef = React.useRef(false);
|
| 17 |
const wasPlayingRef = React.useRef(false);
|
|
|
|
| 28 |
const t = Number(e.target.value);
|
| 29 |
setSliderValue(t);
|
| 30 |
// Seek videos immediately while dragging (no debounce)
|
| 31 |
+
seek(t);
|
| 32 |
};
|
| 33 |
|
| 34 |
const handleSliderMouseDown = () => {
|
|
|
|
| 40 |
const handleSliderMouseUp = () => {
|
| 41 |
sliderActiveRef.current = false;
|
| 42 |
// Final seek to exact slider position
|
| 43 |
+
seek(sliderValue);
|
| 44 |
if (wasPlayingRef.current) {
|
| 45 |
setIsPlaying(true);
|
| 46 |
}
|
|
|
|
| 50 |
<div className="sticky bottom-0 mt-auto w-full max-w-4xl mx-auto flex items-center gap-3 panel-raised bg-[var(--surface-0)]/90 backdrop-blur px-3 py-2">
|
| 51 |
<button
|
| 52 |
title="Jump backward 5 seconds"
|
| 53 |
+
onClick={() => seek(Math.max(0, currentTime - 5))}
|
| 54 |
className="hidden md:flex h-8 w-8 items-center justify-center rounded-md text-slate-400 hover:text-slate-100 hover:bg-white/5 transition-colors"
|
| 55 |
>
|
| 56 |
<FaBackward size={14} />
|
|
|
|
| 66 |
</button>
|
| 67 |
<button
|
| 68 |
title="Jump forward 5 seconds"
|
| 69 |
+
onClick={() => seek(Math.min(duration, currentTime + 5))}
|
| 70 |
className="hidden md:flex h-8 w-8 items-center justify-center rounded-md text-slate-400 hover:text-slate-100 hover:bg-white/5 transition-colors"
|
| 71 |
>
|
| 72 |
<FaForward size={14} />
|
| 73 |
</button>
|
| 74 |
<button
|
| 75 |
title="Rewind from start"
|
| 76 |
+
onClick={() => seek(0)}
|
| 77 |
className="hidden md:flex h-8 w-8 items-center justify-center rounded-md text-slate-400 hover:text-slate-100 hover:bg-white/5 transition-colors"
|
| 78 |
>
|
| 79 |
<FaUndoAlt size={14} />
|
src/components/simple-videos-player.tsx
CHANGED
|
@@ -24,13 +24,8 @@ export const SimpleVideosPlayer = ({
|
|
| 24 |
videosInfo,
|
| 25 |
onVideosReady,
|
| 26 |
}: VideoPlayerProps) => {
|
| 27 |
-
const {
|
| 28 |
-
|
| 29 |
-
setCurrentTime,
|
| 30 |
-
externalSeekVersion,
|
| 31 |
-
isPlaying,
|
| 32 |
-
setIsPlaying,
|
| 33 |
-
} = useTime();
|
| 34 |
const videoRefs = useRef<(HTMLVideoElement | null)[]>([]);
|
| 35 |
const [hiddenVideos, setHiddenVideos] = React.useState<string[]>([]);
|
| 36 |
const [enlargedVideo, setEnlargedVideo] = React.useState<string | null>(null);
|
|
@@ -101,7 +96,7 @@ export const SimpleVideosPlayer = ({
|
|
| 101 |
});
|
| 102 |
// Update the slider as a status report — don't bump externalSeekVersion
|
| 103 |
// since we already drove every video to its target.
|
| 104 |
-
|
| 105 |
};
|
| 106 |
|
| 107 |
videoRefs.current.forEach((video, index) => {
|
|
@@ -135,7 +130,7 @@ export const SimpleVideosPlayer = ({
|
|
| 135 |
if (info.isSegmented) {
|
| 136 |
globalTime = video.currentTime - (info.segmentStart ?? 0);
|
| 137 |
}
|
| 138 |
-
|
| 139 |
}
|
| 140 |
};
|
| 141 |
|
|
@@ -221,7 +216,7 @@ export const SimpleVideosPlayer = ({
|
|
| 221 |
// firstVisibleIdx intentionally omitted — we read it via ref so hiding
|
| 222 |
// the first camera doesn't reset readiness (see the comment near
|
| 223 |
// firstVisibleIdxRef above).
|
| 224 |
-
}, [videosInfo, onVideosReady, setIsPlaying,
|
| 225 |
|
| 226 |
// Handle play/pause — skip hidden videos
|
| 227 |
useEffect(() => {
|
|
|
|
| 24 |
videosInfo,
|
| 25 |
onVideosReady,
|
| 26 |
}: VideoPlayerProps) => {
|
| 27 |
+
const { currentTime, seek, externalSeekVersion, isPlaying, setIsPlaying } =
|
| 28 |
+
useTime();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 29 |
const videoRefs = useRef<(HTMLVideoElement | null)[]>([]);
|
| 30 |
const [hiddenVideos, setHiddenVideos] = React.useState<string[]>([]);
|
| 31 |
const [enlargedVideo, setEnlargedVideo] = React.useState<string | null>(null);
|
|
|
|
| 96 |
});
|
| 97 |
// Update the slider as a status report — don't bump externalSeekVersion
|
| 98 |
// since we already drove every video to its target.
|
| 99 |
+
seek(0, "video");
|
| 100 |
};
|
| 101 |
|
| 102 |
videoRefs.current.forEach((video, index) => {
|
|
|
|
| 130 |
if (info.isSegmented) {
|
| 131 |
globalTime = video.currentTime - (info.segmentStart ?? 0);
|
| 132 |
}
|
| 133 |
+
seek(globalTime, "video");
|
| 134 |
}
|
| 135 |
};
|
| 136 |
|
|
|
|
| 216 |
// firstVisibleIdx intentionally omitted — we read it via ref so hiding
|
| 217 |
// the first camera doesn't reset readiness (see the comment near
|
| 218 |
// firstVisibleIdxRef above).
|
| 219 |
+
}, [videosInfo, onVideosReady, setIsPlaying, seek]);
|
| 220 |
|
| 221 |
// Handle play/pause — skip hidden videos
|
| 222 |
useEffect(() => {
|
src/components/videos-player.tsx
CHANGED
|
@@ -19,7 +19,7 @@ export const VideosPlayer = ({
|
|
| 19 |
videosInfo,
|
| 20 |
onVideosReady,
|
| 21 |
}: VideoPlayerProps) => {
|
| 22 |
-
const { currentTime,
|
| 23 |
const videoRefs = useRef<HTMLVideoElement[]>([]);
|
| 24 |
const [hiddenVideos, setHiddenVideos] = useState<string[]>([]);
|
| 25 |
|
|
@@ -184,14 +184,14 @@ export const VideosPlayer = ({
|
|
| 184 |
const segmentStart = videoInfo.segmentStart || 0;
|
| 185 |
const globalTime = Math.max(0, video.currentTime - segmentStart);
|
| 186 |
lastVideoTimeRef.current = globalTime;
|
| 187 |
-
|
| 188 |
} else {
|
| 189 |
lastVideoTimeRef.current = video.currentTime;
|
| 190 |
-
|
| 191 |
}
|
| 192 |
};
|
| 193 |
},
|
| 194 |
-
[videosInfo,
|
| 195 |
);
|
| 196 |
|
| 197 |
// Handle video ready and setup segmentation
|
|
|
|
| 19 |
videosInfo,
|
| 20 |
onVideosReady,
|
| 21 |
}: VideoPlayerProps) => {
|
| 22 |
+
const { currentTime, seek, isPlaying, setIsPlaying } = useTime();
|
| 23 |
const videoRefs = useRef<HTMLVideoElement[]>([]);
|
| 24 |
const [hiddenVideos, setHiddenVideos] = useState<string[]>([]);
|
| 25 |
|
|
|
|
| 184 |
const segmentStart = videoInfo.segmentStart || 0;
|
| 185 |
const globalTime = Math.max(0, video.currentTime - segmentStart);
|
| 186 |
lastVideoTimeRef.current = globalTime;
|
| 187 |
+
seek(globalTime);
|
| 188 |
} else {
|
| 189 |
lastVideoTimeRef.current = video.currentTime;
|
| 190 |
+
seek(video.currentTime);
|
| 191 |
}
|
| 192 |
};
|
| 193 |
},
|
| 194 |
+
[videosInfo, seek],
|
| 195 |
);
|
| 196 |
|
| 197 |
// Handle video ready and setup segmentation
|
src/context/time-context.tsx
CHANGED
|
@@ -19,7 +19,7 @@ type TimeUpdateSource = "external" | "video";
|
|
| 19 |
|
| 20 |
type TimeContextType = {
|
| 21 |
currentTime: number;
|
| 22 |
-
|
| 23 |
// Monotonically increasing counter that bumps on every `external` seek.
|
| 24 |
// Sync effects compare the current value against a stored ref to detect
|
| 25 |
// user-initiated seeks without relying on heuristics like "did the time
|
|
@@ -107,7 +107,7 @@ export const TimeProvider: React.FC<{
|
|
| 107 |
<TimeContext.Provider
|
| 108 |
value={{
|
| 109 |
currentTime,
|
| 110 |
-
|
| 111 |
externalSeekVersion,
|
| 112 |
subscribe,
|
| 113 |
isPlaying,
|
|
|
|
| 19 |
|
| 20 |
type TimeContextType = {
|
| 21 |
currentTime: number;
|
| 22 |
+
seek: (t: number, source?: TimeUpdateSource) => void;
|
| 23 |
// Monotonically increasing counter that bumps on every `external` seek.
|
| 24 |
// Sync effects compare the current value against a stored ref to detect
|
| 25 |
// user-initiated seeks without relying on heuristics like "did the time
|
|
|
|
| 107 |
<TimeContext.Provider
|
| 108 |
value={{
|
| 109 |
currentTime,
|
| 110 |
+
seek: updateTime,
|
| 111 |
externalSeekVersion,
|
| 112 |
subscribe,
|
| 113 |
isPlaying,
|