From 6f1d0a9150631ecb42cc6e9a77a5d5ad44ffccce Mon Sep 17 00:00:00 2001 From: eduardruzga Date: Sun, 17 Nov 2024 15:52:38 +0200 Subject: [PATCH 01/37] .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index b43105b77..3303fba9b 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,7 @@ dist-ssr *.sln *.sw? +/.history /.cache /build .env.local From 8246eb3c626d1b4e9789dd344ee4389f53d57a30 Mon Sep 17 00:00:00 2001 From: eduardruzga Date: Mon, 18 Nov 2024 12:04:56 +0200 Subject: [PATCH 02/37] Add windows start command --- package.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index ce8e95d0d..6bbb65aa7 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,9 @@ "test:watch": "vitest", "lint": "eslint --cache --cache-location ./node_modules/.cache/eslint .", "lint:fix": "npm run lint -- --fix", - "start": "bindings=$(./bindings.sh) && wrangler pages dev ./build/client $bindings", + "start:windows": "wrangler pages dev ./build/client", + "start:unix": "bindings=$(./bindings.sh) && wrangler pages dev ./build/client $bindings", + "start": "node -e \"const { spawn } = require('child_process'); const isWindows = process.platform === 'win32'; const cmd = isWindows ? 'npm run start:windows' : 'npm run start:unix'; const child = spawn(cmd, { shell: true, stdio: 'inherit' }); child.on('exit', code => process.exit(code));\"", "typecheck": "tsc", "dockerstart": "bindings=$(./bindings.sh) && wrangler pages dev ./build/client $bindings --ip 0.0.0.0 --port 5173 --no-show-interactive-dev-session", "dockerrun": "docker run -it -d --name bolt-ai-live -p 5173:5173 --env-file .env.local bolt-ai", "dockerbuild:prod": "docker build -t bolt-ai:production -t bolt-ai:latest --target bolt-ai-production .", From 920f3618d28d3f0ea5e907381e98846961e6b719 Mon Sep 17 00:00:00 2001 From: eduardruzga Date: Mon, 18 Nov 2024 19:12:47 +0200 Subject: [PATCH 03/37] Fix package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6bbb65aa7..03a5f79e8 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "lint:fix": "npm run lint -- --fix", "start:windows": "wrangler pages dev ./build/client", "start:unix": "bindings=$(./bindings.sh) && wrangler pages dev ./build/client $bindings", - "start": "node -e \"const { spawn } = require('child_process'); const isWindows = process.platform === 'win32'; const cmd = isWindows ? 'npm run start:windows' : 'npm run start:unix'; const child = spawn(cmd, { shell: true, stdio: 'inherit' }); child.on('exit', code => process.exit(code));\"", "typecheck": "tsc", + "start": "node -e \"const { spawn } = require('child_process'); const isWindows = process.platform === 'win32'; const cmd = isWindows ? 'npm run start:windows' : 'npm run start:unix'; const child = spawn(cmd, { shell: true, stdio: 'inherit' }); child.on('exit', code => process.exit(code));\"", "dockerstart": "bindings=$(./bindings.sh) && wrangler pages dev ./build/client $bindings --ip 0.0.0.0 --port 5173 --no-show-interactive-dev-session", "dockerrun": "docker run -it -d --name bolt-ai-live -p 5173:5173 --env-file .env.local bolt-ai", "dockerbuild:prod": "docker build -t bolt-ai:production -t bolt-ai:latest --target bolt-ai-production .", From e78a5b0a050818454344020b1682c07c1051f932 Mon Sep 17 00:00:00 2001 From: Andrew Trokhymenko Date: Mon, 18 Nov 2024 19:55:28 -0500 Subject: [PATCH 04/37] image-upload --- app/components/chat/BaseChat.tsx | 121 ++++++++++++++++------ app/components/chat/Chat.client.tsx | 42 +++++++- app/components/chat/FilePreview.tsx | 40 +++++++ app/components/chat/SendButton.client.tsx | 5 +- app/components/chat/UserMessage.tsx | 27 ++++- app/components/sidebar/Menu.client.tsx | 2 +- app/components/workbench/EditorPanel.tsx | 9 +- app/components/workbench/FileTree.tsx | 2 +- app/lib/.server/llm/stream-text.ts | 41 ++++++-- app/routes/api.chat.ts | 17 ++- 10 files changed, 244 insertions(+), 62 deletions(-) create mode 100644 app/components/chat/FilePreview.tsx diff --git a/app/components/chat/BaseChat.tsx b/app/components/chat/BaseChat.tsx index 629c5cbed..e0cd9280d 100644 --- a/app/components/chat/BaseChat.tsx +++ b/app/components/chat/BaseChat.tsx @@ -17,6 +17,8 @@ import Cookies from 'js-cookie'; import styles from './BaseChat.module.scss'; import type { ProviderInfo } from '~/utils/types'; +import FilePreview from './FilePreview'; + const EXAMPLE_PROMPTS = [ { text: 'Build a todo app in React using Tailwind' }, { text: 'Build a simple blog using Astro' }, @@ -33,7 +35,7 @@ const ModelSelector = ({ model, setModel, provider, setProvider, modelList, prov { - setProvider(providerList.find(p => p.name === e.target.value)); + setProvider(providerList.find((p) => p.name === e.target.value)); const firstModel = [...modelList].find((m) => m.provider == e.target.value); setModel(firstModel ? firstModel.name : ''); }} @@ -51,7 +51,7 @@ const ModelSelector = ({ model, setModel, provider, setProvider, modelList, prov key={provider?.name} value={model} onChange={(e) => setModel(e.target.value)} - style={{ maxWidth: "70%" }} + style={{ maxWidth: '70%' }} className="flex-1 p-2 rounded-lg border border-bolt-elements-borderColor bg-bolt-elements-prompt-background text-bolt-elements-textPrimary focus:outline-none focus:ring-2 focus:ring-bolt-elements-focus transition-all" > {[...modelList] @@ -93,32 +93,34 @@ interface BaseChatProps { setImageDataList?: (dataList: string[]) => void; } export const BaseChat = React.forwardRef( - ({ - textareaRef, - messageRef, - scrollRef, - showChat, - chatStarted = false, - isStreaming = false, - model, - setModel, - provider, - setProvider, - input = '', - enhancingPrompt, - handleInputChange, - promptEnhanced, - enhancePrompt, - sendMessage, - handleStop, - uploadedFiles, - setUploadedFiles, - imageDataList, - setImageDataList, - messages, - children, // Add this - }, ref) => { - console.log(provider); + ( + { + textareaRef, + messageRef, + scrollRef, + showChat = true, + chatStarted = false, + isStreaming = false, + model, + setModel, + provider, + setProvider, + input = '', + enhancingPrompt, + handleInputChange, + promptEnhanced, + enhancePrompt, + sendMessage, + handleStop, + uploadedFiles, + setUploadedFiles, + imageDataList, + setImageDataList, + messages, + children, // Add this + }, + ref, + ) => { const TEXTAREA_MAX_HEIGHT = chatStarted ? 400 : 200; const [apiKeys, setApiKeys] = useState>({}); const [modelList, setModelList] = useState(MODEL_LIST); @@ -139,7 +141,7 @@ export const BaseChat = React.forwardRef( Cookies.remove('apiKeys'); } - initializeModelList().then(modelList => { + initializeModelList().then((modelList) => { setModelList(modelList); }); }, []); @@ -239,12 +241,13 @@ export const BaseChat = React.forwardRef( setProvider={setProvider} providerList={PROVIDER_LIST} /> - {provider && + {provider && ( updateApiKey(provider.name, key)} - />} + /> + )} ( className="transition-all" onClick={() => handleFileUpload()} > -
+
( ); }, ); - - - diff --git a/app/components/chat/FilePreview.tsx b/app/components/chat/FilePreview.tsx index 378ada6ed..31fd11b9e 100644 --- a/app/components/chat/FilePreview.tsx +++ b/app/components/chat/FilePreview.tsx @@ -1,23 +1,22 @@ -// FilePreview.tsx +// Remove the lucide-react import import React from 'react'; -import { X } from 'lucide-react'; +// Rest of the interface remains the same interface FilePreviewProps { files: File[]; - imageDataList: string[]; // or imagePreviews: string[] + imageDataList: string[]; onRemove: (index: number) => void; } const FilePreview: React.FC = ({ files, imageDataList, onRemove }) => { if (!files || files.length === 0) { - return null; // Or render a placeholder if desired + return null; } return ( -
{/* Add horizontal scrolling if needed */} +
{files.map((file, index) => (
- {/* Display image preview or file icon */} {imageDataList[index] && (
{file.name} @@ -26,7 +25,7 @@ const FilePreview: React.FC = ({ files, imageDataList, onRemov className="absolute -top-2 -right-2 z-10 bg-white rounded-full p-1 shadow-md hover:bg-gray-100" >
- +
diff --git a/app/components/chat/UserMessage.tsx b/app/components/chat/UserMessage.tsx index a57d4fa83..d7e1228fd 100644 --- a/app/components/chat/UserMessage.tsx +++ b/app/components/chat/UserMessage.tsx @@ -21,9 +21,6 @@ export function UserMessage({ content }: UserMessageProps) { ); } -// function sanitizeUserMessage(content: string) { -// return content.replace(modificationsRegex, '').replace(MODEL_REGEX, 'Using: $1').replace(PROVIDER_REGEX, ' ($1)\n\n').trim(); -// } function sanitizeUserMessage(content: string | Array<{type: string, text?: string, image_url?: {url: string}}>) { if (Array.isArray(content)) { return content.map(item => { diff --git a/app/lib/.server/llm/stream-text.ts b/app/lib/.server/llm/stream-text.ts index a50c28eb8..160339610 100644 --- a/app/lib/.server/llm/stream-text.ts +++ b/app/lib/.server/llm/stream-text.ts @@ -45,12 +45,12 @@ function extractPropertiesFromMessage(message: Message): { model: string; provid if (item.type === 'text') { return { type: 'text', - text: item.text?.replace(/\[Model:.*?\]\n\n/, '').replace(/\[Provider:.*?\]\n\n/, '') + text: item.text?.replace(MODEL_REGEX, '').replace(PROVIDER_REGEX, '') }; } return item; // Preserve image_url and other types as is }) - : textContent.replace(/\[Model:.*?\]\n\n/, '').replace(/\[Provider:.*?\]\n\n/, ''); + : textContent.replace(MODEL_REGEX, '').replace(PROVIDER_REGEX, ''); return { model, provider, content: cleanedContent }; } @@ -80,16 +80,6 @@ export function streamText( return message; // No changes for non-user messages }); - // const modelConfig = getModel(currentProvider, currentModel, env, apiKeys); - // const coreMessages = convertToCoreMessages(processedMessages); - - // console.log('Debug streamText:', JSON.stringify({ - // model: modelConfig, - // messages: processedMessages, - // coreMessages: coreMessages, - // system: getSystemPrompt() - // }, null, 2)); - return _streamText({ model: getModel(currentProvider, currentModel, env, apiKeys), system: getSystemPrompt(), diff --git a/app/routes/api.chat.ts b/app/routes/api.chat.ts index 284fccbcf..8fdb3d743 100644 --- a/app/routes/api.chat.ts +++ b/app/routes/api.chat.ts @@ -30,15 +30,15 @@ function parseCookies(cookieHeader) { } async function chatAction({ context, request }: ActionFunctionArgs) { - // console.log('=== API CHAT LOGGING START ==='); - // console.log('Request received:', request.url); - + const { messages, imageData } = await request.json<{ messages: Messages, imageData?: string[] }>(); const cookieHeader = request.headers.get("Cookie"); + + // Parse the cookie's value (returns an object or null if no cookie exists) const apiKeys = JSON.parse(parseCookies(cookieHeader).apiKeys || "{}"); const stream = new SwitchableStream(); @@ -71,13 +71,6 @@ async function chatAction({ context, request }: ActionFunctionArgs) { const result = await streamText(messages, context.cloudflare.env, options, apiKeys); - // console.log('=== API CHAT LOGGING START ==='); - // console.log('StreamText:', JSON.stringify({ - // messages, - // result, - // }, null, 2)); - // console.log('=== API CHAT LOGGING END ==='); - stream.switchSource(result.toAIStream()); return new Response(stream.readable, { diff --git a/package.json b/package.json index 56a9e720e..40ede0f77 100644 --- a/package.json +++ b/package.json @@ -74,7 +74,6 @@ "jose": "^5.6.3", "js-cookie": "^3.0.5", "jszip": "^3.10.1", - "lucide-react": "^0.460.0", "nanostores": "^0.10.3", "ollama-ai-provider": "^0.15.2", "react": "^18.2.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ead147e97..4158d19c2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -155,9 +155,6 @@ importers: jszip: specifier: ^3.10.1 version: 3.10.1 - lucide-react: - specifier: ^0.460.0 - version: 0.460.0(react@18.3.1) nanostores: specifier: ^0.10.3 version: 0.10.3 @@ -3674,11 +3671,6 @@ packages: resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} engines: {node: '>=12'} - lucide-react@0.460.0: - resolution: {integrity: sha512-BVtq/DykVeIvRTJvRAgCsOwaGL8Un3Bxh8MbDxMhEWlZay3T4IpEKDEpwt5KZ0KJMHzgm6jrltxlT5eXOWXDHg==} - peerDependencies: - react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc - magic-string@0.25.9: resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==} @@ -9492,10 +9484,6 @@ snapshots: lru-cache@7.18.3: {} - lucide-react@0.460.0(react@18.3.1): - dependencies: - react: 18.3.1 - magic-string@0.25.9: dependencies: sourcemap-codec: 1.4.8 From 76713c2e6e5b2518f9d8d995647204e4ab66c6b9 Mon Sep 17 00:00:00 2001 From: Andrew Trokhymenko Date: Wed, 20 Nov 2024 17:56:07 -0500 Subject: [PATCH 07/37] fixes for PR #332 --- app/components/chat/UserMessage.tsx | 21 +++++---------------- app/components/workbench/EditorPanel.tsx | 9 +++++---- app/lib/.server/llm/stream-text.ts | 5 +++++ 3 files changed, 15 insertions(+), 20 deletions(-) diff --git a/app/components/chat/UserMessage.tsx b/app/components/chat/UserMessage.tsx index d7e1228fd..c5d9c9b81 100644 --- a/app/components/chat/UserMessage.tsx +++ b/app/components/chat/UserMessage.tsx @@ -9,10 +9,7 @@ interface UserMessageProps { } export function UserMessage({ content }: UserMessageProps) { - const sanitizedContent = sanitizeUserMessage(content); - const textContent = Array.isArray(sanitizedContent) - ? sanitizedContent.find(item => item.type === 'text')?.text || '' - : sanitizedContent; + const textContent = sanitizeUserMessage(content); return (
@@ -23,17 +20,9 @@ export function UserMessage({ content }: UserMessageProps) { function sanitizeUserMessage(content: string | Array<{type: string, text?: string, image_url?: {url: string}}>) { if (Array.isArray(content)) { - return content.map(item => { - if (item.type === 'text') { - return { - type: 'text', - text: item.text?.replace(/\[Model:.*?\]\n\n/, '').replace(/\[Provider:.*?\]\n\n/, '') - }; - } - return item; // Keep image_url items unchanged - }); + const textItem = content.find(item => item.type === 'text'); + return textItem?.text?.replace(MODEL_REGEX, '').replace(PROVIDER_REGEX, '') || ''; } - // Handle legacy string content - return content.replace(/\[Model:.*?\]\n\n/, '').replace(/\[Provider:.*?\]\n\n/, ''); -} + return content.replace(MODEL_REGEX, '').replace(PROVIDER_REGEX, ''); +} \ No newline at end of file diff --git a/app/components/workbench/EditorPanel.tsx b/app/components/workbench/EditorPanel.tsx index e789f1d6d..a9c9d33e7 100644 --- a/app/components/workbench/EditorPanel.tsx +++ b/app/components/workbench/EditorPanel.tsx @@ -23,6 +23,7 @@ import { isMobile } from '~/utils/mobile'; import { FileBreadcrumb } from './FileBreadcrumb'; import { FileTree } from './FileTree'; import { Terminal, type TerminalRef } from './terminal/Terminal'; +import React from 'react'; interface EditorPanelProps { files?: FileMap; @@ -203,7 +204,7 @@ export const EditorPanel = memo( const isActive = activeTerminal === index; return ( - <> + {index == 0 ? ( - + )} - + ); })} {terminalCount < MAX_TERMINALS && } diff --git a/app/lib/.server/llm/stream-text.ts b/app/lib/.server/llm/stream-text.ts index 160339610..79515120f 100644 --- a/app/lib/.server/llm/stream-text.ts +++ b/app/lib/.server/llm/stream-text.ts @@ -80,6 +80,11 @@ export function streamText( return message; // No changes for non-user messages }); + console.log('Stream Text:', JSON.stringify({ + model: getModel(currentProvider, currentModel, env, apiKeys), + messages: convertToCoreMessages(processedMessages), + })); + return _streamText({ model: getModel(currentProvider, currentModel, env, apiKeys), system: getSystemPrompt(), From 302cd28775b5abe214817c2b7790a313b2f8f34f Mon Sep 17 00:00:00 2001 From: Andrew Trokhymenko Date: Wed, 20 Nov 2024 19:35:57 -0500 Subject: [PATCH 08/37] . --- app/lib/.server/llm/stream-text.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/app/lib/.server/llm/stream-text.ts b/app/lib/.server/llm/stream-text.ts index 79515120f..160339610 100644 --- a/app/lib/.server/llm/stream-text.ts +++ b/app/lib/.server/llm/stream-text.ts @@ -80,11 +80,6 @@ export function streamText( return message; // No changes for non-user messages }); - console.log('Stream Text:', JSON.stringify({ - model: getModel(currentProvider, currentModel, env, apiKeys), - messages: convertToCoreMessages(processedMessages), - })); - return _streamText({ model: getModel(currentProvider, currentModel, env, apiKeys), system: getSystemPrompt(), From 937ba7e61b9ba45d5283fbac3e8a34bd39f7d641 Mon Sep 17 00:00:00 2001 From: Andrew Trokhymenko Date: Thu, 21 Nov 2024 00:17:06 -0500 Subject: [PATCH 09/37] model pickup --- app/lib/.server/llm/stream-text.ts | 2 ++ app/routes/api.chat.ts | 8 ++++++-- app/utils/constants.ts | 6 ++++-- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/app/lib/.server/llm/stream-text.ts b/app/lib/.server/llm/stream-text.ts index 160339610..3b563ea26 100644 --- a/app/lib/.server/llm/stream-text.ts +++ b/app/lib/.server/llm/stream-text.ts @@ -64,6 +64,8 @@ export function streamText( let currentModel = DEFAULT_MODEL; let currentProvider = DEFAULT_PROVIDER; + console.log('StreamText:', JSON.stringify(messages)); + const processedMessages = messages.map((message) => { if (message.role === 'user') { const { model, provider, content } = extractPropertiesFromMessage(message); diff --git a/app/routes/api.chat.ts b/app/routes/api.chat.ts index 8fdb3d743..d622b4649 100644 --- a/app/routes/api.chat.ts +++ b/app/routes/api.chat.ts @@ -31,11 +31,14 @@ function parseCookies(cookieHeader) { async function chatAction({ context, request }: ActionFunctionArgs) { - const { messages, imageData } = await request.json<{ + const { messages, imageData, model } = await request.json<{ messages: Messages, - imageData?: string[] + imageData?: string[], + model: string }>(); + console.log('ChatAction:', JSON.stringify(messages)); + const cookieHeader = request.headers.get("Cookie"); // Parse the cookie's value (returns an object or null if no cookie exists) @@ -47,6 +50,7 @@ async function chatAction({ context, request }: ActionFunctionArgs) { const options: StreamingOptions = { toolChoice: 'none', apiKeys, + model, onFinish: async ({ text: content, finishReason }) => { if (finishReason !== 'length') { return stream.close(); diff --git a/app/utils/constants.ts b/app/utils/constants.ts index 308832b93..501a87ea4 100644 --- a/app/utils/constants.ts +++ b/app/utils/constants.ts @@ -30,13 +30,15 @@ const PROVIDER_LIST: ProviderInfo[] = [ icon: "i-ph:cloud-arrow-down", }, { name: 'OpenAILike', - staticModels: [], + staticModels: [ + { name: 'o1-mini', label: 'o1-mini', provider: 'OpenAILike' }, + ], getDynamicModels: getOpenAILikeModels }, { name: 'OpenRouter', staticModels: [ - { name: 'gpt-4o', label: 'GPT-4o', provider: 'OpenAI' }, + { name: 'gpt-4o', label: 'GPT-4o', provider: 'OpenRouter' }, { name: 'anthropic/claude-3.5-sonnet', label: 'Anthropic: Claude 3.5 Sonnet (OpenRouter)', From 36b7d94bdd0ab8b92d780d581889c4dc75607cd7 Mon Sep 17 00:00:00 2001 From: navyseal4000 Date: Wed, 13 Nov 2024 21:36:50 -0600 Subject: [PATCH 10/37] Added speech to text capability --- app/components/chat/BaseChat.tsx | 79 ++++++++++++++++++++++++++++++++ app/types/global.d.ts | 2 + 2 files changed, 81 insertions(+) diff --git a/app/components/chat/BaseChat.tsx b/app/components/chat/BaseChat.tsx index 902cb232c..fee28c012 100644 --- a/app/components/chat/BaseChat.tsx +++ b/app/components/chat/BaseChat.tsx @@ -87,6 +87,35 @@ interface BaseChatProps { enhancePrompt?: () => void; } +const SpeechRecognitionButton = ({ + isListening, + onStart, + onStop, + disabled +}: { + isListening: boolean; + onStart: () => void; + onStop: () => void; + disabled: boolean; +}) => { + return ( + + {isListening ? ( +
+ ) : ( +
+ )} + + ); +}; + export const BaseChat = React.forwardRef( ( { @@ -114,6 +143,8 @@ export const BaseChat = React.forwardRef( const TEXTAREA_MAX_HEIGHT = chatStarted ? 400 : 200; const [apiKeys, setApiKeys] = useState>({}); const [modelList, setModelList] = useState(MODEL_LIST); + const [isListening, setIsListening] = useState(false); + const [recognition, setRecognition] = useState(null); useEffect(() => { // Load API keys from cookies on component mount @@ -134,8 +165,49 @@ export const BaseChat = React.forwardRef( 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(''); + + 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 updateApiKey = (provider: string, key: string) => { try { const updatedApiKeys = { ...apiKeys, [provider]: key }; @@ -284,6 +356,13 @@ export const BaseChat = React.forwardRef( )} + +
{input.length > 3 ? (
diff --git a/app/types/global.d.ts b/app/types/global.d.ts index a1f6789de..193c65d3e 100644 --- a/app/types/global.d.ts +++ b/app/types/global.d.ts @@ -1,3 +1,5 @@ interface Window { showDirectoryPicker(): Promise; + webkitSpeechRecognition: typeof SpeechRecognition; + SpeechRecognition: typeof SpeechRecognition; } From a896f3f312bcecf9b9df588b63a4a3b78efbe06d Mon Sep 17 00:00:00 2001 From: navyseal4000 Date: Thu, 21 Nov 2024 07:55:53 -0600 Subject: [PATCH 11/37] Clear speech to text, listening upon submission --- app/components/chat/BaseChat.tsx | 38 ++++++++++++++++++++++++++++---- app/utils/constants.ts | 2 +- 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/app/components/chat/BaseChat.tsx b/app/components/chat/BaseChat.tsx index fee28c012..dde0ccaad 100644 --- a/app/components/chat/BaseChat.tsx +++ b/app/components/chat/BaseChat.tsx @@ -145,6 +145,7 @@ export const BaseChat = React.forwardRef( const [modelList, setModelList] = useState(MODEL_LIST); const [isListening, setIsListening] = useState(false); const [recognition, setRecognition] = useState(null); + const [transcript, setTranscript] = useState(''); useEffect(() => { // Load API keys from cookies on component mount @@ -177,6 +178,9 @@ export const BaseChat = React.forwardRef( .map(result => result.transcript) .join(''); + setTranscript(transcript); + + if (handleInputChange) { const syntheticEvent = { target: { value: transcript }, @@ -208,6 +212,25 @@ export const BaseChat = React.forwardRef( } }; + 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 updateApiKey = (provider: string, key: string) => { try { const updatedApiKeys = { ...apiKeys, [provider]: key }; @@ -301,8 +324,11 @@ export const BaseChat = React.forwardRef( } event.preventDefault(); - - sendMessage?.(event); + if (isStreaming) { + handleStop?.(); + return; + } + handleSendMessage?.(event); } }} value={input} @@ -327,7 +353,7 @@ export const BaseChat = React.forwardRef( return; } - sendMessage?.(event); + handleSendMessage?.(event); }} /> )} @@ -384,7 +410,11 @@ export const BaseChat = React.forwardRef(