/* * @ts-nocheck * Preventing TS checks with files presented in the video for a better presentation. */ import type { Message } from 'ai'; import React, { type RefCallback, useEffect, useState } from 'react'; import { ClientOnly } from 'remix-utils/client-only'; import { Menu } from '~/components/sidebar/Menu.client'; import { IconButton } from '~/components/ui/IconButton'; import { Workbench } from '~/components/workbench/Workbench.client'; import { classNames } from '~/utils/classNames'; import { MODEL_LIST, PROVIDER_LIST, initializeModelList } from '~/utils/constants'; import { Messages } from './Messages.client'; import { SendButton } from './SendButton.client'; import { APIKeyManager } from './APIKeyManager'; import Cookies from 'js-cookie'; import * as Tooltip from '@radix-ui/react-tooltip'; import styles from './BaseChat.module.scss'; import type { ProviderInfo } from '~/utils/types'; import { ExportChatButton } from '~/components/chat/chatExportAndImport/ExportChatButton'; import { ImportButtons } from '~/components/chat/chatExportAndImport/ImportButtons'; import { ExamplePrompts } from '~/components/chat/ExamplePrompts'; import GitCloneButton from './GitCloneButton'; import FilePreview from './FilePreview'; import { ModelSelector } from '~/components/chat/ModelSelector'; import { SpeechRecognitionButton } from '~/components/chat/SpeechRecognition'; const TEXTAREA_MIN_HEIGHT = 76; interface BaseChatProps { textareaRef?: React.RefObject | undefined; messageRef?: RefCallback | undefined; scrollRef?: RefCallback | undefined; showChat?: boolean; chatStarted?: boolean; isStreaming?: boolean; messages?: Message[]; description?: string; enhancingPrompt?: boolean; promptEnhanced?: boolean; input?: string; model?: string; setModel?: (model: string) => void; provider?: ProviderInfo; setProvider?: (provider: ProviderInfo) => void; handleStop?: () => void; sendMessage?: (event: React.UIEvent, messageInput?: string) => void; handleInputChange?: (event: React.ChangeEvent) => void; enhancePrompt?: () => void; importChat?: (description: string, messages: Message[]) => Promise; exportChat?: () => void; uploadedFiles?: File[]; setUploadedFiles?: (files: File[]) => void; imageDataList?: string[]; setImageDataList?: (dataList: string[]) => void; } export const BaseChat = React.forwardRef( ( { textareaRef, messageRef, scrollRef, showChat = true, chatStarted = false, isStreaming = false, model, setModel, provider, setProvider, input = '', enhancingPrompt, handleInputChange, promptEnhanced, enhancePrompt, sendMessage, handleStop, importChat, exportChat, uploadedFiles = [], setUploadedFiles, imageDataList = [], setImageDataList, messages, }, ref, ) => { const TEXTAREA_MAX_HEIGHT = chatStarted ? 400 : 200; const [apiKeys, setApiKeys] = useState>(() => { const savedKeys = Cookies.get('apiKeys'); if (savedKeys) { try { return JSON.parse(savedKeys); } catch (error) { console.error('Failed to parse API keys from cookies:', error); return {}; } } return {}; }); const [modelList, setModelList] = useState(MODEL_LIST); const [isModelSettingsCollapsed, setIsModelSettingsCollapsed] = useState(false); const [isListening, setIsListening] = useState(false); const [recognition, setRecognition] = useState(null); const [transcript, setTranscript] = useState(''); // Load enabled providers from cookies const [enabledProviders, setEnabledProviders] = useState(() => { const savedProviders = Cookies.get('providers'); if (savedProviders) { try { const parsedProviders = JSON.parse(savedProviders); return PROVIDER_LIST.filter((p) => parsedProviders[p.name]); } catch (error) { console.error('Failed to parse providers from cookies:', error); return PROVIDER_LIST; } } return PROVIDER_LIST; }); // Update enabled providers when cookies change useEffect(() => { const updateProvidersFromCookies = () => { const savedProviders = Cookies.get('providers'); if (savedProviders) { try { const parsedProviders = JSON.parse(savedProviders); setEnabledProviders(PROVIDER_LIST.filter((p) => parsedProviders[p.name])); } catch (error) { console.error('Failed to parse providers from cookies:', error); } } }; updateProvidersFromCookies(); const interval = setInterval(updateProvidersFromCookies, 1000); return () => clearInterval(interval); }, [PROVIDER_LIST]); console.log(transcript); useEffect(() => { // Load API keys from cookies on component mount try { const storedApiKeys = Cookies.get('apiKeys'); if (storedApiKeys) { const parsedKeys = JSON.parse(storedApiKeys); if (typeof parsedKeys === 'object' && parsedKeys !== null) { setApiKeys(parsedKeys); } } } catch (error) { console.error('Error loading API keys from cookies:', error); // Clear invalid cookie data Cookies.remove('apiKeys'); } initializeModelList().then((modelList) => { setModelList(modelList); }); if (typeof window !== 'undefined' && ('SpeechRecognition' in window || 'webkitSpeechRecognition' in window)) { const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition; const recognition = new SpeechRecognition(); recognition.continuous = true; recognition.interimResults = true; recognition.onresult = (event) => { const transcript = Array.from(event.results) .map((result) => result[0]) .map((result) => result.transcript) .join(''); setTranscript(transcript); if (handleInputChange) { const syntheticEvent = { target: { value: transcript }, } as React.ChangeEvent; handleInputChange(syntheticEvent); } }; recognition.onerror = (event) => { console.error('Speech recognition error:', event.error); setIsListening(false); }; setRecognition(recognition); } }, []); const startListening = () => { if (recognition) { recognition.start(); setIsListening(true); } }; const stopListening = () => { if (recognition) { recognition.stop(); setIsListening(false); } }; const handleSendMessage = (event: React.UIEvent, messageInput?: string) => { if (sendMessage) { sendMessage(event, messageInput); if (recognition) { recognition.abort(); // Stop current recognition setTranscript(''); // Clear transcript setIsListening(false); // Clear the input by triggering handleInputChange with empty value if (handleInputChange) { const syntheticEvent = { target: { value: '' }, } as React.ChangeEvent; handleInputChange(syntheticEvent); } } } }; const handleFileUpload = () => { const input = document.createElement('input'); input.type = 'file'; input.accept = 'image/*'; input.onchange = async (e) => { const file = (e.target as HTMLInputElement).files?.[0]; if (file) { const reader = new FileReader(); reader.onload = (e) => { const base64Image = e.target?.result as string; setUploadedFiles?.([...uploadedFiles, file]); setImageDataList?.([...imageDataList, base64Image]); }; reader.readAsDataURL(file); } }; input.click(); }; const handlePaste = async (e: React.ClipboardEvent) => { const items = e.clipboardData?.items; if (!items) { return; } for (const item of items) { if (item.type.startsWith('image/')) { e.preventDefault(); const file = item.getAsFile(); if (file) { const reader = new FileReader(); reader.onload = (e) => { const base64Image = e.target?.result as string; setUploadedFiles?.([...uploadedFiles, file]); setImageDataList?.([...imageDataList, base64Image]); }; reader.readAsDataURL(file); } break; } } }; const baseChat = (
{() => }
{!chatStarted && (

Where ideas begin

Bring ideas to life in seconds or get help on existing projects.

)}
{() => { return chatStarted ? ( ) : null; }}
{enabledProviders.length > 0 && provider && ( { const newApiKeys = { ...apiKeys, [provider.name]: key }; setApiKeys(newApiKeys); Cookies.set('apiKeys', JSON.stringify(newApiKeys)); }} /> )}
{ setUploadedFiles?.(uploadedFiles.filter((_, i) => i !== index)); setImageDataList?.(imageDataList.filter((_, i) => i !== index)); }} />