From c5158ff52c297516d61c23c47fc152f8dbdd952a Mon Sep 17 00:00:00 2001 From: Bohdan Triapitsyn Date: Thu, 29 Jan 2026 18:23:43 +0200 Subject: [PATCH] Chat input updates with Stop button and update dialog changelog UI (#241) * feat: re-organizaed input footer UI in chat Add StopIcon component and use it for aborting generation in chat controls Introduce ProviderLogoOrFallback to show provider logo or fallback icon in model controls Remove mobile-only abort button in StatusRow and simplify abort handling * feat: improved render of changelog in UpdateDialog --- packages/desktop/src-tauri/Cargo.lock | 2 +- packages/ui/src/components/chat/ChatInput.tsx | 65 ++----- .../ui/src/components/chat/ModelControls.tsx | 78 +++++--- packages/ui/src/components/chat/StatusRow.tsx | 20 +- packages/ui/src/components/icons/StopIcon.tsx | 20 ++ .../ui/src/components/ui/UpdateDialog.tsx | 182 +++++++++++++++--- 6 files changed, 251 insertions(+), 116 deletions(-) create mode 100644 packages/ui/src/components/icons/StopIcon.tsx diff --git a/packages/desktop/src-tauri/Cargo.lock b/packages/desktop/src-tauri/Cargo.lock index 1f68710e..9dfbb9d5 100644 --- a/packages/desktop/src-tauri/Cargo.lock +++ b/packages/desktop/src-tauri/Cargo.lock @@ -2994,7 +2994,7 @@ dependencies = [ [[package]] name = "openchamber-desktop" -version = "1.5.9" +version = "1.6.0" dependencies = [ "anyhow", "axum", diff --git a/packages/ui/src/components/chat/ChatInput.tsx b/packages/ui/src/components/chat/ChatInput.tsx index c736886a..2cd9d88b 100644 --- a/packages/ui/src/components/chat/ChatInput.tsx +++ b/packages/ui/src/components/chat/ChatInput.tsx @@ -4,7 +4,6 @@ import { RiAddCircleLine, RiAiAgentLine, RiAttachment2, - RiCloseCircleLine, RiFileUploadLine, RiSendPlane2Line, } from '@remixicon/react'; @@ -31,6 +30,7 @@ import { toast } from '@/components/ui'; import { useFileStore } from '@/stores/fileStore'; import { isVSCodeRuntime } from '@/lib/desktop'; import { isIMECompositionEvent } from '@/lib/ime'; +import { StopIcon } from '@/components/icons/StopIcon'; import { DropdownMenu, DropdownMenuContent, @@ -121,7 +121,6 @@ export const ChatInput: React.FC = ({ onOpenSettings, scrollToBo const abortCurrentOperation = useSessionStore((state) => state.abortCurrentOperation); const acknowledgeSessionAbort = useSessionStore((state) => state.acknowledgeSessionAbort); const abortPromptSessionId = useSessionStore((state) => state.abortPromptSessionId); - const abortPromptExpiresAt = useSessionStore((state) => state.abortPromptExpiresAt); const clearAbortPrompt = useSessionStore((state) => state.clearAbortPrompt); const sessionAbortFlags = useSessionStore((state) => state.sessionAbortFlags); const attachedFiles = useSessionStore((state) => state.attachedFiles); @@ -297,11 +296,6 @@ export const ChatInput: React.FC = ({ onOpenSettings, scrollToBo const canAbort = working.isWorking; - const isAbortPromptActive = React.useMemo(() => { - if (!currentSessionId) return false; - return abortPromptSessionId === currentSessionId && Boolean(abortPromptExpiresAt); - }, [abortPromptSessionId, abortPromptExpiresAt, currentSessionId]); - // Add message to queue instead of sending const handleQueueMessage = React.useCallback(() => { if (!hasContent || !currentSessionId) return; @@ -1138,7 +1132,7 @@ export const ChatInput: React.FC = ({ onOpenSettings, scrollToBo event.target.value = ''; }, [attachFiles]); - const footerGapClass = 'gap-x-1.5 gap-y-0'; + const footerGapClass = isMobile ? 'gap-x-0.5 gap-y-0' : 'gap-x-1.5 gap-y-0'; const isVSCode = isVSCodeRuntime(); const footerPaddingClass = isMobile ? 'px-1.5 py-1.5' : (isVSCode ? 'px-1.5 py-1' : 'px-2.5 py-1.5'); const footerHeightClass = isMobile ? 'h-9 w-9' : (isVSCode ? 'h-[22px] w-[22px]' : 'h-7 w-7'); @@ -1149,22 +1143,21 @@ export const ChatInput: React.FC = ({ onOpenSettings, scrollToBo 'flex items-center justify-center text-muted-foreground transition-none outline-none focus:outline-none flex-shrink-0' ); - // Desktop and VSCode: show abort button in footer when Esc triggered - const showAbortInFooter = !isMobile && isAbortPromptActive && canAbort; - - const actionButton = showAbortInFooter ? ( + const cancelButton = canAbort ? ( - ) : ( + ) : null; + + const sendButton = ( )} @@ -2261,21 +2283,15 @@ export const ModelControls: React.FC = ({ className }) => { type="button" onClick={() => setActiveMobilePanel('variant')} className={cn( - 'model-controls__variant-trigger flex items-center gap-1.5 transition-opacity min-w-0 focus:outline-none', + 'model-controls__variant-trigger flex items-center justify-center transition-opacity focus:outline-none', buttonHeight, + 'w-9', 'cursor-pointer hover:opacity-70', )} + aria-label={`Thinking: ${displayVariant}`} + title={`Thinking: ${displayVariant}`} > - - {displayVariant} - ); } @@ -2430,11 +2446,12 @@ export const ModelControls: React.FC = ({ className }) => { onTouchStart={() => handleLongPressStart('agent')} onTouchEnd={handleLongPressEnd} onTouchCancel={handleLongPressEnd} - className={cn( - 'model-controls__agent-trigger flex items-center gap-1.5 transition-opacity min-w-0 focus:outline-none', - buttonHeight, - 'cursor-pointer hover:opacity-70', - )} + className={cn( + 'model-controls__agent-trigger flex items-center gap-1 transition-opacity min-w-0 focus:outline-none', + buttonHeight, + 'cursor-pointer hover:opacity-70', + 'px-1', + )} > = ({ className }) => { }; const inlineClassName = cn( - '@container/model-controls flex items-center min-w-0', + (isMobile ? '@container' : '@container/model-controls'), + 'flex items-center min-w-0', // Only force full-width + truncation behaviors on true mobile layouts. - // VS Code also uses "compact" mode, but should keep its right-aligned inline sizing. + // VS Code uses desktop dropdowns. isMobile && 'w-full', className, ); @@ -2472,14 +2490,14 @@ export const ModelControls: React.FC = ({ className }) => {
- {renderVariantSelector()} - {renderModelSelector()} {renderAgentSelector()} + {renderModelSelector()} + {renderVariantSelector()}
diff --git a/packages/ui/src/components/chat/StatusRow.tsx b/packages/ui/src/components/chat/StatusRow.tsx index 8a44123a..1b0db201 100644 --- a/packages/ui/src/components/chat/StatusRow.tsx +++ b/packages/ui/src/components/chat/StatusRow.tsx @@ -55,9 +55,6 @@ interface StatusRowProps { abortActive?: boolean; completionId?: string | null; isComplete?: boolean; - // Abort state (for mobile/vscode) - showAbort?: boolean; - onAbort?: () => void; // Abort status display showAbortStatus?: boolean; } @@ -71,8 +68,6 @@ export const StatusRow: React.FC = ({ abortActive, completionId, isComplete, - showAbort, - onAbort, showAbortStatus, }) => { const [isExpanded, setIsExpanded] = React.useState(false); @@ -158,18 +153,6 @@ export const StatusRow: React.FC = ({ const toggleExpanded = () => setIsExpanded((prev) => !prev); - // Abort button for mobile/vscode - const abortButton = showAbort && onAbort ? ( - - ) : null; - // Todo trigger button const todoTrigger = hasActiveTodos ? (