Removed drafts and agentIsReady in ChatInput (#5366)
This commit is contained in:
@@ -131,18 +131,11 @@ vi.mock('./contexts/ChatContext', () => ({
|
||||
hasActiveSession: false,
|
||||
setRecipe: vi.fn(),
|
||||
clearRecipe: vi.fn(),
|
||||
draft: '',
|
||||
setDraft: vi.fn(),
|
||||
clearDraft: vi.fn(),
|
||||
contextKey: 'hub',
|
||||
}),
|
||||
DEFAULT_CHAT_TITLE: 'New Chat', // Keep this from HEAD
|
||||
}));
|
||||
|
||||
vi.mock('./contexts/DraftContext', () => ({
|
||||
DraftProvider: ({ children }: { children: React.ReactNode }) => <>{children}</>,
|
||||
}));
|
||||
|
||||
vi.mock('./components/ui/ConfirmationModal', () => ({
|
||||
ConfirmationModal: () => null,
|
||||
}));
|
||||
|
||||
@@ -27,7 +27,6 @@ import SchedulesView from './components/schedule/SchedulesView';
|
||||
import ProviderSettings from './components/settings/providers/ProviderSettingsPage';
|
||||
import { AppLayout } from './components/Layout/AppLayout';
|
||||
import { ChatProvider } from './contexts/ChatContext';
|
||||
import { DraftProvider } from './contexts/DraftContext';
|
||||
|
||||
import 'react-toastify/dist/ReactToastify.css';
|
||||
import { useConfig } from './components/ConfigContext';
|
||||
@@ -638,13 +637,11 @@ export function AppInner() {
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<DraftProvider>
|
||||
<ModelAndProviderProvider>
|
||||
<HashRouter>
|
||||
<AppInner />
|
||||
</HashRouter>
|
||||
<AnnouncementModal />
|
||||
</ModelAndProviderProvider>
|
||||
</DraftProvider>
|
||||
<ModelAndProviderProvider>
|
||||
<HashRouter>
|
||||
<AppInner />
|
||||
</HashRouter>
|
||||
<AnnouncementModal />
|
||||
</ModelAndProviderProvider>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@ import { WaveformVisualizer } from './WaveformVisualizer';
|
||||
import { toastError } from '../toasts';
|
||||
import MentionPopover, { FileItemWithMatch } from './MentionPopover';
|
||||
import { useDictationSettings } from '../hooks/useDictationSettings';
|
||||
import { useChatContext } from '../contexts/ChatContext';
|
||||
import { COST_TRACKING_ENABLED, VOICE_DICTATION_ELEVENLABS_ENABLED } from '../updates';
|
||||
import { CostTracker } from './bottom_menu/CostTracker';
|
||||
import { DroppedFile, useFileDrop } from '../hooks/useFileDrop';
|
||||
@@ -142,20 +141,8 @@ export default function ChatInput({
|
||||
const { getCurrentModelAndProvider, currentModel, currentProvider } = useModelAndProvider();
|
||||
const [tokenLimit, setTokenLimit] = useState<number>(TOKEN_LIMIT_DEFAULT);
|
||||
const [isTokenLimitLoaded, setIsTokenLimitLoaded] = useState(false);
|
||||
|
||||
// Draft functionality - get chat context and global draft context
|
||||
// We need to handle the case where ChatInput is used without ChatProvider (e.g., in Hub)
|
||||
const chatContext = useChatContext(); // This should always be available now
|
||||
const agentIsReady = chatContext === null || chatContext.agentWaitingMessage === null;
|
||||
const draftLoadedRef = useRef(false);
|
||||
|
||||
const [diagnosticsOpen, setDiagnosticsOpen] = useState(false);
|
||||
|
||||
// Debug logging for draft context
|
||||
useEffect(() => {
|
||||
// Debug logging removed - draft functionality is working correctly
|
||||
}, [chatContext?.contextKey, chatContext?.draft, chatContext]);
|
||||
|
||||
// Save queue state (paused/interrupted) to storage
|
||||
useEffect(() => {
|
||||
try {
|
||||
@@ -281,9 +268,6 @@ export default function ChatInput({
|
||||
setValue(initialValue);
|
||||
setDisplayValue(initialValue);
|
||||
|
||||
// Reset draft loaded flag when initialValue changes
|
||||
draftLoadedRef.current = false;
|
||||
|
||||
// Use a functional update to get the current pastedImages
|
||||
// and perform cleanup. This avoids needing pastedImages in the deps.
|
||||
setPastedImages((currentPastedImages) => {
|
||||
@@ -313,38 +297,6 @@ export default function ChatInput({
|
||||
}
|
||||
}, [recipeAccepted, initialPrompt, messages.length]);
|
||||
|
||||
// Draft functionality - load draft if no initial value or recipe
|
||||
useEffect(() => {
|
||||
// Reset draft loaded flag when context changes
|
||||
draftLoadedRef.current = false;
|
||||
}, [chatContext?.contextKey]);
|
||||
|
||||
useEffect(() => {
|
||||
// Only load draft once and if conditions are met
|
||||
if (!initialValue && !recipe && !draftLoadedRef.current && chatContext) {
|
||||
const draftText = chatContext.draft || '';
|
||||
|
||||
if (draftText) {
|
||||
setDisplayValue(draftText);
|
||||
setValue(draftText);
|
||||
}
|
||||
|
||||
// Always mark as loaded after checking, regardless of whether we found a draft
|
||||
draftLoadedRef.current = true;
|
||||
}
|
||||
}, [chatContext, initialValue, recipe]);
|
||||
|
||||
// Save draft when user types (debounced)
|
||||
const debouncedSaveDraft = useMemo(
|
||||
() =>
|
||||
debounce((value: string) => {
|
||||
if (chatContext && chatContext.setDraft) {
|
||||
chatContext.setDraft(value);
|
||||
}
|
||||
}, 500), // Save draft after 500ms of no typing
|
||||
[chatContext]
|
||||
);
|
||||
|
||||
// State to track if the IME is composing (i.e., in the middle of Japanese IME input)
|
||||
const [isComposing, setIsComposing] = useState(false);
|
||||
const [historyIndex, setHistoryIndex] = useState(-1);
|
||||
@@ -608,13 +560,9 @@ export default function ChatInput({
|
||||
const val = evt.target.value;
|
||||
const cursorPosition = evt.target.selectionStart;
|
||||
|
||||
setDisplayValue(val); // Update display immediately
|
||||
updateValue(val); // Update actual value immediately for better responsiveness
|
||||
debouncedSaveDraft(val); // Save draft with debounce
|
||||
// Mark that the user has typed something
|
||||
setDisplayValue(val);
|
||||
updateValue(val);
|
||||
setHasUserTyped(true);
|
||||
|
||||
// Check for @ mention
|
||||
checkForMention(val, cursorPosition, evt.target);
|
||||
};
|
||||
|
||||
@@ -769,9 +717,8 @@ export default function ChatInput({
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
debouncedAutosize.cancel?.();
|
||||
debouncedSaveDraft.cancel?.();
|
||||
};
|
||||
}, [debouncedAutosize, debouncedSaveDraft]);
|
||||
}, [debouncedAutosize]);
|
||||
|
||||
// Handlers for composition events, which are crucial for proper IME behavior
|
||||
const handleCompositionStart = () => {
|
||||
@@ -910,7 +857,6 @@ export default function ChatInput({
|
||||
|
||||
const canSubmit =
|
||||
!isLoading &&
|
||||
agentIsReady &&
|
||||
(displayValue.trim() ||
|
||||
pastedImages.some((img) => img.filePath && !img.error && !img.isLoading) ||
|
||||
allDroppedFiles.some((file) => !file.error && !file.isLoading));
|
||||
@@ -964,11 +910,6 @@ export default function ChatInput({
|
||||
setIsInGlobalHistory(false);
|
||||
setHasUserTyped(false);
|
||||
|
||||
// Clear draft when message is sent
|
||||
if (chatContext && chatContext.clearDraft) {
|
||||
chatContext.clearDraft();
|
||||
}
|
||||
|
||||
// Clear both parent and local dropped files after processing
|
||||
if (onFilesProcessed && droppedFiles.length > 0) {
|
||||
onFilesProcessed();
|
||||
@@ -980,7 +921,6 @@ export default function ChatInput({
|
||||
},
|
||||
[
|
||||
allDroppedFiles,
|
||||
chatContext,
|
||||
displayValue,
|
||||
droppedFiles.length,
|
||||
handleSubmit,
|
||||
@@ -1066,7 +1006,6 @@ export default function ChatInput({
|
||||
e.preventDefault();
|
||||
const canSubmit =
|
||||
!isLoading &&
|
||||
agentIsReady &&
|
||||
(displayValue.trim() ||
|
||||
pastedImages.some((img) => img.filePath && !img.error && !img.isLoading) ||
|
||||
allDroppedFiles.some((file) => !file.error && !file.isLoading));
|
||||
@@ -1120,7 +1059,6 @@ export default function ChatInput({
|
||||
isAnyDroppedFileLoading ||
|
||||
isRecording ||
|
||||
isTranscribing ||
|
||||
!agentIsReady ||
|
||||
isExtensionsLoading;
|
||||
|
||||
// Queue management functions - no storage persistence, only in-memory
|
||||
@@ -1372,7 +1310,7 @@ export default function ChatInput({
|
||||
? 'Recording...'
|
||||
: isTranscribing
|
||||
? 'Transcribing...'
|
||||
: (chatContext?.agentWaitingMessage ?? 'Send')}
|
||||
: 'Send'}
|
||||
</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React, { createContext, useContext, ReactNode } from 'react';
|
||||
import { ChatType } from '../types/chat';
|
||||
import { Recipe } from '../recipe';
|
||||
import { useDraftContext } from './DraftContext';
|
||||
|
||||
// TODO(Douwe): We should not need this anymore
|
||||
export const DEFAULT_CHAT_TITLE = 'New Chat';
|
||||
@@ -13,10 +12,6 @@ interface ChatContextType {
|
||||
hasActiveSession: boolean;
|
||||
setRecipe: (recipe: Recipe | null) => void;
|
||||
clearRecipe: () => void;
|
||||
// Draft functionality
|
||||
draft: string;
|
||||
setDraft: (draft: string) => void;
|
||||
clearDraft: () => void;
|
||||
// Context identification
|
||||
contextKey: string; // 'hub' or 'pair-{sessionId}'
|
||||
agentWaitingMessage: string | null;
|
||||
@@ -39,19 +34,6 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
||||
agentWaitingMessage,
|
||||
contextKey = 'hub',
|
||||
}) => {
|
||||
const draftContext = useDraftContext();
|
||||
|
||||
// Draft functionality using the app-level DraftContext
|
||||
const draft = draftContext.getDraft(contextKey);
|
||||
|
||||
const setDraft = (newDraft: string) => {
|
||||
draftContext.setDraft(contextKey, newDraft);
|
||||
};
|
||||
|
||||
const clearDraft = () => {
|
||||
draftContext.clearDraft(contextKey);
|
||||
};
|
||||
|
||||
const resetChat = () => {
|
||||
setChat({
|
||||
sessionId: '',
|
||||
@@ -61,7 +43,6 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
||||
recipe: null,
|
||||
recipeParameterValues: null,
|
||||
});
|
||||
clearDraft();
|
||||
};
|
||||
|
||||
const setRecipe = (recipe: Recipe | null) => {
|
||||
@@ -88,9 +69,6 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
||||
hasActiveSession,
|
||||
setRecipe,
|
||||
clearRecipe,
|
||||
draft,
|
||||
setDraft,
|
||||
clearDraft,
|
||||
contextKey,
|
||||
agentWaitingMessage,
|
||||
};
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
import React, { createContext, useContext, useState, ReactNode } from 'react';
|
||||
|
||||
interface DraftContextType {
|
||||
getDraft: (contextKey: string) => string;
|
||||
setDraft: (contextKey: string, draft: string) => void;
|
||||
clearDraft: (contextKey: string) => void;
|
||||
}
|
||||
|
||||
const DraftContext = createContext<DraftContextType | undefined>(undefined);
|
||||
|
||||
export const DraftProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
|
||||
// Store all drafts by contextKey
|
||||
const [drafts, setDrafts] = useState<Record<string, string>>({});
|
||||
|
||||
const getDraft = (contextKey: string): string => {
|
||||
return drafts[contextKey] || '';
|
||||
};
|
||||
|
||||
const setDraft = (contextKey: string, draft: string) => {
|
||||
setDrafts((prev) => ({ ...prev, [contextKey]: draft }));
|
||||
};
|
||||
|
||||
const clearDraft = (contextKey: string) => {
|
||||
setDrafts((prev) => {
|
||||
const newDrafts = { ...prev };
|
||||
delete newDrafts[contextKey];
|
||||
return newDrafts;
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<DraftContext.Provider value={{ getDraft, setDraft, clearDraft }}>
|
||||
{children}
|
||||
</DraftContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useDraftContext = (): DraftContextType => {
|
||||
const context = useContext(DraftContext);
|
||||
if (context === undefined) {
|
||||
throw new Error('useDraftContext must be used within a DraftProvider');
|
||||
}
|
||||
return context;
|
||||
};
|
||||
Reference in New Issue
Block a user