diff --git a/apps/example/src/index.tsx b/apps/example/src/index.tsx index 77d0d32b..eefd5ee8 100644 --- a/apps/example/src/index.tsx +++ b/apps/example/src/index.tsx @@ -12,6 +12,7 @@ root.render( ({ mcpHeaders: { 'http://localhost:3002': { diff --git a/packages/client/src/components/UseAIChat.test.tsx b/packages/client/src/components/UseAIChat.test.tsx index 9b42cb8a..c21dab48 100644 --- a/packages/client/src/components/UseAIChat.test.tsx +++ b/packages/client/src/components/UseAIChat.test.tsx @@ -3,6 +3,7 @@ import { describe, test, expect, mock } from 'bun:test'; import { render, fireEvent } from '@testing-library/react'; import { UseAIChat, __UseAIChatContext, type ChatUIContextValue } from './UseAIChat'; import type { PersistedMessage } from '../providers/chatRepository/types'; +import { StringsContext, defaultStrings } from '../theme'; function createContextValue(overrides: Partial = {}): ChatUIContextValue { return { @@ -116,4 +117,75 @@ describe('UseAIChat', () => { expect(queryByTestId('inline-save-command')).toBeNull(); }); }); + + describe('input disclaimer', () => { + const userMessage: PersistedMessage = { + id: 'msg-1', + role: 'user', + content: 'hello world', + createdAt: new Date(0), + }; + + test('stays hidden when the inputDisclaimer feature is not enabled', () => { + const ctx = createContextValue({ messages: [userMessage] }); + + const { queryByTestId } = render( + <__UseAIChatContext.Provider value={ctx}> + + + ); + + expect(queryByTestId('chat-input-disclaimer')).toBeNull(); + }); + + test('stays hidden while the chat is empty', () => { + const ctx = createContextValue({ enabledFeatures: { inputDisclaimer: true } }); + + const { queryByTestId } = render( + <__UseAIChatContext.Provider value={ctx}> + + + ); + + expect(queryByTestId('chat-input-disclaimer')).toBeNull(); + }); + + test('appears once the conversation has started', () => { + const ctx = createContextValue({ + messages: [userMessage], + enabledFeatures: { inputDisclaimer: true }, + }); + + const { getByTestId } = render( + <__UseAIChatContext.Provider value={ctx}> + + + ); + + expect(getByTestId('chat-input-disclaimer')).toHaveTextContent( + defaultStrings.input.disclaimer + ); + }); + + test('uses the overridden text from strings', () => { + const ctx = createContextValue({ + messages: [userMessage], + enabledFeatures: { inputDisclaimer: true }, + }); + const strings = { + ...defaultStrings, + input: { ...defaultStrings.input, disclaimer: 'Custom disclaimer' }, + }; + + const { getByTestId } = render( + + <__UseAIChatContext.Provider value={ctx}> + + + + ); + + expect(getByTestId('chat-input-disclaimer')).toHaveTextContent('Custom disclaimer'); + }); + }); }); diff --git a/packages/client/src/components/UseAIChatPanel.tsx b/packages/client/src/components/UseAIChatPanel.tsx index 944fd64a..751b7505 100644 --- a/packages/client/src/components/UseAIChatPanel.tsx +++ b/packages/client/src/components/UseAIChatPanel.tsx @@ -1381,6 +1381,23 @@ export function UseAIChatPanel({ )} + + {/* Accuracy disclaimer. Kept out of the empty chat so the first + impression stays clean; once the user has sent a message it stays + visible for the rest of the conversation. */} + {features.inputDisclaimer && displayMessages.length > 0 && ( +
+ {strings.input.disclaimer} +
+ )}