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
35 changes: 21 additions & 14 deletions ui/desktop/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useEffect, useRef, useState } from 'react';
import { IpcRendererEvent } from 'electron';
import { openSharedSessionFromDeepLink, type SessionLinksViewOptions } from './sessionLinks';
import { type SharedSessionDetails } from './sharedSessions';
import { initializeSystem } from './utils/providerUtils';
import { ErrorUI } from './components/ErrorBoundary';
import { ConfirmationModal } from './components/ui/ConfirmationModal';
Expand All @@ -9,6 +10,7 @@ import { toastService } from './toasts';
import { extractExtensionName } from './components/settings/extensions/utils';
import { GoosehintsModal } from './components/GoosehintsModal';
import { type ExtensionConfig } from './extensions';
import { type Recipe } from './recipe';

import ChatView from './components/ChatView';
import SuspenseLoader from './suspense-loader';
Expand Down Expand Up @@ -52,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 @@ -237,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 @@ -282,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 @@ -297,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 @@ -333,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 @@ -530,7 +535,9 @@ export default function App() {
{view === 'schedules' && <SchedulesView onClose={() => setView('chat')} />}
{view === 'sharedSession' && (
<SharedSessionView
session={viewOptions?.sessionDetails}
session={
(viewOptions?.sessionDetails as unknown as SharedSessionDetails | null) || null
}
isLoading={isLoadingSharedSession}
error={viewOptions?.error || sharedSessionError}
onBack={() => setView('sessions')}
Expand All @@ -556,7 +563,7 @@ export default function App() {
)}
{view === 'recipeEditor' && (
<RecipeEditor
config={viewOptions?.config || window.electron.getConfig().recipeConfig}
config={(viewOptions?.config as Recipe) || window.electron.getConfig().recipeConfig}
/>
)}
{view === 'permission' && (
Expand Down
2 changes: 1 addition & 1 deletion ui/desktop/src/components/ChatInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -502,7 +502,7 @@ export default function ChatInput({
className="absolute -top-1 -right-1 bg-gray-700 hover:bg-red-600 text-white rounded-full w-5 h-5 flex items-center justify-center text-xs leading-none opacity-0 group-hover:opacity-100 focus:opacity-100 transition-opacity z-10"
aria-label="Remove image"
>
<Close size={14} />
<Close className="w-3.5 h-3.5" />
</button>
)}
</div>
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
2 changes: 1 addition & 1 deletion ui/desktop/src/components/ConfigContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ export const ConfigProvider: React.FC<ConfigProviderProps> = ({ children }) => {
return extensionsList;
}

const extensionResponse: ExtensionResponse = result.data;
const extensionResponse: ExtensionResponse = result.data!;
setExtensionsList(extensionResponse.extensions);
return extensionResponse.extensions;
}
Expand Down
4 changes: 2 additions & 2 deletions ui/desktop/src/components/ErrorBoundary.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export function ErrorUI({ error }: { error: Error }) {

export class ErrorBoundary extends React.Component<
{ children: React.ReactNode },
{ error: Error; hasError: boolean }
{ error: Error | null; hasError: boolean }
> {
constructor(props: { children: React.ReactNode }) {
super(props);
Expand All @@ -69,7 +69,7 @@ export class ErrorBoundary extends React.Component<

render() {
if (this.state.hasError) {
return <ErrorUI error={this.state.error} />;
return <ErrorUI error={this.state.error || new Error('Unknown error')} />;
}
return this.props.children;
}
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
2 changes: 1 addition & 1 deletion ui/desktop/src/components/GooseMessage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ export default function GooseMessage({
{/* NOTE from alexhancock on 1/14/2025 - disabling again temporarily due to non-determinism in when the forms show up */}
{false && metadata && (
<div className="flex mt-[16px]">
<GooseResponseForm message={textContent} metadata={metadata} append={append} />
<GooseResponseForm message={textContent} metadata={metadata || null} append={append} />
</div>
)}
</div>
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
8 changes: 4 additions & 4 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 All @@ -125,7 +125,7 @@ export const GoosehintsModal = ({ directory, setIsGoosehintsModalOpen }: Goosehi
<ModalHelpText />
<div className="flex flex-col flex-1">
{goosehintsFileReadError ? (
<ModalError error={goosehintsFileReadError} />
<ModalError error={new Error(goosehintsFileReadError)} />
) : (
<div className="flex flex-col flex-1 space-y-2 h-full">
<ModalFileInfo filePath={goosehintsFilePath} found={goosehintsFileFound} />
Expand Down
4 changes: 2 additions & 2 deletions ui/desktop/src/components/LinkPreview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,9 @@ async function fetchMetadata(url: string): Promise<Metadata> {

return {
title: title || url,
description,
description: description || undefined,
favicon,
image,
image: image || undefined,
url,
};
} catch (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
4 changes: 2 additions & 2 deletions 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 All @@ -326,7 +326,7 @@ export const SearchView: React.FC<PropsWithChildren<SearchViewProps>> = ({
onSearch={handleSearch}
onClose={handleCloseSearch}
onNavigate={handleNavigate}
searchResults={searchResults || internalSearchResults}
searchResults={searchResults || internalSearchResults || undefined}
inputRef={searchInputRef}
initialSearchTerm={initialSearchTerm}
/>
Expand Down
Loading