Skip to content
Merged
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
49 changes: 11 additions & 38 deletions ui/desktop/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import { ToastContainer } from 'react-toastify';
import { extractExtensionName } from './components/settings/extensions/utils';
import { GoosehintsModal } from './components/GoosehintsModal';
import { SessionDetails, fetchSessionDetails } from './sessions';

Check warning on line 14 in ui/desktop/src/App.tsx

View workflow job for this annotation

GitHub Actions / Lint Electron Desktop App

'fetchSessionDetails' is defined but never used. Allowed unused vars must match /^_/u

import WelcomeView from './components/WelcomeView';
import ChatView from './components/ChatView';
Expand All @@ -21,6 +21,7 @@
import ConfigureProvidersView from './components/settings/providers/ConfigureProvidersView';
import SessionsView from './components/sessions/SessionsView';
import ProviderSettings from './components/settings_v2/providers/ProviderSettingsPage';
import { useChat } from './hooks/useChat';

import 'react-toastify/dist/ReactToastify.css';

Expand All @@ -43,7 +44,7 @@
| {
resumedSession?: SessionDetails;
}
| Record<string, any>;

Check warning on line 47 in ui/desktop/src/App.tsx

View workflow job for this annotation

GitHub Actions / Lint Electron Desktop App

Unexpected any. Specify a different type
};

export default function App() {
Expand All @@ -61,7 +62,7 @@

const { switchModel } = useModel();
const { addRecentModel } = useRecentModels();
const setView = (view: View, viewOptions: Record<any, any> = {}) => {

Check warning on line 65 in ui/desktop/src/App.tsx

View workflow job for this annotation

GitHub Actions / Lint Electron Desktop App

Unexpected any. Specify a different type

Check warning on line 65 in ui/desktop/src/App.tsx

View workflow job for this annotation

GitHub Actions / Lint Electron Desktop App

Unexpected any. Specify a different type
setInternalView({ view, viewOptions });
};

Expand All @@ -74,7 +75,7 @@
}

useEffect(() => {
const handleAddExtension = (_: any, link: string) => {

Check warning on line 78 in ui/desktop/src/App.tsx

View workflow job for this annotation

GitHub Actions / Lint Electron Desktop App

Unexpected any. Specify a different type
const command = extractCommand(link);
const extName = extractExtensionName(link);
window.electron.logInfo(`Adding extension from deep link ${link}`);
Expand Down Expand Up @@ -149,43 +150,12 @@
};

setupStoredProvider();
}, []);

Check warning on line 153 in ui/desktop/src/App.tsx

View workflow job for this annotation

GitHub Actions / Lint Electron Desktop App

React Hook useEffect has missing dependencies: 'addRecentModel' and 'switchModel'. Either include them or remove the dependency array

// Check for resumeSessionId in URL parameters
useEffect(() => {
const checkForResumeSession = async () => {
const urlParams = new URLSearchParams(window.location.search);
const resumeSessionId = urlParams.get('resumeSessionId');

if (!resumeSessionId) {
return;
}

setIsLoadingSession(true);
try {
const sessionDetails = await fetchSessionDetails(resumeSessionId);

// Only set view if we have valid session details
if (sessionDetails && sessionDetails.session_id) {
setView('chat', {
resumedSession: sessionDetails,
});
} else {
console.error('Invalid session details received');
}
} catch (error) {
console.error('Failed to fetch session details:', error);
} finally {
// Always clear the loading state
setIsLoadingSession(false);
}
};

checkForResumeSession();
}, []);
const { chat, setChat } = useChat({ setView, setIsLoadingSession });

useEffect(() => {
const handleFatalError = (_: any, errorMessage: string) => {

Check warning on line 158 in ui/desktop/src/App.tsx

View workflow job for this annotation

GitHub Actions / Lint Electron Desktop App

Unexpected any. Specify a different type
setFatalError(errorMessage);
};

Expand Down Expand Up @@ -233,6 +203,13 @@
return <ErrorScreen error={fatalError} onReload={() => window.electron.reloadApp()} />;
}

if (isLoadingSession)
return (
<div className="flex justify-center items-center py-12">
<div className="animate-spin rounded-full h-8 w-8 border-t-2 border-b-2 border-textStandard"></div>
</div>
);

return (
<>
<ToastContainer
Expand Down Expand Up @@ -300,16 +277,12 @@
)}
{view === 'chat' && !isLoadingSession && (
<ChatView
chat={chat}
setChat={setChat}
setView={setView}
viewOptions={viewOptions}
setIsGoosehintsModalOpen={setIsGoosehintsModalOpen}
/>
)}
{view === 'chat' && isLoadingSession && (
<div className="flex justify-center items-center py-12">
<div className="animate-spin rounded-full h-8 w-8 border-t-2 border-b-2 border-textStandard"></div>
</div>
)}
{view === 'sessions' && <SessionsView setView={setView} />}
</div>
</div>
Expand Down
40 changes: 8 additions & 32 deletions ui/desktop/src/components/ChatView.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useEffect, useRef, useState, useMemo } from 'react';
import { getApiUrl } from '../config';
import { generateSessionId } from '../sessions';

Check warning on line 3 in ui/desktop/src/components/ChatView.tsx

View workflow job for this annotation

GitHub Actions / Lint Electron Desktop App

'generateSessionId' is defined but never used. Allowed unused vars must match /^_/u
import BottomMenu from './BottomMenu';
import FlappyGoose from './FlappyGoose';
import GooseMessage from './GooseMessage';
Expand All @@ -11,7 +11,7 @@
import { Card } from './ui/card';
import { ScrollArea, ScrollAreaHandle } from './ui/scroll-area';
import UserMessage from './UserMessage';
import { askAi } from '../utils/askAI';

Check warning on line 14 in ui/desktop/src/components/ChatView.tsx

View workflow job for this annotation

GitHub Actions / Lint Electron Desktop App

'askAi' is defined but never used. Allowed unused vars must match /^_/u
import Splash from './Splash';
import 'react-toastify/dist/ReactToastify.css';
import { useMessageStream } from '../hooks/useMessageStream';
Expand All @@ -27,7 +27,7 @@
} from '../types/message';

export interface ChatType {
id: number;
id: string;
title: string;
// messages up to this index are presumed to be "history" from a resumed session, this is used to track older tool confirmation requests
// anything before this index should not render any buttons, but anything after should
Expand All @@ -36,40 +36,16 @@
}

export default function ChatView({
chat,
setChat,
setView,
viewOptions,
setIsGoosehintsModalOpen,
}: {
chat: ChatType;
setChat: (chat: ChatType) => void;
setView: (view: View, viewOptions?: Record<any, any>) => void;
viewOptions?: Record<any, any>;
setIsGoosehintsModalOpen: (isOpen: boolean) => void;
}) {
// Check if we're resuming a session
const resumedSession = viewOptions?.resumedSession;

// Generate or retrieve session ID
// The session ID should not change for the duration of the chat
const sessionId = resumedSession?.session_id || generateSessionId();

const [chat, setChat] = useState<ChatType>(() => {
// If resuming a session, convert the session messages to our format
if (resumedSession) {
return {
id: resumedSession.session_id,
title: resumedSession.metadata?.description || `ID: ${resumedSession.session_id}`,
messages: resumedSession.messages,
messageHistoryIndex: resumedSession.messages.length,
};
}

return {
id: sessionId,
title: 'New Chat',
messages: [],
messageHistoryIndex: 0,
};
});

const [messageMetadata, setMessageMetadata] = useState<Record<string, string[]>>({});
const [hasMessages, setHasMessages] = useState(false);
const [lastInteractionTime, setLastInteractionTime] = useState<number>(Date.now());
Expand All @@ -89,8 +65,8 @@
handleSubmit: _submitMessage,
} = useMessageStream({
api: getApiUrl('/reply'),
initialMessages: resumedSession ? resumedSession.messages : chat?.messages || [],
body: { session_id: sessionId, session_working_dir: window.appConfig.get('GOOSE_WORKING_DIR') },
initialMessages: chat.messages,
body: { session_id: chat.id, session_working_dir: window.appConfig.get('GOOSE_WORKING_DIR') },
onFinish: async (message, _reason) => {
window.electron.stopPowerSaveBlocker();

Expand Down Expand Up @@ -122,7 +98,7 @@
const updatedChat = { ...prevChat, messages };
return updatedChat;
});
}, [messages, sessionId, resumedSession]);
}, [messages]);

useEffect(() => {
if (messages.length > 0) {
Expand Down
55 changes: 55 additions & 0 deletions ui/desktop/src/hooks/useChat.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { useEffect, useState } from 'react';
import { ChatType } from '../components/ChatView';
import { fetchSessionDetails, generateSessionId } from '../sessions';

type UseChatArgs = {
setIsLoadingSession: (isLoading: boolean) => void;
setView: (view: string) => void;
};
export const useChat = ({ setIsLoadingSession, setView }: UseChatArgs) => {
const [chat, setChat] = useState<ChatType>({
id: generateSessionId(),
title: 'New Chat',
messages: [],
messageHistoryIndex: 0,
});

// Check for resumeSessionId in URL parameters
useEffect(() => {
const checkForResumeSession = async () => {
const urlParams = new URLSearchParams(window.location.search);
const resumeSessionId = urlParams.get('resumeSessionId');

if (!resumeSessionId) {
return;
}

setIsLoadingSession(true);
try {
const sessionDetails = await fetchSessionDetails(resumeSessionId);

// Only set view if we have valid session details
if (sessionDetails && sessionDetails.session_id) {
setChat({
id: sessionDetails.session_id,
title: sessionDetails.metadata?.description || `ID: ${sessionDetails.session_id}`,
messages: sessionDetails.messages,
messageHistoryIndex: sessionDetails.messages.length,
});
setView('chat');
} else {
console.error('Invalid session details received');
}
} catch (error) {
console.error('Failed to fetch session details:', error);
} finally {
// Always clear the loading state
setIsLoadingSession(false);
}
};

checkForResumeSession();
}, []);

return { chat, setChat };
};
12 changes: 2 additions & 10 deletions ui/desktop/src/sessions.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { getApiUrl, getSecretKey } from './config';
import { Message } from './types/message';

export interface SessionMetadata {
description: string;
Expand Down Expand Up @@ -28,19 +29,10 @@ export interface SessionsResponse {
sessions: Session[];
}

export interface SessionMessage {
role: 'user' | 'assistant';
created: number;
content: {
type: string;
text: string;
}[];
}

export interface SessionDetails {
session_id: string;
metadata: SessionMetadata;
messages: SessionMessage[];
messages: Message[];
}

/**
Expand Down
Loading