From 06e5489126c09184d75e1d51d3700c70950affe0 Mon Sep 17 00:00:00 2001 From: embire2 Date: Thu, 5 Feb 2026 17:21:00 +0200 Subject: [PATCH 1/2] feat: issue 2097 wave 2 features --- .../@settings/tabs/features/FeaturesTab.tsx | 74 ++++++++- app/components/chat/BaseChat.tsx | 33 +++- app/components/chat/Chat.client.tsx | 85 +++++++++- app/components/chat/ChatBox.tsx | 156 ++++++++++++++---- app/components/chat/PendingFileActions.tsx | 115 +++++++++++++ app/components/ui/BackgroundRays/index.tsx | 7 + app/components/workbench/Workbench.client.tsx | 2 +- app/lib/.server/llm/stream-text.ts | 47 +++++- app/lib/hooks/useSettings.ts | 48 ++++++ app/lib/stores/settings.ts | 32 ++++ app/lib/stores/workbench.ts | 138 +++++++++++++++- app/routes/api.chat.ts | 5 +- docs/issue-2097-tracker.txt | 8 + 13 files changed, 706 insertions(+), 44 deletions(-) create mode 100644 app/components/chat/PendingFileActions.tsx create mode 100644 docs/issue-2097-tracker.txt diff --git a/app/components/@settings/tabs/features/FeaturesTab.tsx b/app/components/@settings/tabs/features/FeaturesTab.tsx index 3b14a7565d..11c45087d5 100644 --- a/app/components/@settings/tabs/features/FeaturesTab.tsx +++ b/app/components/@settings/tabs/features/FeaturesTab.tsx @@ -111,10 +111,18 @@ export default function FeaturesTab() { isLatestBranch, contextOptimizationEnabled, eventLogs, + autoPromptEnhancement, + confirmFileWrites, + performanceMode, + agentMode, setAutoSelectTemplate, enableLatestBranch, enableContextOptimization, setEventLogs, + setAutoPromptEnhancement, + setConfirmFileWrites, + setPerformanceMode, + setAgentMode, setPromptId, promptId, } = useSettings(); @@ -169,12 +177,41 @@ export default function FeaturesTab() { toast.success(`Event logging ${enabled ? 'enabled' : 'disabled'}`); break; } + case 'autoPromptEnhancement': { + setAutoPromptEnhancement(enabled); + toast.success(`Auto prompt enhancement ${enabled ? 'enabled' : 'disabled'}`); + break; + } + case 'confirmFileWrites': { + setConfirmFileWrites(enabled); + toast.success(`Confirm file writes ${enabled ? 'enabled' : 'disabled'}`); + break; + } + case 'performanceMode': { + setPerformanceMode(enabled); + toast.success(`Performance mode ${enabled ? 'enabled' : 'disabled'}`); + break; + } + case 'agentMode': { + setAgentMode(enabled); + toast.success(`Agent mode ${enabled ? 'enabled' : 'disabled'}`); + break; + } default: break; } }, - [enableLatestBranch, setAutoSelectTemplate, enableContextOptimization, setEventLogs], + [ + enableLatestBranch, + setAutoSelectTemplate, + enableContextOptimization, + setEventLogs, + setAutoPromptEnhancement, + setConfirmFileWrites, + setPerformanceMode, + setAgentMode, + ], ); const features = { @@ -211,6 +248,41 @@ export default function FeaturesTab() { enabled: eventLogs, tooltip: 'Enabled by default to record detailed logs of system events and user actions', }, + { + id: 'autoPromptEnhancement', + title: 'Auto Prompt Enhancement', + description: 'Automatically enhance prompts before sending', + icon: 'i-bolt:stars', + enabled: autoPromptEnhancement, + beta: true, + tooltip: 'Adds an extra LLM step to refine prompts for clarity and completeness', + }, + { + id: 'confirmFileWrites', + title: 'Confirm File Changes', + description: 'Require approval before applying file edits', + icon: 'i-ph:git-diff', + enabled: confirmFileWrites, + beta: true, + tooltip: 'Stages AI file changes for review, similar to git confirmations', + }, + { + id: 'performanceMode', + title: 'Performance Mode', + description: 'Reduce visual effects for better performance', + icon: 'i-ph:speedometer', + enabled: performanceMode, + tooltip: 'Disables heavy visuals and blur effects to reduce CPU/GPU usage', + }, + { + id: 'agentMode', + title: 'Agent Mode', + description: 'Run a planning step before responses', + icon: 'i-ph:robot', + enabled: agentMode, + experimental: true, + tooltip: 'Adds a backend planning step to improve multi-step task execution', + }, ], beta: [], }; diff --git a/app/components/chat/BaseChat.tsx b/app/components/chat/BaseChat.tsx index 7daffb6dd9..716a3a38cb 100644 --- a/app/components/chat/BaseChat.tsx +++ b/app/components/chat/BaseChat.tsx @@ -33,6 +33,7 @@ import { ChatBox } from './ChatBox'; import type { DesignScheme } from '~/types/design-scheme'; import type { ElementInfo } from '~/components/workbench/Inspector'; import LlmErrorAlert from './LLMApiAlert'; +import { PendingFileActions } from './PendingFileActions'; const TEXTAREA_MIN_HEIGHT = 76; @@ -49,6 +50,15 @@ interface BaseChatProps { enhancingPrompt?: boolean; promptEnhanced?: boolean; input?: string; + autoPromptEnhancement?: boolean; + setAutoPromptEnhancement?: (enabled: boolean) => void; + agentMode?: boolean; + setAgentMode?: (enabled: boolean) => void; + performanceMode?: boolean; + setPerformanceMode?: (enabled: boolean) => void; + isAutoEnhancing?: boolean; + confirmFileWrites?: boolean; + setConfirmFileWrites?: (enabled: boolean) => void; model?: string; setModel?: (model: string) => void; provider?: ProviderInfo; @@ -97,6 +107,15 @@ export const BaseChat = React.forwardRef( setProvider, providerList, input = '', + autoPromptEnhancement, + setAutoPromptEnhancement, + agentMode, + setAgentMode, + performanceMode, + setPerformanceMode, + isAutoEnhancing, + confirmFileWrites, + setConfirmFileWrites, enhancingPrompt, handleInputChange, @@ -370,7 +389,7 @@ export const BaseChat = React.forwardRef( {() => { return chatStarted ? ( (
@@ -424,6 +443,7 @@ export const BaseChat = React.forwardRef( {llmErrorAlert && clearLlmErrorAlert?.()} />}
{progressAnnotations && } + ( handleStop={handleStop} handleSendMessage={handleSendMessage} enhancingPrompt={enhancingPrompt} + autoPromptEnhancement={autoPromptEnhancement} + setAutoPromptEnhancement={setAutoPromptEnhancement} + agentMode={agentMode} + setAgentMode={setAgentMode} + performanceMode={performanceMode} + setPerformanceMode={setPerformanceMode} + isAutoEnhancing={isAutoEnhancing} + confirmFileWrites={confirmFileWrites} + setConfirmFileWrites={setConfirmFileWrites} enhancePrompt={enhancePrompt} isListening={isListening} startListening={startListening} diff --git a/app/components/chat/Chat.client.tsx b/app/components/chat/Chat.client.tsx index c4706e1764..d1d3bff06d 100644 --- a/app/components/chat/Chat.client.tsx +++ b/app/components/chat/Chat.client.tsx @@ -100,8 +100,22 @@ export const ChatImpl = memo( (project) => project.id === supabaseConn.selectedProjectId, ); const supabaseAlert = useStore(workbenchStore.supabaseAlert); - const { activeProviders, promptId, autoSelectTemplate, contextOptimizationEnabled } = useSettings(); + const { + activeProviders, + promptId, + autoSelectTemplate, + contextOptimizationEnabled, + autoPromptEnhancement, + agentMode, + performanceMode, + confirmFileWrites, + setAutoPromptEnhancement, + setAgentMode, + setPerformanceMode, + setConfirmFileWrites, + } = useSettings(); const [llmErrorAlert, setLlmErrorAlert] = useState(undefined); + const [isAutoEnhancing, setIsAutoEnhancing] = useState(false); const [model, setModel] = useState(() => { const savedModel = Cookies.get('selectedModel'); return savedModel || DEFAULT_MODEL; @@ -140,6 +154,7 @@ export const ChatImpl = memo( contextOptimization: contextOptimizationEnabled, chatMode, designScheme, + agentMode, supabase: { isConnected: supabaseConn.isConnected, hasSelectedProject: !!selectedProject, @@ -386,6 +401,59 @@ export const ChatImpl = memo( return attachments; }; + const enhancePromptForSend = async (rawPrompt: string): Promise => { + if (!rawPrompt?.trim()) { + return rawPrompt; + } + + setIsAutoEnhancing(true); + + try { + const response = await fetch('/api/enhancer', { + method: 'POST', + body: JSON.stringify({ + message: rawPrompt, + model, + provider, + apiKeys, + }), + }); + + if (!response.ok || !response.body) { + throw new Error(`Enhancer failed: ${response.status}`); + } + + const reader = response.body.getReader(); + const decoder = new TextDecoder(); + let enhanced = ''; + + while (true) { + const { value, done } = await reader.read(); + + if (done) { + break; + } + + enhanced += decoder.decode(value); + } + + const trimmed = enhanced.trim(); + + if (trimmed && trimmed !== rawPrompt.trim()) { + toast.success('Prompt enhanced'); + return trimmed; + } + + return rawPrompt; + } catch (error) { + console.error('Auto prompt enhancement failed:', error); + toast.error('Auto prompt enhancement failed'); + return rawPrompt; + } finally { + setIsAutoEnhancing(false); + } + }; + const sendMessage = async (_event: React.UIEvent, messageInput?: string) => { const messageContent = messageInput || input; @@ -400,11 +468,15 @@ export const ChatImpl = memo( let finalMessageContent = messageContent; + if (autoPromptEnhancement && !messageInput) { + finalMessageContent = await enhancePromptForSend(finalMessageContent); + } + if (selectedElement) { console.log('Selected Element:', selectedElement); const elementInfo = `
${JSON.stringify(`${selectedElement.displayText}`)}
`; - finalMessageContent = messageContent + elementInfo; + finalMessageContent = finalMessageContent + elementInfo; } runAnimation(); @@ -643,6 +715,15 @@ export const ChatImpl = memo( apiKeys, ); }} + autoPromptEnhancement={autoPromptEnhancement} + setAutoPromptEnhancement={setAutoPromptEnhancement} + agentMode={agentMode} + setAgentMode={setAgentMode} + performanceMode={performanceMode} + setPerformanceMode={setPerformanceMode} + isAutoEnhancing={isAutoEnhancing} + confirmFileWrites={confirmFileWrites} + setConfirmFileWrites={setConfirmFileWrites} uploadedFiles={uploadedFiles} setUploadedFiles={setUploadedFiles} imageDataList={imageDataList} diff --git a/app/components/chat/ChatBox.tsx b/app/components/chat/ChatBox.tsx index 4cd9a149a2..78db65e8c1 100644 --- a/app/components/chat/ChatBox.tsx +++ b/app/components/chat/ChatBox.tsx @@ -2,6 +2,9 @@ import React from 'react'; import { ClientOnly } from 'remix-utils/client-only'; import { classNames } from '~/utils/classNames'; import { PROVIDER_LIST } from '~/utils/constants'; +import { useStore } from '@nanostores/react'; +import { chatStore } from '~/lib/stores/chat'; +import { workbenchStore } from '~/lib/stores/workbench'; import { ModelSelector } from '~/components/chat/ModelSelector'; import { APIKeyManager } from './APIKeyManager'; import { LOCAL_PROVIDERS } from '~/lib/stores/settings'; @@ -55,6 +58,15 @@ interface ChatBoxProps { handleStop?: (() => void) | undefined; enhancingPrompt?: boolean | undefined; enhancePrompt?: (() => void) | undefined; + autoPromptEnhancement?: boolean; + setAutoPromptEnhancement?: ((enabled: boolean) => void) | undefined; + agentMode?: boolean; + setAgentMode?: ((enabled: boolean) => void) | undefined; + performanceMode?: boolean; + setPerformanceMode?: ((enabled: boolean) => void) | undefined; + isAutoEnhancing?: boolean; + confirmFileWrites?: boolean; + setConfirmFileWrites?: ((enabled: boolean) => void) | undefined; chatMode?: 'discuss' | 'build'; setChatMode?: (mode: 'discuss' | 'build') => void; designScheme?: DesignScheme; @@ -64,10 +76,22 @@ interface ChatBoxProps { } export const ChatBox: React.FC = (props) => { + const showEffects = !props.performanceMode; + const showWorkbench = useStore(workbenchStore.showWorkbench); + + const toggleMobileView = () => { + const nextShowWorkbench = !showWorkbench; + workbenchStore.showWorkbench.set(nextShowWorkbench); + chatStore.setKey('showChat', !nextShowWorkbench); + }; + return (
= (props) => { */ )} > - - - - - - - - - - - - - - - - - - + {showEffects && ( + + + + + + + + + + + + + + + + + + + )}
{() => ( @@ -167,7 +193,9 @@ export const ChatBox: React.FC = (props) => {
)}