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
7 changes: 0 additions & 7 deletions ui/desktop/src/App.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}));
Expand Down
15 changes: 6 additions & 9 deletions ui/desktop/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

woohoo! two more to go! well, also this:

      <Suspense fallback={SuspenseLoader()}>
        <ConfigProvider>
          <ErrorBoundary>
            <App />
          </ErrorBoundary>
        </ConfigProvider>
      </Suspense>

);
}
70 changes: 4 additions & 66 deletions ui/desktop/src/components/ChatInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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) => {
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
};

Expand Down Expand Up @@ -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 = () => {
Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -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();
Expand All @@ -980,7 +921,6 @@ export default function ChatInput({
},
[
allDroppedFiles,
chatContext,
displayValue,
droppedFiles.length,
handleSubmit,
Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -1120,7 +1059,6 @@ export default function ChatInput({
isAnyDroppedFileLoading ||
isRecording ||
isTranscribing ||
!agentIsReady ||
isExtensionsLoading;

// Queue management functions - no storage persistence, only in-memory
Expand Down Expand Up @@ -1372,7 +1310,7 @@ export default function ChatInput({
? 'Recording...'
: isTranscribing
? 'Transcribing...'
: (chatContext?.agentWaitingMessage ?? 'Send')}
: 'Send'}
</p>
</TooltipContent>
</Tooltip>
Expand Down
22 changes: 0 additions & 22 deletions ui/desktop/src/contexts/ChatContext.tsx
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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;
Expand All @@ -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: '',
Expand All @@ -61,7 +43,6 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
recipe: null,
recipeParameterValues: null,
});
clearDraft();
};

const setRecipe = (recipe: Recipe | null) => {
Expand All @@ -88,9 +69,6 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
hasActiveSession,
setRecipe,
clearRecipe,
draft,
setDraft,
clearDraft,
contextKey,
agentWaitingMessage,
};
Expand Down
44 changes: 0 additions & 44 deletions ui/desktop/src/contexts/DraftContext.tsx

This file was deleted.

Loading