-
Notifications
You must be signed in to change notification settings - Fork 1
Implement Timeline Visualization in GUI #63
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
64b2feb
c6c0055
f6132f7
79f0003
fb8bf1b
e2a7a24
2e3317e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -14,16 +14,22 @@ | |
| }); | ||
|
|
||
| // Enhanced GUI app with comprehensive features | ||
| struct TimeLoopGui { | ||
| // AI communication | ||
| ai_sender: Sender<Result<String, String>>, | ||
| ai_receiver: Receiver<Result<String, String>>, | ||
| // Timeline visualization constants | ||
| const TIMELINE_HEIGHT: f32 = 60.0; | ||
| const TIMELINE_BG_COLOR: egui::Color32 = egui::Color32::from_rgb(30, 30, 30); | ||
| const COLOR_COMMAND: egui::Color32 = egui::Color32::from_rgb(100, 149, 237); // Cornflower Blue | ||
| const COLOR_FILE_CHANGE: egui::Color32 = egui::Color32::from_rgb(255, 99, 71); // Tomato Red | ||
| const COLOR_TERMINAL: egui::Color32 = egui::Color32::from_rgb(100, 100, 100); | ||
| const COLOR_KEYPRESS: egui::Color32 = egui::Color32::from_rgb(60, 60, 60); | ||
| const COLOR_METADATA: egui::Color32 = egui::Color32::WHITE; | ||
|
|
||
| struct TimeLoopGui { | ||
| // Session management | ||
| session_manager: Option<SessionManager>, | ||
| sessions: Vec<timeloop_terminal::session::Session>, | ||
| selected: Option<String>, | ||
| replay_summary: Option<timeloop_terminal::replay::ReplaySummary>, | ||
| replay_events: Vec<Event>, | ||
|
|
||
| // Replay controls | ||
| playing: bool, | ||
|
|
@@ -60,7 +66,6 @@ | |
|
|
||
| impl Default for TimeLoopGui { | ||
| fn default() -> Self { | ||
| let (tx, rx) = std::sync::mpsc::channel(); | ||
| let mut sessions = Vec::new(); | ||
| let session_manager = SessionManager::new().ok(); | ||
|
|
||
|
|
@@ -82,6 +87,7 @@ | |
| sessions, | ||
| selected: None, | ||
| replay_summary: None, | ||
| replay_events: Vec::new(), | ||
| playing: false, | ||
| speed: 1.0, | ||
| position_ms: 0, | ||
|
|
@@ -413,9 +419,16 @@ | |
|
|
||
| // Load replay summary | ||
| if let Ok(engine) = ReplayEngine::new(session_id) { | ||
| if let Ok(rs) = engine.get_session_summary() { | ||
| self.replay_summary = Some(rs); | ||
| if let Ok(events) = engine.get_events() { | ||
| self.replay_summary = Some(ReplayEngine::calculate_summary(&events)); | ||
| self.replay_events = events; | ||
| } else { | ||
| self.replay_events.clear(); | ||
| self.replay_summary = None; | ||
| } | ||
| } else { | ||
| self.replay_events.clear(); | ||
| self.replay_summary = None; | ||
| } | ||
|
Comment on lines
421
to
432
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This block for loading session data has a significant performance issue and can be made more robust. Performance: Robustness: The error handling can lead to an inconsistent UI state and can be simplified. The suggested change improves robustness, but the performance issue will remain until if let Ok(engine) = ReplayEngine::new(session_id) {
if let Ok(events) = engine.get_events() {
self.replay_summary = engine.get_session_summary().ok();
self.replay_events = events;
} else {
self.replay_events.clear();
self.replay_summary = None;
}
} else {
self.replay_events.clear();
self.replay_summary = None;
} |
||
| } | ||
|
|
||
|
|
@@ -639,8 +652,80 @@ | |
| // Timeline visualization | ||
| ui.group(|ui| { | ||
| ui.heading("📈 Timeline"); | ||
| ui.label("Event timeline visualization would go here"); | ||
| // TODO: Implement actual timeline visualization | ||
| let (rect, _response) = ui.allocate_exact_size( | ||
| egui::vec2(ui.available_width(), TIMELINE_HEIGHT), | ||
| egui::Sense::hover(), | ||
| ); | ||
|
|
||
| // Draw timeline background | ||
| ui.painter().rect_filled(rect, 4.0, TIMELINE_BG_COLOR); | ||
|
|
||
| let total_duration_ms = rs.duration.num_milliseconds() as f64; | ||
|
|
||
| if total_duration_ms > 0.0 { | ||
| if let Some(first_event) = self.replay_events.first() { | ||
| let start_time = first_event.timestamp; | ||
|
|
||
| // Draw events | ||
| for event in &self.replay_events { | ||
| let offset_ms = (event.timestamp - start_time).num_milliseconds() as f64; | ||
| let t = (offset_ms / total_duration_ms) as f32; | ||
| // Clamp t to [0.0, 1.0] to handle potential slight time skews | ||
| let t = t.clamp(0.0, 1.0); | ||
| let x = rect.min.x + t * rect.width(); | ||
|
|
||
| let (color, height_fraction, y_offset) = match event.event_type { | ||
| EventType::Command { .. } => (COLOR_COMMAND, 0.8, 0.1), | ||
Check failureCode scanning / clippy failed to resolve: use of undeclared type EventType Error
failed to resolve: use of undeclared type EventType
|
||
| EventType::FileChange { .. } => (COLOR_FILE_CHANGE, 0.8, 0.1), | ||
Check failureCode scanning / clippy failed to resolve: use of undeclared type EventType Error
failed to resolve: use of undeclared type EventType
|
||
| EventType::TerminalState { .. } => (COLOR_TERMINAL, 0.4, 0.3), | ||
Check failureCode scanning / clippy failed to resolve: use of undeclared type EventType Error
failed to resolve: use of undeclared type EventType
|
||
| EventType::KeyPress { .. } => (COLOR_KEYPRESS, 0.2, 0.4), | ||
Check failureCode scanning / clippy failed to resolve: use of undeclared type EventType Error
failed to resolve: use of undeclared type EventType
|
||
| EventType::SessionMetadata { .. } => (COLOR_METADATA, 0.5, 0.25), | ||
Check failureCode scanning / clippy failed to resolve: use of undeclared type EventType Error
failed to resolve: use of undeclared type EventType
|
||
| }; | ||
|
Comment on lines
+677
to
+683
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The colors and layout properties for different event types are hardcoded as magic values within this |
||
|
|
||
| let y_start = rect.min.y + rect.height() * y_offset; | ||
| let y_end = y_start + rect.height() * height_fraction; | ||
|
|
||
| // Use thinner lines for keypresses to avoid clutter | ||
| let stroke_width = if matches!(event.event_type, EventType::KeyPress { .. }) { | ||
Check failureCode scanning / clippy failed to resolve: use of undeclared type EventType Error
failed to resolve: use of undeclared type EventType
|
||
| 1.0 | ||
| } else { | ||
| 2.0 | ||
| }; | ||
|
|
||
| ui.painter().line_segment( | ||
| [egui::pos2(x, y_start), egui::pos2(x, y_end)], | ||
| egui::Stroke::new(stroke_width, color), | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| // Draw playback position indicator | ||
| let playback_t = (self.position_ms as f64 / total_duration_ms) as f32; | ||
| let playback_t = playback_t.clamp(0.0, 1.0); | ||
| let cursor_x = rect.min.x + playback_t * rect.width(); | ||
|
|
||
| ui.painter().line_segment( | ||
| [egui::pos2(cursor_x, rect.min.y), egui::pos2(cursor_x, rect.max.y)], | ||
| egui::Stroke::new(2.0, egui::Color32::WHITE), | ||
| ); | ||
|
|
||
| // Draw cursor triangle/head | ||
| ui.painter().circle_filled( | ||
| egui::pos2(cursor_x, rect.min.y), | ||
| 4.0, | ||
| egui::Color32::WHITE, | ||
| ); | ||
| } else { | ||
| ui.label("Session duration is zero, cannot display timeline."); | ||
| } | ||
|
|
||
| // Legend | ||
| ui.horizontal(|ui| { | ||
| ui.label("Legend:"); | ||
| ui.colored_label(COLOR_COMMAND, "Command"); | ||
| ui.colored_label(COLOR_FILE_CHANGE, "File Change"); | ||
| ui.colored_label(COLOR_TERMINAL, "Terminal State"); | ||
| }); | ||
|
nur-srijan marked this conversation as resolved.
|
||
| }); | ||
|
|
||
| } else { | ||
|
|
||
Check failure
Code scanning / clippy
cannot find type Event in this scope Error