Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions src-tauri/src/commands/conversations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,27 @@ use crate::parsers::hermes::HermesParser;
use crate::parsers::openclaw::OpenClawParser;
use crate::parsers::opencode::OpenCodeParser;
use crate::parsers::{path_eq_for_matching, AgentParser, ParseError};

const DEFAULT_CONVERSATION_TURNS_PAGE_SIZE: usize = 30;

fn paginate_turns(
turns: Vec<MessageTurn>,
before_turn_index: Option<usize>,
page_size: Option<usize>,
) -> (Vec<MessageTurn>, bool, Option<usize>, usize) {
let total = turns.len();
let page_size = page_size.unwrap_or(DEFAULT_CONVERSATION_TURNS_PAGE_SIZE).max(1);
let end = before_turn_index.unwrap_or(total).min(total);
let start = end.saturating_sub(page_size);
let has_more_history = start > 0;
let next_before_turn_index = has_more_history.then_some(start);
(
turns[start..end].to_vec(),
has_more_history,
next_before_turn_index,
page_size,
)
}
use crate::web::event_bridge::{
emit_event, ConversationChange, EventEmitter, TabsChanged, CONVERSATION_CHANGED_EVENT,
TABS_CHANGED_EVENT,
Expand Down Expand Up @@ -821,6 +842,50 @@ pub async fn get_folder_conversation(
.await
}

pub async fn get_folder_conversation_paginated_core(
conn: &sea_orm::DatabaseConnection,
manager: &crate::acp::manager::ConnectionManager,
emitter: &EventEmitter,
conversation_id: i32,
before_turn_index: Option<usize>,
page_size: Option<usize>,
) -> Result<DbConversationDetailPage, AppCommandError> {
let detail =
get_folder_conversation_with_live_core(conn, manager, emitter, conversation_id).await?;
let (turns, has_more_history, next_before_turn_index, page_size) =
paginate_turns(detail.turns, before_turn_index, page_size);
Ok(DbConversationDetailPage {
summary: detail.summary,
turns,
has_more_history,
next_before_turn_index,
page_size,
session_stats: detail.session_stats,
in_flight_user_turn_id: detail.in_flight_user_turn_id,
})
}

#[cfg(feature = "tauri-runtime")]
#[cfg_attr(feature = "tauri-runtime", tauri::command)]
pub async fn get_folder_conversation_paginated(
app: tauri::AppHandle,
db: tauri::State<'_, AppDatabase>,
manager: tauri::State<'_, crate::acp::manager::ConnectionManager>,
conversation_id: i32,
before_turn_index: Option<usize>,
page_size: Option<usize>,
) -> Result<DbConversationDetailPage, AppCommandError> {
get_folder_conversation_paginated_core(
&db.conn,
&manager,
&EventEmitter::Tauri(app),
conversation_id,
before_turn_index,
page_size,
)
.await
}

/// Emit a `conversation://changed` Upsert for `conversation_id` so every
/// client's sidebar inserts-or-replaces the row in real time. Re-fetches the
/// fresh summary via `get_by_id`, which filters out soft-deleted rows — so an
Expand Down
1 change: 1 addition & 0 deletions src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -736,6 +736,7 @@ mod tauri_app {
conversations::save_opened_tabs,
conversations::import_local_conversations,
conversations::get_folder_conversation,
conversations::get_folder_conversation_paginated,
conversations::list_folders,
conversations::get_stats,
conversations::get_sidebar_data,
Expand Down
13 changes: 13 additions & 0 deletions src-tauri/src/models/conversation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,19 @@ pub struct DbConversationDetail {
pub in_flight_user_turn_id: Option<String>,
}

#[derive(Debug, Clone, Serialize)]
pub struct DbConversationDetailPage {
pub summary: DbConversationSummary,
pub turns: Vec<MessageTurn>,
pub has_more_history: bool,
pub next_before_turn_index: Option<usize>,
pub page_size: usize,
#[serde(skip_serializing_if = "Option::is_none")]
pub session_stats: Option<SessionStats>,
#[serde(skip_serializing_if = "Option::is_none")]
pub in_flight_user_turn_id: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FolderInfo {
pub path: String,
Expand Down
25 changes: 25 additions & 0 deletions src-tauri/src/web/handlers/conversations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,14 @@ pub struct GetFolderConversationParams {
pub conversation_id: i32,
}

#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GetFolderConversationPaginatedParams {
pub conversation_id: i32,
pub before_turn_index: Option<usize>,
pub page_size: Option<usize>,
}

pub async fn get_folder_conversation(
Extension(state): Extension<Arc<AppState>>,
Json(params): Json<GetFolderConversationParams>,
Expand All @@ -142,6 +150,23 @@ pub async fn get_folder_conversation(
Ok(Json(result))
}

pub async fn get_folder_conversation_paginated(
Extension(state): Extension<Arc<AppState>>,
Json(params): Json<GetFolderConversationPaginatedParams>,
) -> Result<Json<DbConversationDetailPage>, AppCommandError> {
let db = &state.db;
let result = conv_commands::get_folder_conversation_paginated_core(
&db.conn,
&state.connection_manager,
&state.emitter,
params.conversation_id,
params.before_turn_index,
params.page_size,
)
.await?;
Ok(Json(result))
}

pub async fn list_folders() -> Result<Json<Vec<FolderInfo>>, AppCommandError> {
let result = conv_commands::list_folders().await?;
Ok(Json(result))
Expand Down
4 changes: 4 additions & 0 deletions src-tauri/src/web/router.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ pub fn build_router(
"/get_folder_conversation",
post(handlers::conversations::get_folder_conversation),
)
.route(
"/get_folder_conversation_paginated",
post(handlers::conversations::get_folder_conversation_paginated),
)
.route(
"/list_opened_tabs",
post(handlers::conversations::list_opened_tabs),
Expand Down
6 changes: 3 additions & 3 deletions src/components/message/message-list-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -903,9 +903,9 @@ export function MessageListView({
<MessageThread
className="flex-1 min-h-0"
resize={shouldUseSmoothResize ? "smooth" : undefined}
>
<AutoScrollOnSend signal={sendSignal} />
<VirtualizedMessageThread
>
<AutoScrollOnSend signal={sendSignal} />
<VirtualizedMessageThread
items={threadItems}
getItemKey={getThreadItemKey}
renderItem={renderThreadItem}
Expand Down
13 changes: 13 additions & 0 deletions src/lib/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import type {
ConversationSummary,
ConversationDetail,
DbConversationDetail,
DbConversationDetailPage,
FolderInfo,
AgentStats,
SidebarData,
Expand Down Expand Up @@ -888,6 +889,18 @@ export async function getFolderConversation(
return getTransport().call("get_folder_conversation", { conversationId })
}

export async function getFolderConversationPaginated(params: {
conversationId: number
beforeTurnIndex?: number | null
pageSize?: number | null
}): Promise<DbConversationDetailPage> {
return getTransport().call("get_folder_conversation_paginated", {
conversationId: params.conversationId,
beforeTurnIndex: params.beforeTurnIndex ?? null,
pageSize: params.pageSize ?? null,
})
}

export async function removeFolderFromHistory(path: string): Promise<void> {
return getTransport().call("remove_folder_from_history", { path })
}
Expand Down
13 changes: 13 additions & 0 deletions src/lib/tauri.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type {
ConversationSummary,
ConversationDetail,
DbConversationDetail,
DbConversationDetailPage,
FolderInfo,
AgentStats,
SidebarData,
Expand Down Expand Up @@ -591,6 +592,18 @@ export async function getFolderConversation(
return invoke("get_folder_conversation", { conversationId })
}

export async function getFolderConversationPaginated(params: {
conversationId: number
beforeTurnIndex?: number | null
pageSize?: number | null
}): Promise<DbConversationDetailPage> {
return invoke("get_folder_conversation_paginated", {
conversationId: params.conversationId,
beforeTurnIndex: params.beforeTurnIndex ?? null,
pageSize: params.pageSize ?? null,
})
}

export async function removeFolderFromHistory(path: string): Promise<void> {
return invoke("remove_folder_from_history", { path })
}
Expand Down
10 changes: 10 additions & 0 deletions src/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,16 @@ export interface DbConversationDetail {
in_flight_user_turn_id?: string | null
}

export interface DbConversationDetailPage {
summary: DbConversationSummary
turns: MessageTurn[]
has_more_history: boolean
next_before_turn_index?: number | null
page_size: number
session_stats?: SessionStats | null
in_flight_user_turn_id?: string | null
}

export type ConversationStatus =
| "in_progress"
| "pending_review"
Expand Down