Skip to content

Commit f337dc7

Browse files
committed
Integrate timeline into Trace panel and filters
Wire TracePanel to the global timeline context so trace data respect timeline source and paused time. Import useTimeline and pass source into useTraceBuffer; apply timelineMode/selectedTimeMs filtering (when paused) to visible frames and add those values to the effect deps. Simplify Trace page pause handling by removing the frozenRef snapshot logic and instead freezing the cursor via seek to collection end when pausing; update filteredFrames to use the canonical frames array and adjust its deps accordingly. These changes ensure the trace list follows the global timeline source and paused time and simplify state handling.
1 parent fa05730 commit f337dc7

2 files changed

Lines changed: 15 additions & 20 deletions

File tree

pecan/src/components/TracePanel.tsx

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { useTraceBuffer } from "../lib/useDataStore";
33
import type { TelemetrySample } from "../lib/DataStore";
44
import TourGuide, { type TourStep } from "./TourGuide";
55
import { Play, Pause, Trash2, X, HelpCircle } from "lucide-react";
6+
import { useTimeline } from "../context/TimelineContext";
67

78
type DirectionFilter = "all" | "rx" | "tx";
89

@@ -72,7 +73,8 @@ export default function TracePanel({
7273
onClose,
7374
initialOffset = { x: 0, y: 0 },
7475
}: TracePanelProps) {
75-
const { frames, clearTrace } = useTraceBuffer(100);
76+
const { source, mode: timelineMode, selectedTimeMs } = useTimeline();
77+
const { frames, clearTrace } = useTraceBuffer(100, source);
7678
const [open, setOpen] = useState(true);
7779
const [position, setPosition] = useState({ top: 80, left: 80 });
7880
const [size, setSize] = useState({ width: DEFAULT_WIDTH, height: DEFAULT_HEIGHT });
@@ -102,10 +104,14 @@ export default function TracePanel({
102104
const activeSource = paused ? snapshot : frames;
103105

104106
const visible = useMemo(() => {
107+
const timelinePaused = timelineMode === "paused";
108+
const timeFiltered = timelinePaused
109+
? activeSource.filter((f) => f.timestamp <= selectedTimeMs)
110+
: activeSource;
105111
let filtered =
106112
direction === "all"
107-
? activeSource
108-
: activeSource.filter((f) => (f.direction ?? "rx") === direction);
113+
? timeFiltered
114+
: timeFiltered.filter((f) => (f.direction ?? "rx") === direction);
109115
if (filter) {
110116
const term = filter.trim().toLowerCase();
111117
filtered = filtered.filter(
@@ -115,7 +121,7 @@ export default function TracePanel({
115121
);
116122
}
117123
return filtered.slice(-maxRows);
118-
}, [activeSource, direction, maxRows, filter]);
124+
}, [activeSource, direction, maxRows, filter, timelineMode, selectedTimeMs]);
119125

120126
const handlePause = () => {
121127
if (!paused) {

pecan/src/pages/Trace.tsx

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -434,14 +434,10 @@ function Trace() {
434434
const [tourOpen, setTourOpen] = useState(false);
435435
const [currentTourStep, setCurrentTourStep] = useState(0);
436436

437-
// Frozen copy while paused
438-
const frozenRef = useRef<TelemetrySample[]>([]);
439-
const activeFrames = paused ? frozenRef.current : frames;
440-
441437
// Easter Egg State
442438
const [showRaceGame, setShowRaceGame] = useState(false);
443439

444-
// Freeze on pause
440+
// Pause: freeze the cursor at the latest data point
445441
const handlePause = useCallback(() => {
446442
if (replayLocked) {
447443
return;
@@ -452,15 +448,8 @@ function Trace() {
452448
return;
453449
}
454450

455-
frozenRef.current = [...frames];
456451
seek(collectionEndMs ?? Date.now());
457-
}, [paused, replayLocked, goLive, frames, seek, collectionEndMs]);
458-
459-
useEffect(() => {
460-
if (paused && frozenRef.current.length === 0) {
461-
frozenRef.current = [...frames];
462-
}
463-
}, [paused, frames]);
452+
}, [paused, replayLocked, goLive, seek, collectionEndMs]);
464453

465454
// Re-enable auto-scroll when unpausing
466455
useEffect(() => {
@@ -486,8 +475,8 @@ function Trace() {
486475
// Filter logic: match CAN ID or message name (comma-separated terms)
487476
const filteredFrames = useMemo(() => {
488477
const timelineFrames = paused
489-
? activeFrames.filter((frame) => frame.timestamp <= selectedTimeMs)
490-
: activeFrames;
478+
? frames.filter((frame) => frame.timestamp <= selectedTimeMs)
479+
: frames;
491480

492481
const terms = filter
493482
.split(",")
@@ -499,7 +488,7 @@ function Trace() {
499488
const name = f.messageName.toLowerCase();
500489
return terms.some((t) => id.includes(t) || name.includes(t));
501490
});
502-
}, [activeFrames, filter, paused, selectedTimeMs]);
491+
}, [frames, filter, paused, selectedTimeMs]);
503492

504493
const enriched = useMemo(() => buildEnriched(filteredFrames), [filteredFrames]);
505494
const fixed = useMemo(() => enriched, [enriched]);

0 commit comments

Comments
 (0)