steer_tui/tui/
model.rs

1//! Core data model for TUI chat items
2//!
3//! This module defines the ChatItem enum which represents all possible rows
4//! that can appear in the chat panel, including both conversation messages
5//! and meta rows like slash commands and system notices.
6
7use steer_core::app::conversation::{AppCommandType, CommandResponse, Message};
8use steer_tools::ToolCall;
9use time::OffsetDateTime;
10
11pub type RowId = String;
12
13/// Severity levels for system notices
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub enum NoticeLevel {
16    Error,
17    Warn,
18    Info,
19}
20
21/// All rows that can appear in the scrollback panel
22#[derive(Debug, Clone)]
23pub enum ChatItemData {
24    /// Conversation message
25    Message(Message),
26
27    /// A tool call that is in progress
28    PendingToolCall {
29        id: RowId,
30        tool_call: ToolCall,
31        ts: OffsetDateTime,
32    },
33
34    /// Raw slash command entered by the user
35    SlashInput {
36        id: RowId,
37        raw: String,
38        ts: OffsetDateTime,
39    },
40
41    /// Internal notices (errors, warnings, info)
42    SystemNotice {
43        id: RowId,
44        level: NoticeLevel,
45        text: String,
46        ts: OffsetDateTime,
47    },
48
49    /// Core replied to a command
50    CoreCmdResponse {
51        id: RowId,
52        cmd: AppCommandType,
53        resp: CommandResponse,
54        ts: OffsetDateTime,
55    },
56
57    /// TUI command response (e.g., /help, /theme, /auth)
58    TuiCommandResponse {
59        id: RowId,
60        command: String,
61        response: String,
62        ts: OffsetDateTime,
63    },
64
65    InFlightOperation {
66        id: RowId,
67        operation_id: uuid::Uuid,
68        label: String,
69        ts: OffsetDateTime,
70    },
71}
72
73#[derive(Debug, Clone)]
74pub struct ChatItem {
75    pub parent_chat_item_id: Option<RowId>,
76    pub data: ChatItemData,
77}
78
79impl ChatItem {
80    /// Get the unique identifier for this chat item
81    pub fn id(&self) -> &str {
82        match &self.data {
83            ChatItemData::Message(row) => row.id(),
84            ChatItemData::PendingToolCall { id, .. } => id,
85            ChatItemData::SlashInput { id, .. } => id,
86            ChatItemData::CoreCmdResponse { id, .. } => id,
87            ChatItemData::SystemNotice { id, .. } => id,
88            ChatItemData::TuiCommandResponse { id, .. } => id,
89            ChatItemData::InFlightOperation { id, .. } => id,
90        }
91    }
92
93    /// Get the timestamp for this chat item
94    pub fn timestamp(&self) -> OffsetDateTime {
95        match &self.data {
96            ChatItemData::Message(message) => {
97                OffsetDateTime::from_unix_timestamp(message.timestamp() as i64).unwrap()
98            }
99            ChatItemData::PendingToolCall { ts, .. } => *ts,
100            ChatItemData::SlashInput { ts, .. } => *ts,
101            ChatItemData::CoreCmdResponse { ts, .. } => *ts,
102            ChatItemData::SystemNotice { ts, .. } => *ts,
103            ChatItemData::TuiCommandResponse { ts, .. } => *ts,
104            ChatItemData::InFlightOperation { ts, .. } => *ts,
105        }
106    }
107
108    /// Get the parent message ID for filtering
109    pub fn parent_message_id(&self) -> Option<&str> {
110        match &self.data {
111            ChatItemData::Message(msg) => msg.parent_message_id(),
112            _ => None,
113        }
114    }
115}
116
117pub fn generate_row_id() -> RowId {
118    ulid::Ulid::new().to_string()
119}