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
3 changes: 2 additions & 1 deletion ui/desktop/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
"test-e2e:report": "playwright show-report",
"test-e2e:single": "npm run generate-api && playwright test -g",
"lint": "eslint \"src/**/*.{ts,tsx}\" --fix --no-warn-ignored",
"lint:check": "eslint \"src/**/*.{ts,tsx}\" --max-warnings 0 --no-warn-ignored",
"lint:check": "npm run typecheck && eslint \"src/**/*.{ts,tsx}\" --max-warnings 0 --no-warn-ignored",
"format": "prettier --write \"src/**/*.{ts,tsx,css,json}\"",
"format:check": "prettier --check \"src/**/*.{ts,tsx,css,json}\"",
"prepare": "cd ../.. && husky install",
Expand Down Expand Up @@ -73,6 +73,7 @@
"license": "Apache-2.0",
"lint-staged": {
"src/**/*.{ts,tsx}": [
"bash -c 'npm run typecheck'",
"eslint --fix --max-warnings 0 --no-warn-ignored",
"prettier --write"
],
Expand Down
31 changes: 18 additions & 13 deletions ui/desktop/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,20 +54,20 @@ export type ViewOptions = {
extensionId?: string;
showEnvVars?: boolean;
deepLinkConfig?: ExtensionConfig;
// Session view options

// 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;
};
Expand Down Expand Up @@ -239,15 +239,18 @@ export default function App() {
}, []);

useEffect(() => {
const handleOpenSharedSession = async (event: IpcRendererEvent, ...args: unknown[]) => {
const handleOpenSharedSession = async (_event: IpcRendererEvent, ...args: unknown[]) => {
const link = args[0] as string;
window.electron.logInfo(`Opening shared session from deep link ${link}`);
setIsLoadingSharedSession(true);
setSharedSessionError(null);
try {
await openSharedSessionFromDeepLink(link, (view: View, options?: SessionLinksViewOptions) => {
setView(view, options as ViewOptions);
});
await openSharedSessionFromDeepLink(
link,
(view: View, options?: SessionLinksViewOptions) => {
setView(view, options as ViewOptions);
}
);
} catch (error) {
console.error('Unexpected error opening shared session:', error);
setView('sessions');
Expand Down Expand Up @@ -284,7 +287,7 @@ export default function App() {

useEffect(() => {
console.log('Setting up fatal error handler');
const handleFatalError = (event: IpcRendererEvent, ...args: unknown[]) => {
const handleFatalError = (_event: IpcRendererEvent, ...args: unknown[]) => {
const errorMessage = args[0] as string;
console.error('Encountered a fatal error: ', errorMessage);
console.error('Current view:', view);
Expand All @@ -299,7 +302,7 @@ export default function App() {

useEffect(() => {
console.log('Setting up view change handler');
const handleSetView = (event: IpcRendererEvent, ...args: unknown[]) => {
const handleSetView = (_event: IpcRendererEvent, ...args: unknown[]) => {
const newView = args[0] as View;
console.log(`Received view change request to: ${newView}`);
setView(newView);
Expand Down Expand Up @@ -335,7 +338,7 @@ export default function App() {

useEffect(() => {
console.log('Setting up extension handler');
const handleAddExtension = async (event: IpcRendererEvent, ...args: unknown[]) => {
const handleAddExtension = async (_event: IpcRendererEvent, ...args: unknown[]) => {
const link = args[0] as string;
try {
console.log(`Received add-extension event with link: ${link}`);
Expand Down Expand Up @@ -532,7 +535,9 @@ export default function App() {
{view === 'schedules' && <SchedulesView onClose={() => setView('chat')} />}
{view === 'sharedSession' && (
<SharedSessionView
session={viewOptions?.sessionDetails as SharedSessionDetails | null || null}
session={
(viewOptions?.sessionDetails as unknown as SharedSessionDetails | null) || null
}
isLoading={isLoadingSharedSession}
error={viewOptions?.error || sharedSessionError}
onBack={() => setView('sessions')}
Expand Down
22 changes: 6 additions & 16 deletions ui/desktop/src/components/ChatView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -244,18 +244,11 @@ function ChatContent({

// Create a new window for the recipe editor
console.log('Opening recipe editor with config:', response.recipe);
const recipeConfig: {
id: string;
title: string;
description: string;
instructions: string;
activities: string[];
prompt: string;
} = {
const recipeConfig = {
id: response.recipe.title || 'untitled',
title: response.recipe.title,
description: response.recipe.description,
instructions: response.recipe.instructions,
name: response.recipe.title || 'Untitled Recipe',
description: response.recipe.description || '',
instructions: response.recipe.instructions || '',
activities: response.recipe.activities || [],
prompt: response.recipe.prompt || '',
};
Expand Down Expand Up @@ -287,11 +280,8 @@ function ChatContent({

// Update chat messages when they change and save to sessionStorage
useEffect(() => {
setChat((prevChat: ChatType) => {
const updatedChat = { ...prevChat, messages };
return updatedChat;
});
}, [messages, setChat]);
setChat({ ...chat, messages });
}, [messages, setChat, chat]);

useEffect(() => {
if (messages.length > 0) {
Expand Down
17 changes: 8 additions & 9 deletions ui/desktop/src/components/FlappyGoose.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,5 @@
import React, { useEffect, useRef, useState, useCallback } from 'react';

declare var requestAnimationFrame: (callback: FrameRequestCallback) => number;
declare class HTMLCanvasElement {}
declare class HTMLImageElement {}
declare class DOMHighResTimeStamp {}
declare class Image {}
declare type FrameRequestCallback = (time: DOMHighResTimeStamp) => void;
import svg1 from '../images/loading-goose/1.svg';
import svg7 from '../images/loading-goose/7.svg';

Expand All @@ -20,9 +14,11 @@ interface FlappyGooseProps {
}

const FlappyGoose: React.FC<FlappyGooseProps> = ({ onClose }) => {
const canvasRef = useRef<HTMLCanvasElement>(null);
// eslint-disable-next-line no-undef
const canvasRef = useRef<HTMLCanvasElement | null>(null);
const [gameOver, setGameOver] = useState(false);
const [displayScore, setDisplayScore] = useState(0);
// eslint-disable-next-line no-undef
const gooseImages = useRef<HTMLImageElement[]>([]);
const framesLoaded = useRef(0);
const [imagesReady, setImagesReady] = useState(false);
Expand Down Expand Up @@ -51,7 +47,7 @@ const FlappyGoose: React.FC<FlappyGooseProps> = ({ onClose }) => {
const OBSTACLE_WIDTH = 40;
const FLAP_DURATION = 150;

const safeRequestAnimationFrame = useCallback((callback: FrameRequestCallback) => {
const safeRequestAnimationFrame = useCallback((callback: (time: number) => void) => {
if (typeof window !== 'undefined' && typeof requestAnimationFrame !== 'undefined') {
requestAnimationFrame(callback);
}
Expand Down Expand Up @@ -216,6 +212,7 @@ const FlappyGoose: React.FC<FlappyGooseProps> = ({ onClose }) => {
useEffect(() => {
const frames = [svg1, svg7];
frames.forEach((src, index) => {
// eslint-disable-next-line no-undef
const img = new Image() as HTMLImageElement;
img.src = src;
img.onload = () => {
Expand Down Expand Up @@ -272,7 +269,9 @@ const FlappyGoose: React.FC<FlappyGooseProps> = ({ onClose }) => {
onClick={flap}
>
<canvas
ref={canvasRef}
ref={(el) => {
canvasRef.current = el;
}}
style={{
border: '2px solid #333',
borderRadius: '8px',
Expand Down
4 changes: 2 additions & 2 deletions ui/desktop/src/components/GooseResponseForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -132,9 +132,9 @@ export default function GooseResponseForm({
return null;
}

function isForm(f: DynamicForm) {
function isForm(f: DynamicForm | null): f is DynamicForm {
return (
f && f.title && f.description && f.fields && Array.isArray(f.fields) && f.fields.length > 0
!!f && !!f.title && !!f.description && !!f.fields && Array.isArray(f.fields) && f.fields.length > 0
);
}

Expand Down
6 changes: 3 additions & 3 deletions ui/desktop/src/components/GoosehintsModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -96,17 +96,17 @@ type GoosehintsModalProps = {

export const GoosehintsModal = ({ directory, setIsGoosehintsModalOpen }: GoosehintsModalProps) => {
const goosehintsFilePath = `${directory}/.goosehints`;
const [goosehintsFile, setGoosehintsFile] = useState<string>(null);
const [goosehintsFile, setGoosehintsFile] = useState<string>('');
const [goosehintsFileFound, setGoosehintsFileFound] = useState<boolean>(false);
const [goosehintsFileReadError, setGoosehintsFileReadError] = useState<string>(null);
const [goosehintsFileReadError, setGoosehintsFileReadError] = useState<string>('');

useEffect(() => {
const fetchGoosehintsFile = async () => {
try {
const { file, error, found } = await getGoosehintsFile(goosehintsFilePath);
setGoosehintsFile(file);
setGoosehintsFileFound(found);
setGoosehintsFileReadError(error);
setGoosehintsFileReadError(error || '');
} catch (error) {
console.error('Error fetching .goosehints file:', error);
}
Expand Down
8 changes: 4 additions & 4 deletions ui/desktop/src/components/ProviderGrid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export function ProviderGrid({ onSubmit }: ProviderGridProps) {
const handleConfigure = async (provider: { id: string; name: string; isConfigured: boolean; description: string }) => {
const providerId = provider.id.toLowerCase();

const modelName = getDefaultModel(providerId);
const modelName = getDefaultModel(providerId) || 'default-model';
const model = createSelectedModel(providerId, modelName);

await initializeSystem(providerId, model.name);
Expand Down Expand Up @@ -189,9 +189,9 @@ export function ProviderGrid({ onSubmit }: ProviderGridProps) {
{showSetupModal && selectedId && (
<div className="relative z-[9999]">
<ProviderSetupModal
provider={providers.find((p) => p.id === selectedId)?.name}
model="Example Model"
endpoint="Example Endpoint"
provider={providers.find((p) => p.id === selectedId)?.name || 'Unknown Provider'}
_model="Example Model"
_endpoint="Example Endpoint"
onSubmit={handleModalSubmit}
onCancel={() => {
setShowSetupModal(false);
Expand Down
6 changes: 3 additions & 3 deletions ui/desktop/src/components/RecipeEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -121,13 +121,13 @@ export default function RecipeEditor({ config }: RecipeEditorProps) {
if (!extension) return null;

// Create a clean copy of the extension configuration
const cleanExtension = { ...extension };
delete cleanExtension.enabled;
const { enabled: _enabled, ...cleanExtension } = extension;
// Remove legacy envs which could potentially include secrets
// env_keys will work but rely on the end user having setup those keys themselves
if ('envs' in cleanExtension) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
delete (cleanExtension as any).envs;
const { envs: _envs, ...finalExtension } = cleanExtension as any;
return finalExtension;
}
return cleanExtension;
})
Expand Down
2 changes: 1 addition & 1 deletion ui/desktop/src/components/conversation/SearchView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,7 @@ export const SearchView: React.FC<PropsWithChildren<SearchViewProps>> = ({
<div
ref={(el) => {
if (el) {
containerRef.current = el;
containerRef.current = el as SearchContainerElement;
// Expose the highlighter instance
containerRef.current._searchHighlighter = highlighterRef.current;
}
Expand Down
13 changes: 11 additions & 2 deletions ui/desktop/src/components/more_menu/MoreMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,15 @@ import { FolderOpen, Moon, Sliders, Sun } from 'lucide-react';
import { useConfig } from '../ConfigContext';
import { ViewOptions, View } from '../../App';

interface RecipeConfig {
id: string;
name: string;
description: string;
instructions?: string;
activities?: string[];
[key: string]: unknown;
}

interface MenuButtonProps {
onClick: () => void;
children: React.ReactNode;
Expand Down Expand Up @@ -187,7 +196,7 @@ export default function MoreMenu({
setOpen(false);
window.electron.createChatWindow(
undefined,
window.appConfig.get('GOOSE_WORKING_DIR')
window.appConfig.get('GOOSE_WORKING_DIR') as string | undefined
);
}}
subtitle="Start a new session in the current directory"
Expand Down Expand Up @@ -244,7 +253,7 @@ export default function MoreMenu({
undefined, // dir
undefined, // version
undefined, // resumeSessionId
recipeConfig, // recipe config
recipeConfig as RecipeConfig, // recipe config
'recipeEditor' // view type
);
}}
Expand Down
5 changes: 3 additions & 2 deletions ui/desktop/src/components/schedule/CreateScheduleModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React, { useState, useEffect, FormEvent } from 'react';
import { Card } from '../ui/card';
import { Button } from '../ui/button';
import { Input } from '../ui/input';
import { Select } from '../ui/select';
import { Select } from '../ui/Select';
import cronstrue from 'cronstrue';

type FrequencyValue = 'once' | 'hourly' | 'daily' | 'weekly' | 'monthly';
Expand Down Expand Up @@ -292,7 +292,8 @@ export const CreateScheduleModal: React.FC<CreateScheduleModalProps> = ({
instanceId="frequency-select-modal"
options={frequencies}
value={frequencies.find((f) => f.value === frequency)}
onChange={(selectedOption: FrequencyOption | null) => {
onChange={(newValue: unknown) => {
const selectedOption = newValue as FrequencyOption | null;
if (selectedOption) setFrequency(selectedOption.value);
}}
placeholder="Select frequency..."
Expand Down
3 changes: 2 additions & 1 deletion ui/desktop/src/components/schedule/EditScheduleModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,8 @@ export const EditScheduleModal: React.FC<EditScheduleModalProps> = ({
instanceId="frequency-select-modal"
options={frequencies}
value={frequencies.find((f) => f.value === frequency)}
onChange={(selectedOption: FrequencyOption | null) => {
onChange={(newValue: unknown) => {
const selectedOption = newValue as FrequencyOption | null;
if (selectedOption) setFrequency(selectedOption.value);
}}
placeholder="Select frequency..."
Expand Down
8 changes: 4 additions & 4 deletions ui/desktop/src/components/sessions/SharedSessionView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,13 @@ const SharedSessionView: React.FC<SharedSessionViewProps> = ({
<div className="flex items-center text-sm text-textSubtle mt-1 space-x-5">
<span className="flex items-center">
<Calendar className="w-4 h-4 mr-1" />
{formatMessageTimestamp(session.messages[0]?.created)}
{session ? formatMessageTimestamp(session.messages[0]?.created) : 'Unknown'}
</span>
<span className="flex items-center">
<MessageSquareText className="w-4 h-4 mr-1" />
{session.message_count}
{session ? session.message_count : 0}
</span>
{session.total_tokens !== null && (
{session && session.total_tokens !== null && (
<span className="flex items-center">
<Target className="w-4 h-4 mr-1" />
{session.total_tokens.toLocaleString()}
Expand All @@ -49,7 +49,7 @@ const SharedSessionView: React.FC<SharedSessionViewProps> = ({
<div className="flex items-center text-sm text-textSubtle space-x-5">
<span className="flex items-center">
<Folder className="w-4 h-4 mr-1" />
{session.working_dir}
{session ? session.working_dir : 'Unknown'}
</span>
</div>
</div>
Expand Down
4 changes: 2 additions & 2 deletions ui/desktop/src/components/settings/ProviderSetupModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export function ProviderSetupModal({
const [configValues, setConfigValues] = React.useState<{ [key: string]: string }>(
default_key_value
);
const requiredKeys = required_keys[provider] || ['API Key'];
const requiredKeys = (required_keys as Record<string, string[]>)[provider] || ['API Key'];
const headerText = title || `Setup ${provider}`;

const shouldShowBattle = React.useMemo(() => {
Expand Down Expand Up @@ -59,7 +59,7 @@ export function ProviderSetupModal({
) : (
<form onSubmit={handleSubmit}>
<div className="mt-[24px] space-y-4">
{requiredKeys.map((keyName) => (
{requiredKeys.map((keyName: string) => (
<div key={keyName}>
<Input
type={isSecretKey(keyName) ? 'password' : 'text'}
Expand Down
1 change: 0 additions & 1 deletion ui/desktop/src/components/settings/SettingsView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ const DEFAULT_SETTINGS: SettingsType = {
enabled: true,
},
],
// @ts-expect-error "we actually do always have all the properties required for builtins, but tsc cannot tell for some reason"
extensions: BUILT_IN_EXTENSIONS,
};

Expand Down
Loading