Skip to content

Commit 165db36

Browse files
milispclaude
andcommitted
refactor: streamline conversation management and UI layout
- Remove unused stop_session command and related backend functionality - Simplify ChatView component by removing complex tab management logic - Clean up ConversationTabs to focus on sessions and favorites only - Remove SessionManager component and session list visibility controls - Add SearchInput component for consistent search UI across tabs - Improve conversation selection and session handling in chat interface - Remove sessionLoader dependency and consolidate session management - Update NotesView to remove notes list sidebar - Fix minor layout issues in NoteList component 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 2c93e3a commit 165db36

File tree

17 files changed

+231
-599
lines changed

17 files changed

+231
-599
lines changed

src-tauri/src/commands.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,6 @@ pub async fn approve_patch(
5252
codex::approve_patch(state, session_id, approval_id, approved).await
5353
}
5454

55-
#[tauri::command]
56-
pub async fn stop_session(state: State<'_, CodexState>, session_id: String) -> Result<(), String> {
57-
codex::stop_session(state, session_id).await
58-
}
5955

6056
#[tauri::command]
6157
pub async fn pause_session(state: State<'_, CodexState>, session_id: String) -> Result<(), String> {

src-tauri/src/lib.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use commands::{
1212
approve_execution, approve_patch, check_codex_version, close_session, delete_session_file,
1313
get_latest_session_id, get_running_sessions, get_session_files, load_sessions_from_disk,
1414
pause_session, read_history_file, read_session_file, send_message,
15-
start_codex_session, stop_session,
15+
start_codex_session,
1616
};
1717
use config::{
1818
add_or_update_model_provider, add_or_update_profile, delete_profile, get_profile_config,
@@ -57,7 +57,6 @@ pub fn run() {
5757
send_message,
5858
approve_execution,
5959
approve_patch,
60-
stop_session,
6160
pause_session,
6261
close_session,
6362
get_running_sessions,

src-tauri/src/services/codex.rs

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -92,25 +92,6 @@ pub async fn approve_patch(
9292
}
9393
}
9494

95-
pub async fn stop_session(state: State<'_, CodexState>, session_id: String) -> Result<(), String> {
96-
let sessions = state.sessions.lock().await;
97-
let stored_sessions: Vec<String> = sessions.keys().cloned().collect();
98-
99-
log::debug!("Attempting to interrupt session: {}", session_id);
100-
log::debug!("Currently stored sessions: {:?}", stored_sessions);
101-
102-
if let Some(client) = sessions.get(&session_id) {
103-
log::debug!("Found session, sending interrupt: {}", session_id);
104-
client
105-
.interrupt()
106-
.await
107-
.map_err(|e| format!("Failed to interrupt session: {}", e))?;
108-
Ok(())
109-
} else {
110-
log::debug!("Session not found: {}", session_id);
111-
Err("Session not found".to_string())
112-
}
113-
}
11495

11596
pub async fn pause_session(state: State<'_, CodexState>, session_id: String) -> Result<(), String> {
11697
let sessions = state.sessions.lock().await;

src/components/ChatView.tsx

Lines changed: 61 additions & 170 deletions
Original file line numberDiff line numberDiff line change
@@ -1,106 +1,45 @@
1-
import React, { useState, useEffect } from "react";
1+
import React, { useState, useEffect, useMemo } from "react";
22
import { ChatInterface } from "./chat/ChatInterface";
3-
import { ConversationTabs } from "./chat/ConversationTabs";
3+
import { ConversationTabs } from "@/components/chat/ConversationTabs";
44
import { useConversationStore } from "@/stores/ConversationStore";
5-
import { useLayoutStore } from "@/stores/layoutStore";
6-
import { sessionManager } from "@/services/sessionManager";
7-
import { sessionLoader } from "@/services/sessionLoader";
85
import type { Conversation } from "@/types/chat";
9-
import { invoke } from "@tauri-apps/api/core";
10-
import { DebugInfo } from "./common/DebugInfo";
116

12-
export const ChatView: React.FC = () => {
13-
const [selectedConversation, setSelectedConversation] =
14-
useState<Conversation | null>(null);
15-
const [searchQueries, setSearchQueries] = useState({
16-
all: "",
17-
favorites: "",
18-
sessions: ""
19-
});
20-
const [historyConversations, setHistoryConversations] = useState<
21-
Conversation[]
22-
>([]);
23-
const [favoriteStatuses, setFavoriteStatuses] = useState<
24-
Record<string, boolean>
25-
>({});
7+
interface ChatViewProps {
8+
selectedConversation?: Conversation | null;
9+
showChatTabs?: boolean;
10+
}
2611

12+
export const ChatView: React.FC<ChatViewProps> = ({ selectedConversation, showChatTabs = false }) => {
2713
const {
2814
currentConversationId,
2915
conversations: activeConversations,
30-
createConversationWithLatestSession,
31-
selectHistoryConversation,
3216
deleteConversation,
3317
setCurrentConversation,
18+
selectHistoryConversation,
3419
pendingNewConversation,
20+
toggleFavorite,
3521
} = useConversationStore();
36-
const { showSessionList, conversationListTab } = useLayoutStore();
3722

38-
// Don't auto-switch tabs to prevent unwanted tab changes when favoriting/deleting conversations
39-
// Users can manually switch tabs as needed
23+
const [searchQuery, setSearchQuery] = useState("");
24+
const [internalSelectedConversation, setInternalSelectedConversation] = useState<Conversation | null>(null);
4025

41-
// Clear selected conversation when starting a new conversation
42-
useEffect(() => {
43-
if (pendingNewConversation) {
44-
setSelectedConversation(null);
45-
}
46-
}, [pendingNewConversation]);
26+
// Generate favorite statuses from the persisted store data
27+
const favoriteStatuses = useMemo(() => {
28+
const statuses: Record<string, boolean> = {};
29+
activeConversations.forEach(conv => {
30+
statuses[conv.id] = conv.isFavorite || false;
31+
});
32+
return statuses;
33+
}, [activeConversations]);
4734

48-
// Also clear when currentConversationId changes to a new codex-event format (from toolbar button)
49-
useEffect(() => {
50-
if (currentConversationId && currentConversationId.startsWith('codex-event-')) {
51-
// Check if this is a new conversation (no messages)
52-
const currentConv = activeConversations.find(conv => conv.id === currentConversationId);
53-
if (currentConv && currentConv.messages.length === 0) {
54-
setSelectedConversation(null);
55-
}
56-
}
57-
}, [currentConversationId, activeConversations]);
58-
59-
const loadHistory = async () => {
60-
try {
61-
const history = await sessionLoader.loadSessionsFromDisk();
62-
setHistoryConversations(history);
63-
64-
const statuses: Record<string, boolean> = {};
65-
for (const conv of history) {
66-
statuses[conv.id] = await sessionLoader.isConversationFavorited(
67-
conv.id,
68-
);
69-
}
70-
setFavoriteStatuses(statuses);
71-
} catch (error) {
72-
console.error("Failed to load history conversations:", error);
73-
}
74-
};
75-
76-
useEffect(() => {
77-
loadHistory();
78-
}, []);
79-
80-
// Handle creating new conversation with latest session
81-
const handleCreateNewConversation = async () => {
82-
try {
83-
await createConversationWithLatestSession();
84-
} catch (error) {
85-
console.error("Failed to create new conversation:", error);
86-
}
87-
};
88-
89-
// Handle conversation selection from history
9035
const handleConversationSelect = (conversation: Conversation) => {
91-
// Always create a new object to force re-render even if same conversation
9236
const conversationCopy = { ...conversation };
93-
94-
// Store the selected conversation data
95-
setSelectedConversation(conversationCopy);
96-
97-
// Use the new method to select history conversation with full data
37+
setInternalSelectedConversation(conversationCopy);
9838
console.log("🔄 ChatView: Calling selectHistoryConversation", conversation.id);
9939
selectHistoryConversation(conversationCopy);
10040
};
10141

10242
const handleSelectSession = (sessionId: string) => {
103-
// Set the current session ID for communication
10443
setCurrentConversation(sessionId);
10544
};
10645

@@ -110,23 +49,7 @@ export const ChatView: React.FC = () => {
11049
) => {
11150
e.stopPropagation();
11251
try {
113-
const conversation = historyConversations.find(
114-
(c) => c.id === conversationId,
115-
);
116-
if (conversation && conversation.filePath) {
117-
await invoke("delete_session_file", {
118-
filePath: conversation.filePath,
119-
});
120-
}
12152
deleteConversation(conversationId);
122-
setHistoryConversations((prev) =>
123-
prev.filter((c) => c.id !== conversationId),
124-
);
125-
setFavoriteStatuses((prev) => {
126-
const newStatus = { ...prev };
127-
delete newStatus[conversationId];
128-
return newStatus;
129-
});
13053
} catch (error) {
13154
console.error("Failed to delete conversation and session file:", error);
13255
}
@@ -138,89 +61,57 @@ export const ChatView: React.FC = () => {
13861
) => {
13962
e.stopPropagation();
14063
try {
141-
await sessionLoader.toggleFavorite(conversationId);
142-
143-
// Update local favorite status
144-
setFavoriteStatuses((prev) => ({
145-
...prev,
146-
[conversationId]: !prev[conversationId],
147-
}));
64+
// Use the store's toggleFavorite method directly for persistence
65+
toggleFavorite(conversationId);
14866
} catch (error) {
14967
console.error("Failed to toggle favorite:", error);
15068
}
15169
};
15270

153-
const handleKillSession = async (sessionId: string) => {
154-
console.log(`💀 ChatView: Killing session ${sessionId}`);
155-
try {
156-
// First check if it's a timestamp format session
157-
const isTimestampFormat = sessionId.startsWith('codex-event-') && sessionId.includes('-') &&
158-
/\d{13}-[a-z0-9]+$/.test(sessionId.replace('codex-event-', ''));
159-
160-
if (!isTimestampFormat) {
161-
console.log(`💀 Ignoring UUID session kill: ${sessionId}`);
162-
return;
163-
}
164-
165-
// Extract the raw session ID for backend process management
166-
const rawSessionId = sessionId.replace('codex-event-', '');
167-
console.log(`🔄 Extracting raw session ID: ${rawSessionId}`);
71+
// Clear selected conversation when starting a new conversation
72+
useEffect(() => {
73+
if (pendingNewConversation) {
74+
setInternalSelectedConversation(null);
75+
}
76+
}, [pendingNewConversation]);
16877

169-
await sessionManager.stopSession(rawSessionId);
170-
console.log(`✅ Session stopped: ${sessionId}`);
171-
172-
// Remove the conversation from the store to update UI
173-
deleteConversation(sessionId);
174-
} catch (error) {
175-
console.warn('Failed to kill session (session may have already been cleaned up):', error);
176-
// Don't throw error - session might have been cleaned up by hot reload or other reasons
78+
// Also clear when currentConversationId changes to a new codex-event format (from toolbar button)
79+
useEffect(() => {
80+
if (currentConversationId && currentConversationId.startsWith('codex-event-')) {
81+
// Check if this is a new conversation (no messages)
82+
const currentConv = activeConversations.find(conv => conv.id === currentConversationId);
83+
if (currentConv && currentConv.messages.length === 0) {
84+
setInternalSelectedConversation(null);
85+
}
17786
}
178-
};
87+
}, [currentConversationId, activeConversations]);
17988

89+
// Use either the passed selectedConversation or internal one
90+
const displayedConversation = selectedConversation || internalSelectedConversation;
91+
92+
if (showChatTabs) {
93+
return (
94+
<ConversationTabs
95+
favoriteStatuses={favoriteStatuses}
96+
activeConversations={activeConversations}
97+
currentConversationId={currentConversationId}
98+
activeSessionId={currentConversationId || ''}
99+
searchQuery={searchQuery}
100+
onSearchChange={setSearchQuery}
101+
onSelectConversation={handleConversationSelect}
102+
onToggleFavorite={handleToggleFavorite}
103+
onDeleteConversation={handleDeleteConversation}
104+
onSelectSession={handleSelectSession}
105+
/>
106+
);
107+
}
180108

181109
return (
182-
<div className="flex h-full min-h-0">
183-
{showSessionList && (
184-
<div className="w-64 border-r h-full overflow-y-auto flex-shrink-0">
185-
<div className="flex flex-col h-full bg-background">
186-
187-
<DebugInfo
188-
conversationListTab={conversationListTab}
189-
currentConversationId={currentConversationId}
190-
historyConversationsCount={historyConversations.length}
191-
activeConversationsCount={activeConversations.length}
192-
searchQueries={searchQueries}
193-
/>
194-
195-
<ConversationTabs
196-
historyConversations={historyConversations}
197-
favoriteStatuses={favoriteStatuses}
198-
activeConversations={activeConversations}
199-
currentConversationId={currentConversationId}
200-
activeSessionId={currentConversationId || ''}
201-
searchQueries={searchQueries}
202-
onSearchChange={setSearchQueries}
203-
onSelectConversation={handleConversationSelect}
204-
onToggleFavorite={handleToggleFavorite}
205-
onDeleteConversation={handleDeleteConversation}
206-
onSelectSession={handleSelectSession}
207-
onKillSession={handleKillSession}
208-
onRefreshConversations={loadHistory}
209-
/>
210-
</div>
211-
</div>
212-
)}
213-
<div className="flex-1 min-h-0 h-full min-w-0">
214-
<ChatInterface
215-
sessionId={currentConversationId || ''}
216-
activeSessionId={currentConversationId || ''}
217-
onCreateSession={handleCreateNewConversation}
218-
onSelectSession={handleSelectSession}
219-
onCloseSession={deleteConversation}
220-
isSessionListVisible={false}
221-
selectedConversation={selectedConversation}
222-
/>
223-
</div>
110+
<div className="flex-1 min-h-0 h-full min-w-0">
111+
<ChatInterface
112+
sessionId={currentConversationId || ''}
113+
selectedConversation={displayedConversation}
114+
/>
224115
</div>
225116
);
226117
};

src/components/NotesView.tsx

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,10 @@
11
import React from "react";
2-
import { NoteList, NoteEditor } from "./notes";
3-
import { useLayoutStore } from "@/stores/layoutStore";
2+
import { NoteEditor } from "./notes/NoteEditor";
43

54
export const NotesView: React.FC = () => {
6-
const { showNotesList } = useLayoutStore();
75

86
return (
97
<div className="flex h-full min-h-0">
10-
{showNotesList && (
11-
<div className="w-64 border-r h-full flex-shrink-0 dark:border-gray-700">
12-
<NoteList />
13-
</div>
14-
)}
158
<div className="flex-1 min-h-0 h-full min-w-0">
169
<NoteEditor />
1710
</div>

0 commit comments

Comments
 (0)