diff --git a/src/apps/chat/components/composer/Composer.tsx b/src/apps/chat/components/composer/Composer.tsx index 22af4d97b..b0ed88cf7 100644 --- a/src/apps/chat/components/composer/Composer.tsx +++ b/src/apps/chat/components/composer/Composer.tsx @@ -25,6 +25,7 @@ import { AudioGenerator } from '~/common/util/audio/AudioGenerator'; import { AudioPlayer } from '~/common/util/audio/AudioPlayer'; import { ButtonAttachFilesMemo, openFileForAttaching } from '~/common/components/ButtonAttachFiles'; import { ChatBeamIcon } from '~/common/components/icons/ChatBeamIcon'; +import { ConfirmationModal } from '~/common/components/modals/ConfirmationModal'; import { ConversationsManager } from '~/common/chat-overlay/ConversationsManager'; import { DMessageMetadata, DMetaReferenceItem, messageFragmentsReduceText } from '~/common/stores/chat/chat.message'; import { ShortcutKey, ShortcutObject, useGlobalShortcuts } from '~/common/components/shortcuts/useGlobalShortcuts'; @@ -44,6 +45,7 @@ import { supportsScreenCapture } from '~/common/util/screenCaptureUtils'; import { useAppStateStore } from '~/common/state/store-appstate'; import { useChatComposerOverlayStore } from '~/common/chat-overlay/store-chat-overlay'; import { useDebouncer } from '~/common/components/useDebouncer'; +import { useOverlayComponents } from '~/common/layout/overlays/useOverlayComponents'; import { useUICounter, useUIPreferencesStore } from '~/common/state/store-ui'; import { useUXLabsStore } from '~/common/state/store-ux-labs'; @@ -114,6 +116,7 @@ export function Composer(props: { const micCardRef = React.useRef(null); // external state + const { showPromisedOverlay } = useOverlayComponents(); const { labsAttachScreenCapture, labsCameraDesktop, labsShowCost, labsShowShortcutBar } = useUXLabsStore(useShallow(state => ({ labsAttachScreenCapture: state.labsAttachScreenCapture, labsCameraDesktop: state.labsCameraDesktop, @@ -217,6 +220,24 @@ export function Composer(props: { }, [composerTextAreaRef, inReferenceTo]); + // Confirmation Modals + + const confirmProceedIfAttachmentsNotSupported = React.useCallback(async (): Promise => { + if (llmAttachmentDraftsCollection.canAttachAllFragments) return true; + return await showPromisedOverlay('composer-unsupported-attachments', { rejectWithValue: false }, ({ onResolve, onUserReject }) => ( + onResolve(true)} + confirmationText="Some attached files may not be fully compatible with the current AI model. This could affect processing. Would you like to review or proceed?" + positiveActionText="Proceed" + negativeActionText="Review Attachments" + title="Attachment Compatibility Notice" + /> + )); + }, [llmAttachmentDraftsCollection.canAttachAllFragments, showPromisedOverlay]); + + // Primary button const handleClear = React.useCallback(() => { @@ -229,6 +250,9 @@ export function Composer(props: { const handleSendAction = React.useCallback(async (_chatExecuteMode: ChatExecuteMode, composerText: string): Promise => { if (!isValidConversation(targetConversationId)) return false; + // await user confirmation (or rejection) if attachments are not supported + if (!await confirmProceedIfAttachmentsNotSupported()) return false; + // validate some chat mode inputs const isDraw = _chatExecuteMode === 'generate-image'; const isBlank = !composerText.trim(); @@ -550,7 +574,10 @@ export function Composer(props: { const sendButtonVariant: VariantProp = (isAppend || (isMobile && isTextBeam)) ? 'outlined' : 'solid'; - const sendButtonColor: ColorPaletteProp = assistantAbortible ? 'warning' : chatExecuteModeSendColor; + const sendButtonColor: ColorPaletteProp = + assistantAbortible ? 'warning' + : !llmAttachmentDraftsCollection.canAttachAllFragments ? 'warning' + : chatExecuteModeSendColor; const sendButtonLabel = chatExecuteModeSendLabel; @@ -562,6 +589,10 @@ export function Composer(props: { : isDraw ? : ; + const beamButtonColor: ColorPaletteProp | undefined = + !llmAttachmentDraftsCollection.canAttachAllFragments ? 'warning' + : undefined; + let textPlaceholder: string = isDraw ? 'Describe an idea or a drawing...' : isReAct ? 'Multi-step reasoning question...' @@ -845,7 +876,7 @@ export function Composer(props: { {!assistantAbortible ? ( diff --git a/src/common/layout/overlays/store-overlays.ts b/src/common/layout/overlays/store-overlays.ts index 05518fad4..fc2eb8370 100644 --- a/src/common/layout/overlays/store-overlays.ts +++ b/src/common/layout/overlays/store-overlays.ts @@ -24,6 +24,7 @@ export type GlobalOverlayId = // string - disabled so we keep an orderliness | 'shortcuts-confirm-close' | 'blocks-off-enhance-code' | 'llms-service-remove' + | 'composer-unsupported-attachments' // The LLM does not seem to support this mime type - continue anyway? // | 'agi-patch-workflow-save' // make sure we use it ;