Skip to content
Closed
35 changes: 27 additions & 8 deletions ui/desktop/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useEffect, useRef, useState } from 'react';
import { useEffect, useRef, useState } from 'react';
import { IpcRendererEvent } from 'electron';
import { openSharedSessionFromDeepLink } from './sessionLinks';
import { initializeSystem } from './utils/providerUtils';
Expand All @@ -8,6 +8,7 @@ import { ToastContainer } from 'react-toastify';
import { toastService } from './toasts';
import { extractExtensionName } from './components/settings/extensions/utils';
import { GoosehintsModal } from './components/GoosehintsModal';
import { type ExtensionConfig } from './extensions';

import ChatView from './components/ChatView';
import SuspenseLoader from './suspense-loader';
Expand Down Expand Up @@ -46,10 +47,28 @@ export type View =
| 'recipeEditor'
| 'permission';

export type ViewOptions =
| SettingsViewOptions
| { resumedSession?: SessionDetails }
| Record<string, unknown>;
export type ViewOptions = {
// Settings view options
extensionId?: string;
showEnvVars?: boolean;
deepLinkConfig?: ExtensionConfig;

// Session view options
resumedSession?: SessionDetails;
sessionDetails?: SessionDetails;
error?: string;
shareToken?: string;
baseUrl?: string;

// Recipe editor options
config?: unknown;

// Permission view options
parentView?: View;

// Generic options
[key: string]: unknown;
};

export type ViewConfig = {
view: View;
Expand Down Expand Up @@ -103,7 +122,7 @@ export default function App() {
return `${cmd} ${args.join(' ')}`.trim();
}

function extractRemoteUrl(link: string): string {
function extractRemoteUrl(link: string): string | null {
const url = new URL(link);
return url.searchParams.get('url');
}
Expand Down Expand Up @@ -164,7 +183,7 @@ export default function App() {
if (provider && model) {
setView('chat');
try {
await initializeSystem(provider, model, {
await initializeSystem(provider as string, model as string, {
getExtensions,
addExtension,
});
Expand Down Expand Up @@ -289,7 +308,7 @@ export default function App() {
};
setView(viewFromUrl, initialViewOptions);
} else {
setView(viewFromUrl);
setView(viewFromUrl as View);
}
}
window.electron.on('set-view', handleSetView);
Expand Down
1 change: 0 additions & 1 deletion ui/desktop/src/components/AgentHeader.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import React from 'react';

interface AgentHeaderProps {
title: string;
Expand Down
25 changes: 12 additions & 13 deletions ui/desktop/src/components/ChatInput.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useRef, useState, useEffect, useCallback } from 'react';
import React, { useRef, useState, useEffect, useMemo } from 'react';
import { Button } from './ui/button';
import type { View } from '../App';
import Stop from './ui/Stop';
Expand Down Expand Up @@ -148,21 +148,20 @@ export default function ChatInput({
}, [droppedFiles, processedFilePaths, displayValue]);

// Debounced function to update actual value
const debouncedSetValue = useCallback((val: string) => {
debounce((value: string) => {
const debouncedSetValue = useMemo(
() => debounce((value: string) => {
setValue(value);
}, 150)(val);
}, []);
}, 150),
[setValue]
);

// Debounced autosize function
const debouncedAutosize = useCallback(
(textArea: HTMLTextAreaElement) => {
debounce((element: HTMLTextAreaElement) => {
element.style.height = '0px'; // Reset height
const scrollHeight = element.scrollHeight;
element.style.height = Math.min(scrollHeight, maxHeight) + 'px';
}, 150)(textArea);
},
const debouncedAutosize = useMemo(
() => debounce((element: HTMLTextAreaElement) => {
element.style.height = '0px'; // Reset height
const scrollHeight = element.scrollHeight;
element.style.height = Math.min(scrollHeight, maxHeight) + 'px';
}, 150),
[maxHeight]
);

Expand Down
2 changes: 1 addition & 1 deletion ui/desktop/src/components/ChatView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ function ChatContent({

// Update chat messages when they change and save to sessionStorage
useEffect(() => {
setChat((prevChat) => {
setChat((prevChat: ChatType) => {
const updatedChat = { ...prevChat, messages };
return updatedChat;
});
Expand Down
6 changes: 3 additions & 3 deletions ui/desktop/src/components/ConfigContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ export const ConfigProvider: React.FC<ConfigProviderProps> = ({ children }) => {

const reloadConfig = useCallback(async () => {
const response = await readAllConfig();
setConfig(response.data.config || {});
setConfig(response.data?.config || {});
}, []);

const upsert = useCallback(
Expand Down Expand Up @@ -186,7 +186,7 @@ export const ConfigProvider: React.FC<ConfigProviderProps> = ({ children }) => {
(async () => {
// Load config
const configResponse = await readAllConfig();
setConfig(configResponse.data.config || {});
setConfig(configResponse.data?.config || {});

// Load providers
try {
Expand All @@ -199,7 +199,7 @@ export const ConfigProvider: React.FC<ConfigProviderProps> = ({ children }) => {
// Load extensions
try {
const extensionsResponse = await apiGetExtensions();
setExtensionsList(extensionsResponse.data.extensions);
setExtensionsList(extensionsResponse.data?.extensions || []);
} catch (error) {
console.error('Failed to load extensions:', error);
}
Expand Down
2 changes: 1 addition & 1 deletion ui/desktop/src/components/ErrorBoundary.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ window.addEventListener('error', (event) => {
);
});

export function ErrorUI({ error }) {
export function ErrorUI({ error }: { error: Error }) {
return (
<div className="fixed inset-0 w-full h-full flex flex-col items-center justify-center gap-6 bg-background">
<div className="flex flex-col items-center gap-4 max-w-[600px] text-center px-6">
Expand Down
2 changes: 1 addition & 1 deletion ui/desktop/src/components/FlappyGoose.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ const FlappyGoose: React.FC<FlappyGooseProps> = ({ onClose }) => {
useEffect(() => {
const frames = [svg1, svg7];
frames.forEach((src, index) => {
const img = new Image();
const img = new Image() as HTMLImageElement;
img.src = src;
img.onload = () => {
framesLoaded.current += 1;
Expand Down
20 changes: 14 additions & 6 deletions ui/desktop/src/components/GooseLogo.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import React from 'react';
import { Goose, Rain } from './icons/Goose';

export default function GooseLogo({ className = '', size = 'default', hover = true }) {
interface GooseLogoProps {
className?: string;
size?: 'default' | 'small';
hover?: boolean;
}

export default function GooseLogo({ className = '', size = 'default', hover = true }: GooseLogoProps) {
const sizes = {
default: {
frame: 'w-16 h-16',
Expand All @@ -13,15 +18,18 @@ export default function GooseLogo({ className = '', size = 'default', hover = tr
rain: 'w-[150px] h-[150px]',
goose: 'w-8 h-8',
},
};
} as const;

const currentSize = sizes[size];

return (
<div
className={`${className} ${sizes[size].frame} ${hover ? 'group/with-hover' : ''} relative overflow-hidden`}
className={`${className} ${currentSize.frame} ${hover ? 'group/with-hover' : ''} relative overflow-hidden`}
>
<Rain
className={`${sizes[size].rain} absolute left-0 bottom-0 ${hover ? 'opacity-0 group-hover/with-hover:opacity-100' : ''} transition-all duration-300 z-1`}
className={`${currentSize.rain} absolute left-0 bottom-0 ${hover ? 'opacity-0 group-hover/with-hover:opacity-100' : ''} transition-all duration-300 z-1`}
/>
<Goose className={`${sizes[size].goose} absolute left-0 bottom-0 z-2`} />
<Goose className={`${currentSize.goose} absolute left-0 bottom-0 z-2`} />
</div>
);
}
2 changes: 1 addition & 1 deletion ui/desktop/src/components/GooseMessage.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useEffect, useMemo, useRef } from 'react';
import { useEffect, useMemo, useRef } from 'react';
import LinkPreview from './LinkPreview';
import ImagePreview from './ImagePreview';
import GooseResponseForm from './GooseResponseForm';
Expand Down
10 changes: 5 additions & 5 deletions ui/desktop/src/components/GoosehintsModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Card } from './ui/card';
import { Button } from './ui/button';
import { Check } from './icons';

const Modal = ({ children }) => (
const Modal = ({ children }: { children: React.ReactNode }) => (
<div className="fixed inset-0 bg-black/20 dark:bg-white/20 backdrop-blur-sm transition-colors animate-[fadein_200ms_ease-in_forwards] z-[1000]">
<Card className="fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 flex flex-col min-w-[80%] min-h-[80%] bg-bgApp rounded-xl overflow-hidden shadow-none px-8 pt-[24px] pb-0">
<div className="flex flex-col flex-1 space-y-8 text-base text-textStandard h-full">
Expand Down Expand Up @@ -48,13 +48,13 @@ const ModalHelpText = () => (
</div>
);

const ModalError = ({ error }) => (
const ModalError = ({ error }: { error: Error }) => (
<div className="text-sm text-textSubtle">
<div className="text-red-600">Error reading .goosehints file: {JSON.stringify(error)}</div>
</div>
);

const ModalFileInfo = ({ filePath, found }) => (
const ModalFileInfo = ({ filePath, found }: { filePath: string; found: boolean }) => (
<div className="text-sm font-medium">
{found ? (
<div className="text-green-600">
Expand All @@ -66,7 +66,7 @@ const ModalFileInfo = ({ filePath, found }) => (
</div>
);

const ModalButtons = ({ onSubmit, onCancel }) => (
const ModalButtons = ({ onSubmit, onCancel }: { onSubmit: () => void; onCancel: () => void }) => (
<div className="-ml-8 -mr-8">
<Button
type="submit"
Expand All @@ -87,7 +87,7 @@ const ModalButtons = ({ onSubmit, onCancel }) => (
</div>
);

const getGoosehintsFile = async (filePath) => await window.electron.readFile(filePath);
const getGoosehintsFile = async (filePath: string) => await window.electron.readFile(filePath);

type GoosehintsModalProps = {
directory: string;
Expand Down
2 changes: 1 addition & 1 deletion ui/desktop/src/components/ImagePreview.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState, useEffect } from 'react';
import { useState, useEffect } from 'react';

interface ImagePreviewProps {
src: string;
Expand Down
2 changes: 1 addition & 1 deletion ui/desktop/src/components/LayingEggLoader.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react';
import { useEffect, useState } from 'react';
import { Geese } from './icons/Geese';

export default function LayingEggLoader() {
Expand Down
9 changes: 5 additions & 4 deletions ui/desktop/src/components/LinkPreview.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react';
import { useEffect, useState } from 'react';
import { Card } from './ui/card';

interface Metadata {
Expand Down Expand Up @@ -85,10 +85,11 @@ export default function LinkPreview({ url }: LinkPreviewProps) {
if (mounted) {
setMetadata(data);
}
} catch (error) {
} catch (err) {
if (mounted) {
console.error('❌ Failed to fetch metadata:', error);
setError(error.message || 'Failed to fetch metadata');
console.error('❌ Failed to fetch metadata:', err);
const errorMessage = err instanceof Error ? err.message : 'Failed to fetch metadata';
setError(errorMessage);
}
} finally {
if (mounted) {
Expand Down
1 change: 0 additions & 1 deletion ui/desktop/src/components/LoadingGoose.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import React from 'react';
import GooseLogo from './GooseLogo';

const LoadingGoose = () => {
Expand Down
1 change: 0 additions & 1 deletion ui/desktop/src/components/LoadingPlaceholder.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import React from 'react';

export function LoadingPlaceholder() {
return (
Expand Down
13 changes: 7 additions & 6 deletions ui/desktop/src/components/ProviderGrid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export function ProviderGrid({ onSubmit }: ProviderGridProps) {
});
}, [activeKeys]);

const handleConfigure = async (provider) => {
const handleConfigure = async (provider: { id: string; name: string; isConfigured: boolean; description: string }) => {
const providerId = provider.id.toLowerCase();

const modelName = getDefaultModel(providerId);
Expand All @@ -63,7 +63,7 @@ export function ProviderGrid({ onSubmit }: ProviderGridProps) {
onSubmit?.();
};

const handleAddKeys = (provider) => {
const handleAddKeys = (provider: { id: string; name: string; isConfigured: boolean; description: string }) => {
setSelectedId(provider.id);
setShowSetupModal(true);
};
Expand All @@ -74,7 +74,7 @@ export function ProviderGrid({ onSubmit }: ProviderGridProps) {
const provider = providers.find((p) => p.id === selectedId)?.name;
if (!provider) return;

const requiredKeys = required_keys[provider];
const requiredKeys = required_keys[provider as keyof typeof required_keys];
if (!requiredKeys || requiredKeys.length === 0) {
console.error(`No keys found for provider ${provider}`);
return;
Expand Down Expand Up @@ -145,12 +145,13 @@ export function ProviderGrid({ onSubmit }: ProviderGridProps) {

setShowSetupModal(false);
setSelectedId(null);
} catch (error) {
console.error('Error handling modal submit:', error);
} catch (err) {
console.error('Error handling modal submit:', err);
const errorMessage = err instanceof Error ? err.message : 'Unknown error occurred';
toastError({
title: provider,
msg: `Failed to ${providers.find((p) => p.id === selectedId)?.isConfigured ? 'update' : 'add'} configuration`,
traceback: error.message,
traceback: errorMessage,
});
}
};
Expand Down
7 changes: 5 additions & 2 deletions ui/desktop/src/components/RecipeEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export default function RecipeEditor({ config }: RecipeEditorProps) {
}
}
// Fall back to config if available, using extension names
const exts = [];
const exts: string[] = [];
return exts;
});
// Section visibility state
Expand Down Expand Up @@ -125,7 +125,10 @@ export default function RecipeEditor({ config }: RecipeEditorProps) {
delete cleanExtension.enabled;
// Remove legacy envs which could potentially include secrets
// env_keys will work but rely on the end user having setup those keys themselves
delete cleanExtension.envs;
if ('envs' in cleanExtension) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
delete (cleanExtension as any).envs;
}
return cleanExtension;
})
.filter(Boolean) as FullExtensionConfig[],
Expand Down
1 change: 0 additions & 1 deletion ui/desktop/src/components/Splash.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import React from 'react';
import SplashPills from './SplashPills';
import GooseLogo from './GooseLogo';

Expand Down
1 change: 0 additions & 1 deletion ui/desktop/src/components/SplashPills.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import React from 'react';
import MarkdownContent from './MarkdownContent';

function truncateText(text: string, maxLength: number = 100): string {
Expand Down
2 changes: 1 addition & 1 deletion ui/desktop/src/components/ToolCallArguments.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState } from 'react';
import { useState } from 'react';
import MarkdownContent from './MarkdownContent';
import Expand from './ui/Expand';

Expand Down
2 changes: 1 addition & 1 deletion ui/desktop/src/components/UserMessage.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useRef, useMemo } from 'react';
import { useRef, useMemo } from 'react';
import LinkPreview from './LinkPreview';
import ImagePreview from './ImagePreview';
import { extractUrls } from '../utils/urlUtils';
Expand Down
1 change: 0 additions & 1 deletion ui/desktop/src/components/WelcomeGooseLogo.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import React from 'react';
import { Goose, Rain } from './icons/Goose';

export default function WelcomeGooseLogo({ className = '' }) {
Expand Down
1 change: 0 additions & 1 deletion ui/desktop/src/components/WelcomeView.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import React from 'react';
import { ProviderGrid } from './ProviderGrid';
import { ScrollArea } from './ui/scroll-area';
import { Button } from './ui/button';
Expand Down
Loading
Loading