1use crate::tui::core_commands::{CommandResponse as CoreCommandResponse, CoreCommandType};
2use steer_grpc::client_api::{McpServerInfo, Message};
3use steer_tools::ToolCall;
4use time::OffsetDateTime;
5
6pub type RowId = String;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub enum NoticeLevel {
11 Error,
12 Warn,
13 Info,
14}
15
16#[derive(Debug, Clone)]
18pub enum ChatItemData {
19 Message(Message),
21
22 PendingToolCall {
24 id: RowId,
25 tool_call: ToolCall,
26 ts: OffsetDateTime,
27 },
28
29 SlashInput {
31 id: RowId,
32 raw: String,
33 ts: OffsetDateTime,
34 },
35
36 SystemNotice {
38 id: RowId,
39 level: NoticeLevel,
40 text: String,
41 ts: OffsetDateTime,
42 },
43
44 CoreCmdResponse {
45 id: RowId,
46 command: CoreCommandType,
47 response: CoreCommandResponse,
48 ts: OffsetDateTime,
49 },
50
51 TuiCommandResponse {
53 id: RowId,
54 command: String,
55 response: TuiCommandResponse,
56 ts: OffsetDateTime,
57 },
58
59 InFlightOperation {
60 id: RowId,
61 operation_id: uuid::Uuid,
62 label: String,
63 ts: OffsetDateTime,
64 },
65}
66
67#[derive(Debug, Clone)]
68pub enum TuiCommandResponse {
69 Text(String),
70 Theme { name: String },
71 ListThemes(Vec<String>),
72 ListMcpServers(Vec<McpServerInfo>),
73}
74
75#[derive(Debug, Clone)]
76pub enum CommandResponse {
77 Core(CoreCommandResponse),
78 Tui(TuiCommandResponse),
79}
80impl From<CoreCommandResponse> for CommandResponse {
81 fn from(response: CoreCommandResponse) -> Self {
82 CommandResponse::Core(response)
83 }
84}
85impl From<TuiCommandResponse> for CommandResponse {
86 fn from(response: TuiCommandResponse) -> Self {
87 CommandResponse::Tui(response)
88 }
89}
90
91#[derive(Debug, Clone)]
92pub struct ChatItem {
93 pub parent_chat_item_id: Option<RowId>,
94 pub data: ChatItemData,
95}
96
97impl ChatItem {
98 pub fn id(&self) -> &str {
100 match &self.data {
101 ChatItemData::Message(row) => row.id(),
102 ChatItemData::PendingToolCall { id, .. } => id,
103 ChatItemData::SlashInput { id, .. } => id,
104 ChatItemData::CoreCmdResponse { id, .. } => id,
105 ChatItemData::SystemNotice { id, .. } => id,
106 ChatItemData::TuiCommandResponse { id, .. } => id,
107 ChatItemData::InFlightOperation { id, .. } => id,
108 }
109 }
110
111 pub fn timestamp(&self) -> OffsetDateTime {
113 match &self.data {
114 ChatItemData::Message(message) => {
115 OffsetDateTime::from_unix_timestamp(message.timestamp() as i64)
116 .unwrap_or(OffsetDateTime::UNIX_EPOCH)
117 }
118 ChatItemData::PendingToolCall { ts, .. } => *ts,
119 ChatItemData::SlashInput { ts, .. } => *ts,
120 ChatItemData::CoreCmdResponse { ts, .. } => *ts,
121 ChatItemData::SystemNotice { ts, .. } => *ts,
122 ChatItemData::TuiCommandResponse { ts, .. } => *ts,
123 ChatItemData::InFlightOperation { ts, .. } => *ts,
124 }
125 }
126
127 pub fn parent_message_id(&self) -> Option<&str> {
129 match &self.data {
130 ChatItemData::Message(msg) => msg.parent_message_id(),
131 _ => None,
132 }
133 }
134}
135
136pub fn generate_row_id() -> RowId {
137 ulid::Ulid::new().to_string()
138}