diff --git a/ui/desktop/src/App.tsx b/ui/desktop/src/App.tsx index 78cd5ed5..fa3121b6 100644 --- a/ui/desktop/src/App.tsx +++ b/ui/desktop/src/App.tsx @@ -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'; @@ -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'; @@ -46,10 +47,28 @@ export type View = | 'recipeEditor' | 'permission'; -export type ViewOptions = - | SettingsViewOptions - | { resumedSession?: SessionDetails } - | Record; +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; @@ -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'); } @@ -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, }); @@ -289,7 +308,7 @@ export default function App() { }; setView(viewFromUrl, initialViewOptions); } else { - setView(viewFromUrl); + setView(viewFromUrl as View); } } window.electron.on('set-view', handleSetView); diff --git a/ui/desktop/src/components/AgentHeader.tsx b/ui/desktop/src/components/AgentHeader.tsx index 35f33a6b..d64b2bf8 100644 --- a/ui/desktop/src/components/AgentHeader.tsx +++ b/ui/desktop/src/components/AgentHeader.tsx @@ -1,4 +1,3 @@ -import React from 'react'; interface AgentHeaderProps { title: string; diff --git a/ui/desktop/src/components/ChatInput.tsx b/ui/desktop/src/components/ChatInput.tsx index 8767f9a5..77f4a2bb 100644 --- a/ui/desktop/src/components/ChatInput.tsx +++ b/ui/desktop/src/components/ChatInput.tsx @@ -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'; @@ -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] ); diff --git a/ui/desktop/src/components/ChatView.tsx b/ui/desktop/src/components/ChatView.tsx index ca0fc0b3..0e54d0a1 100644 --- a/ui/desktop/src/components/ChatView.tsx +++ b/ui/desktop/src/components/ChatView.tsx @@ -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; }); diff --git a/ui/desktop/src/components/ConfigContext.tsx b/ui/desktop/src/components/ConfigContext.tsx index 76631ab6..82032dee 100644 --- a/ui/desktop/src/components/ConfigContext.tsx +++ b/ui/desktop/src/components/ConfigContext.tsx @@ -74,7 +74,7 @@ export const ConfigProvider: React.FC = ({ children }) => { const reloadConfig = useCallback(async () => { const response = await readAllConfig(); - setConfig(response.data.config || {}); + setConfig(response.data?.config || {}); }, []); const upsert = useCallback( @@ -186,7 +186,7 @@ export const ConfigProvider: React.FC = ({ children }) => { (async () => { // Load config const configResponse = await readAllConfig(); - setConfig(configResponse.data.config || {}); + setConfig(configResponse.data?.config || {}); // Load providers try { @@ -199,7 +199,7 @@ export const ConfigProvider: React.FC = ({ 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); } diff --git a/ui/desktop/src/components/ErrorBoundary.tsx b/ui/desktop/src/components/ErrorBoundary.tsx index eec01f6f..1c29d2a6 100644 --- a/ui/desktop/src/components/ErrorBoundary.tsx +++ b/ui/desktop/src/components/ErrorBoundary.tsx @@ -14,7 +14,7 @@ window.addEventListener('error', (event) => { ); }); -export function ErrorUI({ error }) { +export function ErrorUI({ error }: { error: Error }) { return (
diff --git a/ui/desktop/src/components/FlappyGoose.tsx b/ui/desktop/src/components/FlappyGoose.tsx index 3cd8b589..5cd9cb35 100644 --- a/ui/desktop/src/components/FlappyGoose.tsx +++ b/ui/desktop/src/components/FlappyGoose.tsx @@ -216,7 +216,7 @@ const FlappyGoose: React.FC = ({ 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; diff --git a/ui/desktop/src/components/GooseLogo.tsx b/ui/desktop/src/components/GooseLogo.tsx index 1df25c62..4d8ea402 100644 --- a/ui/desktop/src/components/GooseLogo.tsx +++ b/ui/desktop/src/components/GooseLogo.tsx @@ -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', @@ -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 (
- +
); } diff --git a/ui/desktop/src/components/GooseMessage.tsx b/ui/desktop/src/components/GooseMessage.tsx index 79f62b97..6066386b 100644 --- a/ui/desktop/src/components/GooseMessage.tsx +++ b/ui/desktop/src/components/GooseMessage.tsx @@ -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'; diff --git a/ui/desktop/src/components/GoosehintsModal.tsx b/ui/desktop/src/components/GoosehintsModal.tsx index 8f78e886..19624efb 100644 --- a/ui/desktop/src/components/GoosehintsModal.tsx +++ b/ui/desktop/src/components/GoosehintsModal.tsx @@ -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 }) => (
@@ -48,13 +48,13 @@ const ModalHelpText = () => (
); -const ModalError = ({ error }) => ( +const ModalError = ({ error }: { error: Error }) => (
Error reading .goosehints file: {JSON.stringify(error)}
); -const ModalFileInfo = ({ filePath, found }) => ( +const ModalFileInfo = ({ filePath, found }: { filePath: string; found: boolean }) => (
{found ? (
@@ -66,7 +66,7 @@ const ModalFileInfo = ({ filePath, found }) => (
); -const ModalButtons = ({ onSubmit, onCancel }) => ( +const ModalButtons = ({ onSubmit, onCancel }: { onSubmit: () => void; onCancel: () => void }) => (
); -const getGoosehintsFile = async (filePath) => await window.electron.readFile(filePath); +const getGoosehintsFile = async (filePath: string) => await window.electron.readFile(filePath); type GoosehintsModalProps = { directory: string; diff --git a/ui/desktop/src/components/ImagePreview.tsx b/ui/desktop/src/components/ImagePreview.tsx index 7e6d66f5..29e0aff3 100644 --- a/ui/desktop/src/components/ImagePreview.tsx +++ b/ui/desktop/src/components/ImagePreview.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from 'react'; +import { useState, useEffect } from 'react'; interface ImagePreviewProps { src: string; diff --git a/ui/desktop/src/components/LayingEggLoader.tsx b/ui/desktop/src/components/LayingEggLoader.tsx index 3d96947f..faa4f806 100644 --- a/ui/desktop/src/components/LayingEggLoader.tsx +++ b/ui/desktop/src/components/LayingEggLoader.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react'; +import { useEffect, useState } from 'react'; import { Geese } from './icons/Geese'; export default function LayingEggLoader() { diff --git a/ui/desktop/src/components/LinkPreview.tsx b/ui/desktop/src/components/LinkPreview.tsx index 71b33417..f4fc835b 100644 --- a/ui/desktop/src/components/LinkPreview.tsx +++ b/ui/desktop/src/components/LinkPreview.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react'; +import { useEffect, useState } from 'react'; import { Card } from './ui/card'; interface Metadata { @@ -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) { diff --git a/ui/desktop/src/components/LoadingGoose.tsx b/ui/desktop/src/components/LoadingGoose.tsx index 38677d18..e3c623f2 100644 --- a/ui/desktop/src/components/LoadingGoose.tsx +++ b/ui/desktop/src/components/LoadingGoose.tsx @@ -1,4 +1,3 @@ -import React from 'react'; import GooseLogo from './GooseLogo'; const LoadingGoose = () => { diff --git a/ui/desktop/src/components/LoadingPlaceholder.tsx b/ui/desktop/src/components/LoadingPlaceholder.tsx index 2151fc25..4cae5174 100644 --- a/ui/desktop/src/components/LoadingPlaceholder.tsx +++ b/ui/desktop/src/components/LoadingPlaceholder.tsx @@ -1,4 +1,3 @@ -import React from 'react'; export function LoadingPlaceholder() { return ( diff --git a/ui/desktop/src/components/ProviderGrid.tsx b/ui/desktop/src/components/ProviderGrid.tsx index 9ad66b9d..1e3d6721 100644 --- a/ui/desktop/src/components/ProviderGrid.tsx +++ b/ui/desktop/src/components/ProviderGrid.tsx @@ -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); @@ -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); }; @@ -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; @@ -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, }); } }; diff --git a/ui/desktop/src/components/RecipeEditor.tsx b/ui/desktop/src/components/RecipeEditor.tsx index efbb85d2..a621d32c 100644 --- a/ui/desktop/src/components/RecipeEditor.tsx +++ b/ui/desktop/src/components/RecipeEditor.tsx @@ -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 @@ -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[], diff --git a/ui/desktop/src/components/Splash.tsx b/ui/desktop/src/components/Splash.tsx index 529dc343..ddad10d6 100644 --- a/ui/desktop/src/components/Splash.tsx +++ b/ui/desktop/src/components/Splash.tsx @@ -1,4 +1,3 @@ -import React from 'react'; import SplashPills from './SplashPills'; import GooseLogo from './GooseLogo'; diff --git a/ui/desktop/src/components/SplashPills.tsx b/ui/desktop/src/components/SplashPills.tsx index 83bd9bf1..18506b75 100644 --- a/ui/desktop/src/components/SplashPills.tsx +++ b/ui/desktop/src/components/SplashPills.tsx @@ -1,4 +1,3 @@ -import React from 'react'; import MarkdownContent from './MarkdownContent'; function truncateText(text: string, maxLength: number = 100): string { diff --git a/ui/desktop/src/components/ToolCallArguments.tsx b/ui/desktop/src/components/ToolCallArguments.tsx index 581f1580..b2e31a1f 100644 --- a/ui/desktop/src/components/ToolCallArguments.tsx +++ b/ui/desktop/src/components/ToolCallArguments.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import { useState } from 'react'; import MarkdownContent from './MarkdownContent'; import Expand from './ui/Expand'; diff --git a/ui/desktop/src/components/UserMessage.tsx b/ui/desktop/src/components/UserMessage.tsx index 4fad212f..a871b5b1 100644 --- a/ui/desktop/src/components/UserMessage.tsx +++ b/ui/desktop/src/components/UserMessage.tsx @@ -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'; diff --git a/ui/desktop/src/components/WelcomeGooseLogo.tsx b/ui/desktop/src/components/WelcomeGooseLogo.tsx index 9fb17eb8..b47ab9fa 100644 --- a/ui/desktop/src/components/WelcomeGooseLogo.tsx +++ b/ui/desktop/src/components/WelcomeGooseLogo.tsx @@ -1,4 +1,3 @@ -import React from 'react'; import { Goose, Rain } from './icons/Goose'; export default function WelcomeGooseLogo({ className = '' }) { diff --git a/ui/desktop/src/components/WelcomeView.tsx b/ui/desktop/src/components/WelcomeView.tsx index 5003d740..2e5779e3 100644 --- a/ui/desktop/src/components/WelcomeView.tsx +++ b/ui/desktop/src/components/WelcomeView.tsx @@ -1,4 +1,3 @@ -import React from 'react'; import { ProviderGrid } from './ProviderGrid'; import { ScrollArea } from './ui/scroll-area'; import { Button } from './ui/button'; diff --git a/ui/desktop/src/components/alerts/AlertBox.tsx b/ui/desktop/src/components/alerts/AlertBox.tsx index fa89a5f0..c93f249f 100644 --- a/ui/desktop/src/components/alerts/AlertBox.tsx +++ b/ui/desktop/src/components/alerts/AlertBox.tsx @@ -33,10 +33,10 @@ export const AlertBox = ({ alert, className }: AlertBoxProps) => { className={cn( 'h-[2px] w-[2px] rounded-full', alert.type === AlertType.Info - ? i < Math.round((alert.progress.current / alert.progress.total) * 30) + ? i < Math.round((alert.progress!.current / alert.progress!.total) * 30) ? 'dark:bg-black bg-white' : 'dark:bg-black/20 bg-white/20' - : i < Math.round((alert.progress.current / alert.progress.total) * 30) + : i < Math.round((alert.progress!.current / alert.progress!.total) * 30) ? 'bg-white' : 'bg-white/20' )} @@ -46,18 +46,18 @@ export const AlertBox = ({ alert, className }: AlertBoxProps) => {
- {alert.progress.current >= 1000 - ? (alert.progress.current / 1000).toFixed(1) + 'k' - : alert.progress.current} + {alert.progress!.current >= 1000 + ? (alert.progress!.current / 1000).toFixed(1) + 'k' + : alert.progress!.current} - {Math.round((alert.progress.current / alert.progress.total) * 100)}% + {Math.round((alert.progress!.current / alert.progress!.total) * 100)}%
- {alert.progress.total >= 1000 - ? (alert.progress.total / 1000).toFixed(0) + 'k' - : alert.progress.total} + {alert.progress!.total >= 1000 + ? (alert.progress!.total / 1000).toFixed(0) + 'k' + : alert.progress!.total}
diff --git a/ui/desktop/src/components/bottom_menu/BottomMenuModeSelection.tsx b/ui/desktop/src/components/bottom_menu/BottomMenuModeSelection.tsx index 092a6c0b..eb9418dc 100644 --- a/ui/desktop/src/components/bottom_menu/BottomMenuModeSelection.tsx +++ b/ui/desktop/src/components/bottom_menu/BottomMenuModeSelection.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useRef, useState, useCallback } from 'react'; +import { useEffect, useRef, useState, useCallback } from 'react'; import { all_goose_modes, ModeSelectionItem } from '../settings_v2/mode/ModeSelectionItem'; import { useConfig } from '../ConfigContext'; import { View, ViewOptions } from '../../App'; diff --git a/ui/desktop/src/components/conversation/SearchBar.tsx b/ui/desktop/src/components/conversation/SearchBar.tsx index e4ccf29f..6c3b91ee 100644 --- a/ui/desktop/src/components/conversation/SearchBar.tsx +++ b/ui/desktop/src/components/conversation/SearchBar.tsx @@ -168,7 +168,7 @@ export const SearchBar: React.FC = ({
{(() => { - return localSearchResults?.count > 0 && searchTerm + return localSearchResults?.count && localSearchResults.count > 0 && searchTerm ? `${localSearchResults.currentIndex}/${localSearchResults.count}` : null; })()} diff --git a/ui/desktop/src/components/conversation/SearchView.tsx b/ui/desktop/src/components/conversation/SearchView.tsx index 4f331dab..5024a332 100644 --- a/ui/desktop/src/components/conversation/SearchView.tsx +++ b/ui/desktop/src/components/conversation/SearchView.tsx @@ -231,23 +231,19 @@ export const SearchView: React.FC> = ({ highlighterRef.current = null; } - // Cancel any pending highlight operations - debouncedHighlight.cancel?.(); - // Clear search when closing onSearch?.('', false); - }, [debouncedHighlight, onSearch]); + }, [onSearch]); - // Clean up highlighter and debounced functions on unmount + // Clean up highlighter on unmount useEffect(() => { return () => { if (highlighterRef.current) { highlighterRef.current.destroy(); highlighterRef.current = null; } - debouncedHighlight.cancel?.(); }; - }, [debouncedHighlight]); + }, []); // Listen for keyboard events useEffect(() => { diff --git a/ui/desktop/src/components/icons/ArrowDown.tsx b/ui/desktop/src/components/icons/ArrowDown.tsx index 9a72d8c4..715a881a 100644 --- a/ui/desktop/src/components/icons/ArrowDown.tsx +++ b/ui/desktop/src/components/icons/ArrowDown.tsx @@ -1,4 +1,3 @@ -import React from 'react'; export default function ArrowDown({ className = '' }) { return ( diff --git a/ui/desktop/src/components/icons/ArrowUp.tsx b/ui/desktop/src/components/icons/ArrowUp.tsx index 0bef76a8..97a680d6 100644 --- a/ui/desktop/src/components/icons/ArrowUp.tsx +++ b/ui/desktop/src/components/icons/ArrowUp.tsx @@ -1,4 +1,3 @@ -import React from 'react'; export default function ArrowUp({ className = '' }) { return ( diff --git a/ui/desktop/src/components/icons/Attach.tsx b/ui/desktop/src/components/icons/Attach.tsx index 7e6f1e79..82cb1360 100644 --- a/ui/desktop/src/components/icons/Attach.tsx +++ b/ui/desktop/src/components/icons/Attach.tsx @@ -1,4 +1,3 @@ -import React from 'react'; export default function Attach({ className = '' }) { return ( diff --git a/ui/desktop/src/components/icons/Back.tsx b/ui/desktop/src/components/icons/Back.tsx index 1b7d0f16..ffce3cef 100644 --- a/ui/desktop/src/components/icons/Back.tsx +++ b/ui/desktop/src/components/icons/Back.tsx @@ -1,4 +1,3 @@ -import React from 'react'; export default function Back({ className = '' }) { return ( diff --git a/ui/desktop/src/components/icons/Bars.tsx b/ui/desktop/src/components/icons/Bars.tsx index 7951c278..60edc4b7 100644 --- a/ui/desktop/src/components/icons/Bars.tsx +++ b/ui/desktop/src/components/icons/Bars.tsx @@ -1,4 +1,3 @@ -import React from 'react'; export function Bars() { return ( diff --git a/ui/desktop/src/components/icons/ChatSmart.tsx b/ui/desktop/src/components/icons/ChatSmart.tsx index 9e901305..ef2563d0 100644 --- a/ui/desktop/src/components/icons/ChatSmart.tsx +++ b/ui/desktop/src/components/icons/ChatSmart.tsx @@ -1,4 +1,3 @@ -import React from 'react'; export default function ChatSmart({ className = '' }) { return ( diff --git a/ui/desktop/src/components/icons/Check.tsx b/ui/desktop/src/components/icons/Check.tsx index 589a4db8..8afa15c0 100644 --- a/ui/desktop/src/components/icons/Check.tsx +++ b/ui/desktop/src/components/icons/Check.tsx @@ -1,4 +1,3 @@ -import React from 'react'; export default function Check({ className = '' }) { return ( diff --git a/ui/desktop/src/components/icons/ChevronDown.tsx b/ui/desktop/src/components/icons/ChevronDown.tsx index 7af1e622..b730afa7 100644 --- a/ui/desktop/src/components/icons/ChevronDown.tsx +++ b/ui/desktop/src/components/icons/ChevronDown.tsx @@ -1,6 +1,5 @@ -import React from 'react'; -export default function ChevronDown({ className }) { +export default function ChevronDown({ className }: { className?: string }) { return ( {} diff --git a/ui/desktop/src/components/sessions/SessionsView.tsx b/ui/desktop/src/components/sessions/SessionsView.tsx index 0cd09550..739523d7 100644 --- a/ui/desktop/src/components/sessions/SessionsView.tsx +++ b/ui/desktop/src/components/sessions/SessionsView.tsx @@ -30,10 +30,11 @@ const SessionsView: React.FC = ({ setView }) => { // Keep the selected session null if there's an error setSelectedSession(null); + const errorMessage = err instanceof Error ? err.message : String(err); toastError({ title: 'Failed to load session. The file may be corrupted.', msg: 'Please try again later.', - traceback: err, + traceback: errorMessage, }); } finally { setIsLoadingSession(false); diff --git a/ui/desktop/src/components/settings/OllamaBattleGame.tsx b/ui/desktop/src/components/settings/OllamaBattleGame.tsx index c3b98f21..b282640d 100644 --- a/ui/desktop/src/components/settings/OllamaBattleGame.tsx +++ b/ui/desktop/src/components/settings/OllamaBattleGame.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect, useRef } from 'react'; +import { useState, useEffect, useRef } from 'react'; // Import actual PNG images import llamaSprite from '../../assets/battle-game/llama.png'; @@ -22,11 +22,10 @@ interface OllamaBattleGameProps { requiredKeys: string[]; } -export function OllamaBattleGame({ onComplete, _requiredKeys }: OllamaBattleGameProps) { +export function OllamaBattleGame({ onComplete, requiredKeys: _ }: OllamaBattleGameProps) { // Use Audio element type for audioRef - const audioRef = useRef<{ play: () => Promise; pause: () => void; volume: number } | null>( - null - ); + // eslint-disable-next-line no-undef + const audioRef = useRef(null); const [isMuted, setIsMuted] = useState(false); const [battleState, setBattleState] = useState({ @@ -169,10 +168,10 @@ export function OllamaBattleGame({ onComplete, _requiredKeys }: OllamaBattleGame if (!currentStep) return; // Handle host input - if (currentStep.action === 'host_input' && value) { + if (currentStep.action === 'host_input' && value && currentStep.configKey) { setConfigValues((prev) => ({ ...prev, - [currentStep.configKey]: value, + [currentStep.configKey!]: value, })); return; } @@ -405,8 +404,8 @@ export function OllamaBattleGame({ onComplete, _requiredKeys }: OllamaBattleGame !battleState.processingAction && (
{(typeof battleSteps[battleState.currentStep].choices === 'function' - ? battleSteps[battleState.currentStep].choices(battleState.lastChoice || '') - : battleSteps[battleState.currentStep].choices + ? (battleSteps[battleState.currentStep].choices as (choice: string) => string[])(battleState.lastChoice || '') + : battleSteps[battleState.currentStep].choices as string[] )?.map((choice: string) => (