mishig HF Staff commited on
Commit
3c85a10
·
verified ·
1 Parent(s): 3e9fdec

Sync from GitHub via hub-sync

Browse files
src/app/[org]/[dataset]/[episode]/episode-viewer.tsx CHANGED
@@ -138,9 +138,16 @@ function EpisodeViewerInner({
138
 
139
  const [videosReady, setVideosReady] = useState(!videosInfo.length);
140
  const [chartsReady, setChartsReady] = useState(false);
141
- const isLoading = !videosReady || !chartsReady;
142
 
143
  const loadStartRef = useRef(performance.now());
 
 
 
 
 
 
 
 
144
  useEffect(() => {
145
  if (!isLoading) {
146
  console.log(
@@ -148,12 +155,6 @@ function EpisodeViewerInner({
148
  );
149
  }
150
  }, [isLoading, videosReady, chartsReady]);
151
-
152
- const router = useRouter();
153
- const searchParams = useSearchParams();
154
-
155
- // Tab state & lazy stats
156
- const [activeTab, setActiveTab] = useState<ActiveTab>("episodes");
157
  const [, setColumnMinMax] = useState<ColumnMinMax[] | null>(null);
158
  const [episodeLengthStats, setEpisodeLengthStats] =
159
  useState<EpisodeLengthStats | null>(null);
@@ -321,6 +322,11 @@ function EpisodeViewerInner({
321
  // Use context for time sync
322
  const { currentTime, setCurrentTime, setIsPlaying, isPlaying } = useTime();
323
 
 
 
 
 
 
324
  // Pagination state
325
  const pageSize = 100;
326
  const [currentPage, setCurrentPage] = useState(1);
@@ -536,18 +542,26 @@ function EpisodeViewerInner({
536
 
537
  {/* Body: sidebar + content */}
538
  <div className="flex flex-1 min-h-0">
539
- {/* Sidebar — only on Episodes tab */}
540
- {activeTab === "episodes" && (
541
  <Sidebar
542
  datasetInfo={datasetInfo}
543
  paginatedEpisodes={paginatedEpisodes}
544
- episodeId={episodeId}
545
  totalPages={totalPages}
546
  currentPage={currentPage}
547
  prevPage={prevPage}
548
  nextPage={nextPage}
549
  showFlaggedOnly={sidebarFlaggedOnly}
550
  onShowFlaggedOnlyChange={setSidebarFlaggedOnly}
 
 
 
 
 
 
 
 
551
  />
552
  )}
553
 
@@ -675,7 +689,12 @@ function EpisodeViewerInner({
675
 
676
  {activeTab === "urdf" && (
677
  <Suspense fallback={<Loading />}>
678
- <URDFViewer data={data} org={org} dataset={dataset} />
 
 
 
 
 
679
  </Suspense>
680
  )}
681
  </div>
 
138
 
139
  const [videosReady, setVideosReady] = useState(!videosInfo.length);
140
  const [chartsReady, setChartsReady] = useState(false);
 
141
 
142
  const loadStartRef = useRef(performance.now());
143
+
144
+ const router = useRouter();
145
+ const searchParams = useSearchParams();
146
+
147
+ // Tab state & lazy stats
148
+ const [activeTab, setActiveTab] = useState<ActiveTab>("episodes");
149
+ const isLoading = activeTab === "episodes" && (!videosReady || !chartsReady);
150
+
151
  useEffect(() => {
152
  if (!isLoading) {
153
  console.log(
 
155
  );
156
  }
157
  }, [isLoading, videosReady, chartsReady]);
 
 
 
 
 
 
158
  const [, setColumnMinMax] = useState<ColumnMinMax[] | null>(null);
159
  const [episodeLengthStats, setEpisodeLengthStats] =
160
  useState<EpisodeLengthStats | null>(null);
 
322
  // Use context for time sync
323
  const { currentTime, setCurrentTime, setIsPlaying, isPlaying } = useTime();
324
 
325
+ // URDFViewer episode changer — populated by URDFViewer on mount
326
+ const urdfChangerRef = useRef<((ep: number) => void) | undefined>(undefined);
327
+ const [urdfEpisode, setUrdfEpisode] = useState(episodeId);
328
+ useEffect(() => setUrdfEpisode(episodeId), [episodeId]);
329
+
330
  // Pagination state
331
  const pageSize = 100;
332
  const [currentPage, setCurrentPage] = useState(1);
 
542
 
543
  {/* Body: sidebar + content */}
544
  <div className="flex flex-1 min-h-0">
545
+ {/* Sidebar — on Episodes and 3D Replay tabs */}
546
+ {(activeTab === "episodes" || activeTab === "urdf") && (
547
  <Sidebar
548
  datasetInfo={datasetInfo}
549
  paginatedEpisodes={paginatedEpisodes}
550
+ episodeId={activeTab === "urdf" ? urdfEpisode : episodeId}
551
  totalPages={totalPages}
552
  currentPage={currentPage}
553
  prevPage={prevPage}
554
  nextPage={nextPage}
555
  showFlaggedOnly={sidebarFlaggedOnly}
556
  onShowFlaggedOnlyChange={setSidebarFlaggedOnly}
557
+ onEpisodeSelect={
558
+ activeTab === "urdf"
559
+ ? (ep) => {
560
+ setUrdfEpisode(ep);
561
+ urdfChangerRef.current?.(ep);
562
+ }
563
+ : undefined
564
+ }
565
  />
566
  )}
567
 
 
689
 
690
  {activeTab === "urdf" && (
691
  <Suspense fallback={<Loading />}>
692
+ <URDFViewer
693
+ data={data}
694
+ org={org}
695
+ dataset={dataset}
696
+ episodeChangerRef={urdfChangerRef}
697
+ />
698
  </Suspense>
699
  )}
700
  </div>
src/components/side-nav.tsx CHANGED
@@ -16,6 +16,7 @@ interface SidebarProps {
16
  nextPage: () => void;
17
  showFlaggedOnly: boolean;
18
  onShowFlaggedOnlyChange: (v: boolean) => void;
 
19
  }
20
 
21
  const Sidebar: React.FC<SidebarProps> = ({
@@ -28,6 +29,7 @@ const Sidebar: React.FC<SidebarProps> = ({
28
  nextPage,
29
  showFlaggedOnly,
30
  onShowFlaggedOnlyChange,
 
31
  }) => {
32
  const [mobileVisible, setMobileVisible] = useState(false);
33
  const { flagged, count, toggle } = useFlaggedEpisodes();
@@ -74,12 +76,21 @@ const Sidebar: React.FC<SidebarProps> = ({
74
  key={episode}
75
  className="mt-0.5 font-mono text-sm flex items-center gap-1"
76
  >
77
- <Link
78
- href={`./episode_${episode}`}
79
- className={`underline ${episode === episodeId ? "-ml-1 font-bold" : ""}`}
80
- >
81
- Episode {episode}
82
- </Link>
 
 
 
 
 
 
 
 
 
83
  <button
84
  onClick={() => toggle(episode)}
85
  className={`text-xs leading-none px-0.5 rounded transition-colors ${
 
16
  nextPage: () => void;
17
  showFlaggedOnly: boolean;
18
  onShowFlaggedOnlyChange: (v: boolean) => void;
19
+ onEpisodeSelect?: (ep: number) => void;
20
  }
21
 
22
  const Sidebar: React.FC<SidebarProps> = ({
 
29
  nextPage,
30
  showFlaggedOnly,
31
  onShowFlaggedOnlyChange,
32
+ onEpisodeSelect,
33
  }) => {
34
  const [mobileVisible, setMobileVisible] = useState(false);
35
  const { flagged, count, toggle } = useFlaggedEpisodes();
 
76
  key={episode}
77
  className="mt-0.5 font-mono text-sm flex items-center gap-1"
78
  >
79
+ {onEpisodeSelect ? (
80
+ <button
81
+ onClick={() => onEpisodeSelect(episode)}
82
+ className={`underline text-left cursor-pointer ${episode === episodeId ? "-ml-1 font-bold text-orange-400" : ""}`}
83
+ >
84
+ Episode {episode}
85
+ </button>
86
+ ) : (
87
+ <Link
88
+ href={`./episode_${episode}`}
89
+ className={`underline ${episode === episodeId ? "-ml-1 font-bold" : ""}`}
90
+ >
91
+ Episode {episode}
92
+ </Link>
93
+ )}
94
  <button
95
  onClick={() => toggle(episode)}
96
  className={`text-xs leading-none px-0.5 rounded transition-colors ${
src/components/urdf-viewer.tsx CHANGED
@@ -445,12 +445,16 @@ export default function URDFViewer({
445
  data,
446
  org,
447
  dataset,
 
448
  }: {
449
  data: EpisodeData;
450
  org?: string;
451
  dataset?: string;
 
 
 
452
  }) {
453
- const { datasetInfo, episodes } = data;
454
  const fps = datasetInfo.fps || 30;
455
  const robotConfig = useMemo(
456
  () => getRobotConfig(datasetInfo.robot_type),
@@ -515,6 +519,10 @@ export default function URDFViewer({
515
  [ensureDatasetInfo, repoId],
516
  );
517
 
 
 
 
 
518
  const totalFrames = chartData.length;
519
 
520
  // URDF joint names
@@ -711,43 +719,8 @@ export default function URDFViewer({
711
 
712
  {/* Controls */}
713
  <div className="bg-slate-800/90 border-t border-slate-700 p-3 space-y-3 shrink-0">
714
- {/* Episode selector + Timeline */}
715
  <div className="flex items-center gap-3">
716
- {/* Episode selector */}
717
- <div className="flex items-center gap-1.5 shrink-0">
718
- <button
719
- onClick={() => {
720
- if (selectedEpisode > episodes[0])
721
- handleEpisodeChange(selectedEpisode - 1);
722
- }}
723
- disabled={selectedEpisode <= episodes[0]}
724
- className="w-6 h-6 flex items-center justify-center rounded bg-slate-700 hover:bg-slate-600 text-slate-300 disabled:opacity-30 disabled:cursor-not-allowed text-xs"
725
- >
726
-
727
- </button>
728
- <select
729
- value={selectedEpisode}
730
- onChange={(e) => handleEpisodeChange(Number(e.target.value))}
731
- className="bg-slate-900 text-slate-200 text-xs rounded px-1.5 py-1 border border-slate-600 w-28"
732
- >
733
- {episodes.map((ep) => (
734
- <option key={ep} value={ep}>
735
- Episode {ep}
736
- </option>
737
- ))}
738
- </select>
739
- <button
740
- onClick={() => {
741
- if (selectedEpisode < episodes[episodes.length - 1])
742
- handleEpisodeChange(selectedEpisode + 1);
743
- }}
744
- disabled={selectedEpisode >= episodes[episodes.length - 1]}
745
- className="w-6 h-6 flex items-center justify-center rounded bg-slate-700 hover:bg-slate-600 text-slate-300 disabled:opacity-30 disabled:cursor-not-allowed text-xs"
746
- >
747
-
748
- </button>
749
- </div>
750
-
751
  {/* Play/Pause */}
752
  <button
753
  onClick={() => {
 
445
  data,
446
  org,
447
  dataset,
448
+ episodeChangerRef,
449
  }: {
450
  data: EpisodeData;
451
  org?: string;
452
  dataset?: string;
453
+ episodeChangerRef?: React.MutableRefObject<
454
+ ((ep: number) => void) | undefined
455
+ >;
456
  }) {
457
+ const { datasetInfo } = data;
458
  const fps = datasetInfo.fps || 30;
459
  const robotConfig = useMemo(
460
  () => getRobotConfig(datasetInfo.robot_type),
 
519
  [ensureDatasetInfo, repoId],
520
  );
521
 
522
+ useEffect(() => {
523
+ if (episodeChangerRef) episodeChangerRef.current = handleEpisodeChange;
524
+ }, [episodeChangerRef, handleEpisodeChange]);
525
+
526
  const totalFrames = chartData.length;
527
 
528
  // URDF joint names
 
719
 
720
  {/* Controls */}
721
  <div className="bg-slate-800/90 border-t border-slate-700 p-3 space-y-3 shrink-0">
722
+ {/* Timeline */}
723
  <div className="flex items-center gap-3">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
724
  {/* Play/Pause */}
725
  <button
726
  onClick={() => {