envision/component/timeline/types.rs
1//! Types for the Timeline component.
2//!
3//! Contains [`TimelineEvent`], [`TimelineSpan`], [`SelectedType`],
4//! [`TimelineMessage`], and [`TimelineOutput`].
5
6use ratatui::prelude::*;
7
8/// A point event on the timeline.
9///
10/// Represents a single moment in time, rendered as a marker on the timeline.
11///
12/// # Example
13///
14/// ```rust
15/// use envision::component::TimelineEvent;
16/// use ratatui::style::Color;
17///
18/// let event = TimelineEvent::new("e1", 100.0, "Deploy")
19/// .with_color(Color::Green);
20/// assert_eq!(event.id, "e1");
21/// assert_eq!(event.timestamp, 100.0);
22/// assert_eq!(event.label, "Deploy");
23/// assert_eq!(event.color, Color::Green);
24/// ```
25#[derive(Clone, Debug, PartialEq)]
26#[cfg_attr(
27 feature = "serialization",
28 derive(serde::Serialize, serde::Deserialize)
29)]
30pub struct TimelineEvent {
31 /// Unique identifier.
32 pub id: String,
33 /// Timestamp in milliseconds from epoch (or any consistent unit).
34 pub timestamp: f64,
35 /// Display label.
36 pub label: String,
37 /// Color for this event marker.
38 pub color: Color,
39}
40
41impl TimelineEvent {
42 /// Creates a new timeline event with default color.
43 ///
44 /// # Example
45 ///
46 /// ```rust
47 /// use envision::component::TimelineEvent;
48 /// use ratatui::style::Color;
49 ///
50 /// let event = TimelineEvent::new("deploy-1", 500.0, "Deployed v2.0");
51 /// assert_eq!(event.id, "deploy-1");
52 /// assert_eq!(event.timestamp, 500.0);
53 /// assert_eq!(event.label, "Deployed v2.0");
54 /// assert_eq!(event.color, Color::Yellow);
55 /// ```
56 pub fn new(id: impl Into<String>, timestamp: f64, label: impl Into<String>) -> Self {
57 Self {
58 id: id.into(),
59 timestamp,
60 label: label.into(),
61 color: Color::Yellow,
62 }
63 }
64
65 /// Sets the color (builder pattern).
66 ///
67 /// # Example
68 ///
69 /// ```rust
70 /// use envision::component::TimelineEvent;
71 /// use ratatui::style::Color;
72 ///
73 /// let event = TimelineEvent::new("e1", 0.0, "Start")
74 /// .with_color(Color::Red);
75 /// assert_eq!(event.color, Color::Red);
76 /// ```
77 pub fn with_color(mut self, color: Color) -> Self {
78 self.color = color;
79 self
80 }
81
82 /// Sets the color.
83 ///
84 /// # Example
85 ///
86 /// ```rust
87 /// use envision::component::TimelineEvent;
88 /// use ratatui::style::Color;
89 ///
90 /// let mut event = TimelineEvent::new("e1", 0.0, "Start");
91 /// event.set_color(Color::Red);
92 /// assert_eq!(event.color, Color::Red);
93 /// ```
94 pub fn set_color(&mut self, color: Color) {
95 self.color = color;
96 }
97
98 /// Returns the color for this event marker.
99 ///
100 /// # Example
101 ///
102 /// ```rust
103 /// use envision::component::TimelineEvent;
104 /// use ratatui::style::Color;
105 ///
106 /// let event = TimelineEvent::new("e1", 0.0, "Start").with_color(Color::Red);
107 /// assert_eq!(event.color(), Color::Red);
108 /// ```
109 pub fn color(&self) -> Color {
110 self.color
111 }
112}
113
114/// A span (duration) on the timeline.
115///
116/// Represents a range of time, rendered as a horizontal bar.
117///
118/// # Example
119///
120/// ```rust
121/// use envision::component::TimelineSpan;
122/// use ratatui::style::Color;
123///
124/// let span = TimelineSpan::new("s1", 100.0, 500.0, "HTTP Request")
125/// .with_color(Color::Cyan)
126/// .with_lane(1);
127/// assert_eq!(span.id, "s1");
128/// assert_eq!(span.start, 100.0);
129/// assert_eq!(span.end, 500.0);
130/// assert_eq!(span.duration(), 400.0);
131/// assert_eq!(span.lane, 1);
132/// ```
133#[derive(Clone, Debug, PartialEq)]
134#[cfg_attr(
135 feature = "serialization",
136 derive(serde::Serialize, serde::Deserialize)
137)]
138pub struct TimelineSpan {
139 /// Unique identifier.
140 pub id: String,
141 /// Start timestamp.
142 pub start: f64,
143 /// End timestamp.
144 pub end: f64,
145 /// Display label.
146 pub label: String,
147 /// Color for this span bar.
148 pub color: Color,
149 /// Optional row/lane index for vertical positioning.
150 pub lane: usize,
151}
152
153impl TimelineSpan {
154 /// Creates a new timeline span with default color and lane 0.
155 ///
156 /// # Example
157 ///
158 /// ```rust
159 /// use envision::component::TimelineSpan;
160 /// use ratatui::style::Color;
161 ///
162 /// let span = TimelineSpan::new("s1", 200.0, 800.0, "request-1");
163 /// assert_eq!(span.id, "s1");
164 /// assert_eq!(span.start, 200.0);
165 /// assert_eq!(span.end, 800.0);
166 /// assert_eq!(span.label, "request-1");
167 /// assert_eq!(span.color, Color::Cyan);
168 /// assert_eq!(span.lane, 0);
169 /// ```
170 pub fn new(id: impl Into<String>, start: f64, end: f64, label: impl Into<String>) -> Self {
171 Self {
172 id: id.into(),
173 start,
174 end,
175 label: label.into(),
176 color: Color::Cyan,
177 lane: 0,
178 }
179 }
180
181 /// Sets the color (builder pattern).
182 ///
183 /// # Example
184 ///
185 /// ```rust
186 /// use envision::component::TimelineSpan;
187 /// use ratatui::style::Color;
188 ///
189 /// let span = TimelineSpan::new("s1", 0.0, 100.0, "task")
190 /// .with_color(Color::Red);
191 /// assert_eq!(span.color, Color::Red);
192 /// ```
193 pub fn with_color(mut self, color: Color) -> Self {
194 self.color = color;
195 self
196 }
197
198 /// Sets the color.
199 ///
200 /// # Example
201 ///
202 /// ```rust
203 /// use envision::component::TimelineSpan;
204 /// use ratatui::style::Color;
205 ///
206 /// let mut span = TimelineSpan::new("s1", 0.0, 100.0, "task");
207 /// span.set_color(Color::Red);
208 /// assert_eq!(span.color, Color::Red);
209 /// ```
210 pub fn set_color(&mut self, color: Color) {
211 self.color = color;
212 }
213
214 /// Returns the color for this span bar.
215 ///
216 /// # Example
217 ///
218 /// ```rust
219 /// use envision::component::TimelineSpan;
220 /// use ratatui::style::Color;
221 ///
222 /// let span = TimelineSpan::new("s1", 0.0, 100.0, "task").with_color(Color::Red);
223 /// assert_eq!(span.color(), Color::Red);
224 /// ```
225 pub fn color(&self) -> Color {
226 self.color
227 }
228
229 /// Sets the lane (builder pattern).
230 ///
231 /// # Example
232 ///
233 /// ```rust
234 /// use envision::component::TimelineSpan;
235 ///
236 /// let span = TimelineSpan::new("s1", 0.0, 100.0, "task")
237 /// .with_lane(2);
238 /// assert_eq!(span.lane, 2);
239 /// ```
240 pub fn with_lane(mut self, lane: usize) -> Self {
241 self.lane = lane;
242 self
243 }
244
245 /// Returns the duration of this span.
246 ///
247 /// # Example
248 ///
249 /// ```rust
250 /// use envision::component::TimelineSpan;
251 ///
252 /// let span = TimelineSpan::new("s1", 100.0, 400.0, "task");
253 /// assert_eq!(span.duration(), 300.0);
254 /// ```
255 pub fn duration(&self) -> f64 {
256 self.end - self.start
257 }
258}
259
260/// Distinguishes whether the selected item is an event or a span.
261///
262/// # Example
263///
264/// ```rust
265/// use envision::component::SelectedType;
266///
267/// let default = SelectedType::default();
268/// assert_eq!(default, SelectedType::Event);
269/// ```
270#[derive(Clone, Debug, Default, PartialEq, Eq)]
271#[cfg_attr(
272 feature = "serialization",
273 derive(serde::Serialize, serde::Deserialize)
274)]
275pub enum SelectedType {
276 /// A point event is selected.
277 #[default]
278 Event,
279 /// A span is selected.
280 Span,
281}
282
283/// Messages that can be sent to a Timeline.
284///
285/// # Example
286///
287/// ```rust
288/// use envision::component::{
289/// Component, Timeline, TimelineState, TimelineMessage, TimelineEvent,
290/// };
291///
292/// let mut state = TimelineState::new();
293/// let event = TimelineEvent::new("e1", 100.0, "Start");
294/// state.update(TimelineMessage::AddEvent(event));
295/// assert_eq!(state.events().len(), 1);
296/// ```
297#[derive(Clone, Debug, PartialEq)]
298#[cfg_attr(
299 feature = "serialization",
300 derive(serde::Serialize, serde::Deserialize)
301)]
302pub enum TimelineMessage {
303 /// Add a point event.
304 AddEvent(TimelineEvent),
305 /// Add a duration span.
306 AddSpan(TimelineSpan),
307 /// Replace all events.
308 SetEvents(Vec<TimelineEvent>),
309 /// Replace all spans.
310 SetSpans(Vec<TimelineSpan>),
311 /// Clear everything.
312 Clear,
313 /// Narrow the visible window (zoom in).
314 ZoomIn,
315 /// Widen the visible window (zoom out).
316 ZoomOut,
317 /// Shift visible window left.
318 PanLeft,
319 /// Shift visible window right.
320 PanRight,
321 /// Adjust view to show all events/spans.
322 FitAll,
323 /// Select next event/span.
324 SelectNext,
325 /// Select previous event/span.
326 SelectPrev,
327}
328
329/// Output messages from a Timeline.
330///
331/// # Example
332///
333/// ```rust
334/// use envision::component::TimelineOutput;
335///
336/// let output = TimelineOutput::EventSelected("e1".into());
337/// assert_eq!(output, TimelineOutput::EventSelected("e1".into()));
338/// ```
339#[derive(Clone, Debug, PartialEq)]
340#[cfg_attr(
341 feature = "serialization",
342 derive(serde::Serialize, serde::Deserialize)
343)]
344pub enum TimelineOutput {
345 /// An event was selected (carries event id).
346 EventSelected(String),
347 /// A span was selected (carries span id).
348 SpanSelected(String),
349 /// The visible time window changed.
350 ViewChanged {
351 /// New view start.
352 start: f64,
353 /// New view end.
354 end: f64,
355 },
356}