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
26 changes: 16 additions & 10 deletions ui/desktop/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,13 @@ const HubRouteWrapper = ({
setChat,
setPairChat,
setIsGoosehintsModalOpen,
isExtensionsLoading,
}: {
chat: ChatType;
setChat: (chat: ChatType) => void;
setPairChat: (chat: ChatType) => void;
setIsGoosehintsModalOpen: (isOpen: boolean) => void;
isExtensionsLoading: boolean;
}) => {
const navigate = useNavigate();
const setView = createNavigationHandler(navigate);
Expand All @@ -62,6 +64,7 @@ const HubRouteWrapper = ({
setPairChat={setPairChat}
setView={setView}
setIsGoosehintsModalOpen={setIsGoosehintsModalOpen}
isExtensionsLoading={isExtensionsLoading}
/>
);
};
Expand Down Expand Up @@ -400,6 +403,7 @@ export default function App() {
const [agentWaitingMessage, setAgentWaitingMessage] = useState<string | null>(null);
const [isLoadingSharedSession, setIsLoadingSharedSession] = useState(false);
const [sharedSessionError, setSharedSessionError] = useState<string | null>(null);
const [isExtensionsLoading, setIsExtensionsLoading] = useState(false);

// Add separate state for pair chat to maintain its own conversation
const [pairChat, setPairChat] = useState<ChatType>({
Expand Down Expand Up @@ -482,6 +486,7 @@ export default function App() {
addExtension,
setPairChat,
setMessage: setAgentWaitingMessage,
setIsExtensionsLoading,
provider: provider as string,
model: model as string,
});
Expand Down Expand Up @@ -802,20 +807,21 @@ export default function App() {
<Route
index
element={
<ProviderGuard>
<ProviderGuard setIsExtensionsLoading={setIsExtensionsLoading}>
<HubRouteWrapper
chat={chat}
setChat={setChat}
setPairChat={setPairChat}
setIsGoosehintsModalOpen={setIsGoosehintsModalOpen}
isExtensionsLoading={isExtensionsLoading}
/>
</ProviderGuard>
}
/>
<Route
path="pair"
element={
<ProviderGuard>
<ProviderGuard setIsExtensionsLoading={setIsExtensionsLoading}>
<ChatProvider
chat={pairChat}
setChat={setPairChat}
Expand All @@ -836,55 +842,55 @@ export default function App() {
<Route
path="settings"
element={
<ProviderGuard>
<ProviderGuard setIsExtensionsLoading={setIsExtensionsLoading}>
<SettingsRoute />
</ProviderGuard>
}
/>
<Route
path="extensions"
element={
<ProviderGuard>
<ProviderGuard setIsExtensionsLoading={setIsExtensionsLoading}>
<ExtensionsRoute />
</ProviderGuard>
}
/>
<Route
path="sessions"
element={
<ProviderGuard>
<ProviderGuard setIsExtensionsLoading={setIsExtensionsLoading}>
<SessionsRoute />
</ProviderGuard>
}
/>
<Route
path="schedules"
element={
<ProviderGuard>
<ProviderGuard setIsExtensionsLoading={setIsExtensionsLoading}>
<SchedulesRoute />
</ProviderGuard>
}
/>
<Route
path="recipes"
element={
<ProviderGuard>
<ProviderGuard setIsExtensionsLoading={setIsExtensionsLoading}>
<RecipesRoute />
</ProviderGuard>
}
/>
<Route
path="recipe-editor"
element={
<ProviderGuard>
<ProviderGuard setIsExtensionsLoading={setIsExtensionsLoading}>
<RecipeEditorRoute />
</ProviderGuard>
}
/>
<Route
path="shared-session"
element={
<ProviderGuard>
<ProviderGuard setIsExtensionsLoading={setIsExtensionsLoading}>
<SharedSessionRouteWrapper
isLoadingSharedSession={isLoadingSharedSession}
setIsLoadingSharedSession={setIsLoadingSharedSession}
Expand All @@ -896,7 +902,7 @@ export default function App() {
<Route
path="permission"
element={
<ProviderGuard>
<ProviderGuard setIsExtensionsLoading={setIsExtensionsLoading}>
<PermissionRoute />
</ProviderGuard>
}
Expand Down
64 changes: 37 additions & 27 deletions ui/desktop/src/components/ChatInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ interface ChatInputProps {
autoSubmit: boolean;
setAncestorMessages?: (messages: Message[]) => void;
append?: (message: Message) => void;
isExtensionsLoading?: boolean;
}

export default function ChatInput({
Expand All @@ -111,6 +112,7 @@ export default function ChatInput({
autoSubmit = false,
append,
setAncestorMessages,
isExtensionsLoading = false,
}: ChatInputProps) {
const [_value, setValue] = useState(initialValue);
const [displayValue, setDisplayValue] = useState(initialValue); // For immediate visual feedback
Expand Down Expand Up @@ -1125,6 +1127,25 @@ export default function ChatInput({
const isAnyImageLoading = pastedImages.some((img) => img.isLoading);
const isAnyDroppedFileLoading = allDroppedFiles.some((file) => file.isLoading);

const isSubmitButtonDisabled =
!hasSubmittableContent ||
isAnyImageLoading ||
isAnyDroppedFileLoading ||
isRecording ||
isTranscribing ||
isCompacting ||
!agentIsReady ||
isExtensionsLoading;

const isUserInputDisabled =
isAnyImageLoading ||
isAnyDroppedFileLoading ||
isRecording ||
isTranscribing ||
isCompacting ||
!agentIsReady ||
isExtensionsLoading;

// Queue management functions - no storage persistence, only in-memory
const handleRemoveQueuedMessage = (messageId: string) => {
setQueuedMessages((prev) => prev.filter((msg) => msg.id !== messageId));
Expand Down Expand Up @@ -1239,6 +1260,7 @@ export default function ChatInput({
onBlur={() => setIsFocused(false)}
ref={textAreaRef}
rows={1}
disabled={isUserInputDisabled}
style={{
maxHeight: `${maxHeight}px`,
overflowY: 'auto',
Expand Down Expand Up @@ -1349,23 +1371,9 @@ export default function ChatInput({
size="sm"
shape="round"
variant="outline"
disabled={
!hasSubmittableContent ||
isAnyImageLoading ||
isAnyDroppedFileLoading ||
isRecording ||
isTranscribing ||
isCompacting ||
!agentIsReady
}
disabled={isSubmitButtonDisabled}
className={`rounded-full px-10 py-2 flex items-center gap-2 ${
!hasSubmittableContent ||
isAnyImageLoading ||
isAnyDroppedFileLoading ||
isRecording ||
isTranscribing ||
isCompacting ||
!agentIsReady
isSubmitButtonDisabled
? 'bg-slate-600 text-white cursor-not-allowed opacity-50 border-slate-600'
: 'bg-slate-600 text-white hover:bg-slate-700 border-slate-600 hover:cursor-pointer'
}`}
Expand All @@ -1377,17 +1385,19 @@ export default function ChatInput({
</TooltipTrigger>
<TooltipContent>
<p>
{isCompacting
? 'Compacting conversation...'
: isAnyImageLoading
? 'Waiting for images to save...'
: isAnyDroppedFileLoading
? 'Processing dropped files...'
: isRecording
? 'Recording...'
: isTranscribing
? 'Transcribing...'
: (chatContext?.agentWaitingMessage ?? 'Send')}
{isExtensionsLoading
? 'Loading extensions...'
: isCompacting
? 'Compacting conversation...'
: isAnyImageLoading
? 'Waiting for images to save...'
: isAnyDroppedFileLoading
? 'Processing dropped files...'
: isRecording
? 'Recording...'
: isTranscribing
? 'Transcribing...'
: (chatContext?.agentWaitingMessage ?? 'Send')}
</p>
</TooltipContent>
</Tooltip>
Expand Down
20 changes: 11 additions & 9 deletions ui/desktop/src/components/OllamaSetup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@ import { Ollama } from './icons';
interface OllamaSetupProps {
onSuccess: () => void;
onCancel: () => void;
setIsExtensionsLoading?: (loading: boolean) => void;
}

export function OllamaSetup({ onSuccess, onCancel }: OllamaSetupProps) {
export function OllamaSetup({ onSuccess, onCancel, setIsExtensionsLoading }: OllamaSetupProps) {
const { addExtension, getExtensions, upsert } = useConfig();
const [isChecking, setIsChecking] = useState(true);
const [ollamaDetected, setOllamaDetected] = useState(false);
Expand Down Expand Up @@ -113,6 +114,7 @@ export function OllamaSetup({ onSuccess, onCancel }: OllamaSetupProps) {
await initializeSystem('ollama', getPreferredModel(), {
getExtensions,
addExtension,
setIsExtensionsLoading,
});

toastService.success({
Expand Down Expand Up @@ -157,7 +159,9 @@ export function OllamaSetup({ onSuccess, onCancel }: OllamaSetupProps) {
{ollamaDetected ? (
<div className="space-y-4">
<div className="flex items-start mb-16">
<span className="inline-block px-2 py-1 text-xs font-medium bg-green-600 text-white rounded-full">Ollama is detected and running</span>
<span className="inline-block px-2 py-1 text-xs font-medium bg-green-600 text-white rounded-full">
Ollama is detected and running
</span>
</div>

{modelStatus === 'checking' ? (
Expand Down Expand Up @@ -185,14 +189,10 @@ export function OllamaSetup({ onSuccess, onCancel }: OllamaSetupProps) {
) : modelStatus === 'downloading' ? (
<div className="space-y-4">
<div className="bg-background-info/10 border border-border-info rounded-lg p-4">
<p className="text-text-info text-sm">
Downloading {getPreferredModel()}...
</p>
<p className="text-text-info text-sm">Downloading {getPreferredModel()}...</p>
{downloadProgress && (
<>
<p className="text-text-muted text-xs mt-2">
{downloadProgress.status}
</p>
<p className="text-text-muted text-xs mt-2">{downloadProgress.status}</p>
{downloadProgress.total && downloadProgress.completed && (
<div className="mt-3">
<div className="bg-background-muted rounded-full h-2 overflow-hidden">
Expand Down Expand Up @@ -225,7 +225,9 @@ export function OllamaSetup({ onSuccess, onCancel }: OllamaSetupProps) {
) : (
<div className="space-y-4">
<div className="flex items-start mb-16">
<span className="inline-block px-2 py-1 text-xs font-medium bg-orange-600 text-white rounded-full">Ollama is not detected on your system</span>
<span className="inline-block px-2 py-1 text-xs font-medium bg-orange-600 text-white rounded-full">
Ollama is not detected on your system
</span>
</div>

{isPolling ? (
Expand Down
6 changes: 5 additions & 1 deletion ui/desktop/src/components/ProviderGuard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@ import { OpenRouter } from './icons';

interface ProviderGuardProps {
children: React.ReactNode;
setIsExtensionsLoading?: (loading: boolean) => void;
}

export default function ProviderGuard({ children }: ProviderGuardProps) {
export default function ProviderGuard({ children, setIsExtensionsLoading }: ProviderGuardProps) {
const { read, getExtensions, addExtension } = useConfig();
const navigate = useNavigate();
const [isChecking, setIsChecking] = useState(true);
Expand Down Expand Up @@ -72,6 +73,7 @@ export default function ProviderGuard({ children }: ProviderGuardProps) {
await initializeSystem(provider as string, model as string, {
getExtensions,
addExtension,
setIsExtensionsLoading,
});

toastService.configure({ silent: false });
Expand Down Expand Up @@ -138,6 +140,7 @@ export default function ProviderGuard({ children }: ProviderGuardProps) {
await initializeSystem(provider as string, model as string, {
getExtensions,
addExtension,
setIsExtensionsLoading,
});

toastService.configure({ silent: false });
Expand Down Expand Up @@ -267,6 +270,7 @@ export default function ProviderGuard({ children }: ProviderGuardProps) {
setShowOllamaSetup(false);
setShowFirstTimeSetup(true);
}}
setIsExtensionsLoading={setIsExtensionsLoading}
/>
</div>
</div>
Expand Down
3 changes: 3 additions & 0 deletions ui/desktop/src/components/hub.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,15 @@ export default function Hub({
setPairChat,
setView,
setIsGoosehintsModalOpen,
isExtensionsLoading,
}: {
readyForAutoUserPrompt: boolean;
chat: ChatType;
setChat: (chat: ChatType) => void;
setPairChat: (chat: ChatType) => void;
setView: (view: View, viewOptions?: ViewOptions) => void;
setIsGoosehintsModalOpen: (isOpen: boolean) => void;
isExtensionsLoading: boolean;
}) {
// Handle chat input submission - create new chat and navigate to pair
const handleSubmit = (e: React.FormEvent) => {
Expand Down Expand Up @@ -101,6 +103,7 @@ export default function Hub({
disableAnimation={false}
sessionCosts={undefined}
setIsGoosehintsModalOpen={setIsGoosehintsModalOpen}
isExtensionsLoading={isExtensionsLoading}
/>
</div>
</ContextManagerProvider>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,14 @@ import { toastService } from '../../../toasts';
interface ProviderSettingsProps {
onClose: () => void;
isOnboarding: boolean;
setIsExtensionsLoading?: (loading: boolean) => void;
}

export default function ProviderSettings({ onClose, isOnboarding }: ProviderSettingsProps) {
export default function ProviderSettings({
onClose,
isOnboarding,
setIsExtensionsLoading,
}: ProviderSettingsProps) {
const { getProviders, upsert, getExtensions, addExtension } = useConfig();
const [loading, setLoading] = useState(true);
const [providers, setProviders] = useState<ProviderDetails[]>([]);
Expand Down Expand Up @@ -71,6 +76,7 @@ export default function ProviderSettings({ onClose, isOnboarding }: ProviderSett
await initializeSystem(provider.name, model, {
getExtensions,
addExtension,
setIsExtensionsLoading,
});

toastService.configure({ silent: false });
Expand All @@ -92,7 +98,7 @@ export default function ProviderSettings({ onClose, isOnboarding }: ProviderSett
});
}
},
[onClose, upsert, getExtensions, addExtension]
[onClose, upsert, getExtensions, addExtension, setIsExtensionsLoading]
);

return (
Expand Down
Loading
Loading