diff --git a/gadget-code/frontend/src/pages/ChatSessionView.tsx b/gadget-code/frontend/src/pages/ChatSessionView.tsx index b2ce064..083e2b7 100644 --- a/gadget-code/frontend/src/pages/ChatSessionView.tsx +++ b/gadget-code/frontend/src/pages/ChatSessionView.tsx @@ -39,7 +39,6 @@ export default function ChatSessionView() { const [workspaceMode, setWorkspaceMode] = useState(WorkspaceMode.Idle); const [isUpdatingMode, setIsUpdatingMode] = useState(false); const [isUpdatingProvider, setIsUpdatingProvider] = useState(false); - const [isUpdatingModel, setIsUpdatingModel] = useState(false); const [isUpdatingReasoning, setIsUpdatingReasoning] = useState(false); const [logExpanded, setLogExpanded] = useState(false); const [logs, setLogs] = useState([]); @@ -48,6 +47,12 @@ export default function ChatSessionView() { const [selectedProviderId, setSelectedProviderId] = useState(''); const [selectedModelId, setSelectedModelId] = useState(''); const [sessionReasoningEffort, setSessionReasoningEffort] = useState('off'); + const [isEditingProvider, setIsEditingProvider] = useState(false); + const [editProviderId, setEditProviderId] = useState(''); + const [editModelId, setEditModelId] = useState(''); + const [isEditingName, setIsEditingName] = useState(false); + const [editName, setEditName] = useState(''); + const [isUpdatingName, setIsUpdatingName] = useState(false); const messagesEndRef = useRef(null); const inputRef = useRef(null); @@ -435,29 +440,6 @@ export default function ChatSessionView() { } }; - const handleProviderChange = async (e: React.ChangeEvent) => { - if (!session) return; - - const newProviderId = e.target.value; - if (newProviderId === selectedProviderId) return; - - setIsUpdatingProvider(true); - try { - const updatedSession = await chatSessionApi.update(session._id, { - provider: newProviderId, - selectedModel: '' - }); - setSession(updatedSession); - setSelectedProviderId(newProviderId); - setSelectedModelId(''); - showToast('Provider changed. Please select a model.'); - } catch (err) { - showToast(`Failed to change provider: ${err instanceof Error ? err.message : 'Unknown error'}`); - } finally { - setIsUpdatingProvider(false); - } - }; - const getSelectedModelCapabilities = useCallback(() => { const provider = providers.find(p => p._id === selectedProviderId); if (!provider) return null; @@ -486,25 +468,14 @@ export default function ChatSessionView() { } }; - const handleModelChange = async (e: React.ChangeEvent) => { - if (!session) return; + const getProviderName = (id: string) => { + return providers.find(p => p._id === id)?.name ?? 'None'; + }; - const newModelId = e.target.value; - if (newModelId === selectedModelId) return; - - setIsUpdatingModel(true); - try { - const updatedSession = await chatSessionApi.update(session._id, { - selectedModel: newModelId - }); - setSession(updatedSession); - setSelectedModelId(newModelId); - showToast(`Model changed`); - } catch (err) { - showToast(`Failed to change model: ${err instanceof Error ? err.message : 'Unknown error'}`); - } finally { - setIsUpdatingModel(false); - } + const getModelName = (providerId: string, modelId: string) => { + if (!providerId || !modelId) return 'None'; + const provider = providers.find(p => p._id === providerId); + return provider?.models.find(m => m.id === modelId)?.name ?? modelId; }; const getSortedModels = (providerId: string) => { @@ -518,6 +489,62 @@ export default function ChatSessionView() { return name.slice(0, maxLength - 3) + '...'; }; + const handleStartEditProvider = () => { + setEditProviderId(selectedProviderId); + setEditModelId(selectedModelId); + setIsEditingProvider(true); + }; + + const handleSaveProvider = async () => { + if (!session || !editProviderId || !editModelId) return; + setIsUpdatingProvider(true); + try { + const updatedSession = await chatSessionApi.update(session._id, { + provider: editProviderId, + selectedModel: editModelId, + }); + setSession(updatedSession); + setSelectedProviderId(editProviderId); + setSelectedModelId(editModelId); + setIsEditingProvider(false); + showToast('Provider and model updated'); + } catch (err) { + showToast(`Failed to update: ${err instanceof Error ? err.message : 'Unknown error'}`); + } finally { + setIsUpdatingProvider(false); + } + }; + + const handleCancelEditProvider = () => { + setEditProviderId(selectedProviderId); + setEditModelId(selectedModelId); + setIsEditingProvider(false); + }; + + const handleStartEditName = () => { + setEditName(session?.name || ''); + setIsEditingName(true); + }; + + const handleSaveName = async () => { + if (!session || !editName.trim()) return; + setIsUpdatingName(true); + try { + const updatedSession = await chatSessionApi.update(session._id, { name: editName.trim() }); + setSession(updatedSession); + setIsEditingName(false); + showToast('Session name updated'); + } catch (err) { + showToast(`Failed to update name: ${err instanceof Error ? err.message : 'Unknown error'}`); + } finally { + setIsUpdatingName(false); + } + }; + + const handleCancelEditName = () => { + setIsEditingName(false); + }; + const handleSubmitPrompt = async (e: React.FormEvent) => { e.preventDefault(); if (!promptInput.trim() || isProcessing || !socketClient.connected || !sessionLocked) return; @@ -595,8 +622,10 @@ export default function ChatSessionView() { ); }, []); - const promptDisabled = isProcessing || workspaceMode !== WorkspaceMode.Agent || !sessionLocked || !session?.selectedModel; - const promptPlaceholder = workspaceMode !== WorkspaceMode.Agent + const promptDisabled = isProcessing || workspaceMode !== WorkspaceMode.Agent || !sessionLocked || !session?.selectedModel || isEditingProvider; + const promptPlaceholder = isEditingProvider + ? 'Save or cancel provider changes to continue.' + : workspaceMode !== WorkspaceMode.Agent ? 'Select Agent mode to enter a prompt.' : !session?.selectedModel ? 'Please select a model first.' @@ -636,7 +665,10 @@ export default function ChatSessionView() { )} {/* Content Area */} -
+
+ {isEditingProvider && ( +
+ )} {/* Chat View (75%) */}
{/* Messages */} @@ -700,53 +732,151 @@ export default function ChatSessionView() {
Name
-
{session?.name}
+ {isEditingName ? ( +
+ setEditName(e.target.value)} + onKeyDown={(e) => { + if (e.key === 'Enter') { e.preventDefault(); handleSaveName(); } + if (e.key === 'Escape') { e.preventDefault(); handleCancelEditName(); } + }} + className="flex-1 px-2 py-1.5 bg-bg-tertiary border border-border-default rounded text-text-primary text-sm focus:border-brand focus:outline-none" + autoFocus + /> + + +
+ ) : ( +
+
{session?.name}
+ +
+ )}
-
-
-
Provider
- + {isEditingProvider ? ( +
+
+
Provider
+ +
+
+
Model
+ +
+
+ + +
-
-
Model
- + + + + +
-
+ )}
Mode