Repository: vercel/next.js
Pull request: Turbopack: arrange empty space in trace viewer correctly

### What?

Position single spans correctly in the empty space (self time)

Before:

![Screenshot 2025-11-28 at 14.26.24.png](https://app.graphite.com/user-attachments/assets/fc412ac2-7ffe-477f-9f1c-fe01c1dae05e.png)

After:

![Screenshot 2025-11-28 at 14.23.58.png](https://app.graphite.com/user-attachments/assets/b801ec16-0dee-4cdd-b2a3-c967120f41f5.png)


Changed files:
filename: turbopack/crates/turbopack-trace-server/src/span_ref.rs
patch: @@ -184,9 +184,13 @@ impl<'a> SpanRef<'a> {
 
     // TODO(sokra) use events instead of children for visualizing span graphs
     #[allow(dead_code)]
-    pub fn events(&self) -> impl Iterator<Item = SpanEventRef<'a>> {
+    pub fn events(&self) -> impl DoubleEndedIterator<Item = SpanEventRef<'a>> {
         self.span.events.iter().map(|event| match event {
-            &SpanEvent::SelfTime { start, end } => SpanEventRef::SelfTime { start, end },
+            &SpanEvent::SelfTime { start, end } => SpanEventRef::SelfTime {
+                store: self.store,
+                start,
+                end,
+            },
             SpanEvent::Child { index } => SpanEventRef::Child {
                 span: SpanRef {
                     span: &self.store.spans[index.get()],
@@ -545,6 +549,45 @@ impl Debug for SpanRef<'_> {
 #[allow(dead_code)]
 #[derive(Copy, Clone)]
 pub enum SpanEventRef<'a> {
-    SelfTime { start: Timestamp, end: Timestamp },
-    Child { span: SpanRef<'a> },
+    SelfTime {
+        store: &'a Store,
+        start: Timestamp,
+        end: Timestamp,
+    },
+    Child {
+        span: SpanRef<'a>,
+    },
+}
+
+impl SpanEventRef<'_> {
+    pub fn start(&self) -> Timestamp {
+        match self {
+            SpanEventRef::SelfTime { start, .. } => *start,
+            SpanEventRef::Child { span } => span.start(),
+        }
+    }
+
+    pub fn total_time(&self) -> Timestamp {
+        match self {
+            SpanEventRef::SelfTime { start, end, .. } => end.saturating_sub(*start),
+            SpanEventRef::Child { span } => span.total_time(),
+        }
+    }
+
+    pub fn corrected_self_time(&self) -> Timestamp {
+        match self {
+            SpanEventRef::SelfTime { store, start, end } => {
+                let duration = *end - *start;
+                if !duration.is_zero() {
+                    store.set_max_self_time_lookup(*end);
+                    store.self_time_tree.as_ref().map_or(duration, |tree| {
+                        tree.lookup_range_corrected_time(*start, *end)
+                    })
+                } else {
+                    Timestamp::ZERO
+                }
+            }
+            SpanEventRef::Child { span } => span.corrected_self_time(),
+        }
+    }
 }

filename: turbopack/crates/turbopack-trace-server/src/viewer.rs
patch: @@ -10,7 +10,7 @@ use crate::{
     server::ViewRect,
     span_bottom_up_ref::SpanBottomUpRef,
     span_graph_ref::{SpanGraphEventRef, SpanGraphRef},
-    span_ref::SpanRef,
+    span_ref::{SpanEventRef, SpanRef},
     store::{SpanId, Store},
     timestamp::Timestamp,
     u64_empty_string,
@@ -76,6 +76,17 @@ impl ValueMode {
         }
     }
 
+    fn value_from_event(&self, event: &SpanEventRef<'_>) -> u64 {
+        match self {
+            ValueMode::Duration => *event.corrected_self_time(),
+            ValueMode::Cpu => *event.total_time(),
+            _ => match event {
+                SpanEventRef::Child { span } => self.value_from_span(span),
+                SpanEventRef::SelfTime { .. } => 0,
+            },
+        }
+    }
+
     fn value_from_graph(&self, graph: &SpanGraphRef<'_>) -> u64 {
         match self {
             ValueMode::Duration => *graph.corrected_total_time(),
@@ -651,24 +662,31 @@ impl Viewer {
                         }
                     } else if !selected_view_mode.aggregate_children() {
                         let spans = if selected_view_mode.sort_children() {
-                            Either::Left(span.children().sorted_by_cached_key(|child| {
-                                Reverse(value_mode.value_from_span(child))
+                            Either::Left(span.events().sorted_by_cached_key(|child| {
+                                Reverse(value_mode.value_from_event(child))
                             }))
                         } else {
-                            Either::Right(span.children().sorted_by_key(|child| child.start()))
+                            Either::Right(span.events().sorted_by_key(|child| child.start()))
                         };
                         for child in spans {
-                            let filtered = get_filter_mode(child.id());
-                            add_child_item(
-                                &mut children,
-                                &mut current,
-                                view_rect,
-                                child_line_index,
-                                view_mode,
-                                value_mode,
-                                QueueItem::Span(child),
-                                filtered,
-                            );
+                            match child {
+                                SpanEventRef::SelfTime { .. } => {
+                                    current += value_mode.value_from_event(&child);
+                                }
+                                SpanEventRef::Child { span: child } => {
+                                    let filtered = get_filter_mode(child.id());
+                                    add_child_item(
+                                        &mut children,
+                                        &mut current,
+                                        view_rect,
+                                        child_line_index,
+                                        view_mode,
+                                        value_mode,
+                                        QueueItem::Span(child),
+                                        filtered,
+                                    );
+                                }
+                            }
                         }
                     } else {
                         let events = if selected_view_mode.sort_children() {