diff --git a/packages/gitbook/src/components/AI/useAIChat.tsx b/packages/gitbook/src/components/AI/useAIChat.tsx index b734483aa..b5a45e84d 100644 --- a/packages/gitbook/src/components/AI/useAIChat.tsx +++ b/packages/gitbook/src/components/AI/useAIChat.tsx @@ -31,6 +31,11 @@ export type AIChatState = { */ query: string | null; + /** + * The first query sent to the AI. This is appended to the URL when the AI chat is opened. + */ + initialQuery: string | null; + /** * Messages in the session. */ @@ -79,6 +84,7 @@ const globalState = zustand.create<{ followUpSuggestions: [], loading: false, error: false, + initialQuery: null, }, setState: (fn) => set((state) => ({ state: { ...state.state, ...fn(state.state) } })), }; @@ -102,17 +108,14 @@ export function useAIChatController(): AIChatController { const trackEvent = useTrackEvent(); const [searchState, setSearchState] = useSearch(true); - // Track if we've initialized from the URL ask parameter - const hasInitializedFromUrlRef = React.useRef(false); - // Open AI chat and sync with search state const onOpen = React.useCallback(() => { - const { messages } = globalState.getState().state; + const { initialQuery } = globalState.getState().state; setState((state) => ({ ...state, opened: true })); // Update search state to show ask mode with first message or current ask value setSearchState((prev) => ({ - ask: prev?.ask ?? messages[0]?.query ?? '', + ask: prev?.ask ?? initialQuery ?? '', query: prev?.query ?? null, global: prev?.global ?? false, open: false, // Close search popover when opening chat @@ -249,11 +252,9 @@ export function useAIChatController(): AIChatController { followUpSuggestions: [], responseId: null, error: false, + initialQuery: null, })); - // Reset initialization flag so URL ask can be processed again - hasInitializedFromUrlRef.current = false; - // Reset ask parameter to empty string (keeps chat open but clears content) setSearchState((prev) => ({ ask: '', @@ -277,18 +278,21 @@ export function useAIChatController(): AIChatController { // Auto-post the message if ask has content if (searchState?.ask?.trim()) { + const trimmedAsk = searchState.ask.trim(); + const { loading, initialQuery } = globalState.getState().state; + // Don't trigger if we're already posting a message - const loading = globalState.getState().state.loading; if (loading) return; - // Only initialize once from URL - if (hasInitializedFromUrlRef.current) return; + // Only initialize once per URL ask value + if (initialQuery === trimmedAsk) return; // Wait for messageContextRef to be defined before proceeding if (!messageContextRef.current?.location) return; - hasInitializedFromUrlRef.current = true; - onPostMessage({ message: searchState.ask.trim() }); + // Mark this ask value as processed + setState((state) => ({ ...state, initialQuery: trimmedAsk })); + onPostMessage({ message: trimmedAsk }); } }, [ searchState?.ask, @@ -296,6 +300,7 @@ export function useAIChatController(): AIChatController { searchState?.open, messageContextRef, onOpen, + setState, onPostMessage, ]); diff --git a/packages/gitbook/src/components/AIChat/AIChat.tsx b/packages/gitbook/src/components/AIChat/AIChat.tsx index 39e2d8042..df1e91e64 100644 --- a/packages/gitbook/src/components/AIChat/AIChat.tsx +++ b/packages/gitbook/src/components/AIChat/AIChat.tsx @@ -93,6 +93,15 @@ export function AIChatWindow(props: { block: 'start', }); + const timeout = setTimeout(() => { + if (lastUserMessageRef.current) { + lastUserMessageRef.current.scrollIntoView({ + behavior: 'smooth', + block: 'start', + }); + } + }, 100); + // We want the chat messages to scroll underneath the input, but they should scroll past the input when scrolling all the way down. // The best way to do this is to observe the input height and adjust the padding bottom of the scroll container accordingly. const observer = new ResizeObserver((entries) => { @@ -103,7 +112,10 @@ export function AIChatWindow(props: { if (inputRef.current) { observer.observe(inputRef.current); } - return () => observer.disconnect(); + return () => { + observer.disconnect(); + clearTimeout(timeout); + }; }, []); return ( diff --git a/packages/gitbook/src/components/AIChat/AIChatInput.tsx b/packages/gitbook/src/components/AIChat/AIChatInput.tsx index 8afeb30bb..7df985fc7 100644 --- a/packages/gitbook/src/components/AIChat/AIChatInput.tsx +++ b/packages/gitbook/src/components/AIChat/AIChatInput.tsx @@ -38,7 +38,7 @@ export function AIChatInput(props: { // This fixes inconsistent focus behaviour across browsers const timeout = setTimeout(() => { inputRef.current?.focus(); - }, 50); + }, 150); return () => clearTimeout(timeout); } diff --git a/packages/gitbook/src/components/Search/SearchContainer.tsx b/packages/gitbook/src/components/Search/SearchContainer.tsx index bf08699b7..d740e6b08 100644 --- a/packages/gitbook/src/components/Search/SearchContainer.tsx +++ b/packages/gitbook/src/components/Search/SearchContainer.tsx @@ -54,10 +54,12 @@ export function SearchContainer(props: SearchContainerProps) { const onClose = React.useCallback( async (to?: string) => { - if (state?.query === '') { - await setSearchState(null); - } else if (state) { - await setSearchState({ ...state, open: false }); + if (state) { + await setSearchState({ + ...state, + open: false, + query: state.query === '' ? null : state.query, + }); } if (to) { @@ -131,6 +133,8 @@ export function SearchContainer(props: SearchContainerProps) { const normalizedQuery = state?.query?.trim() ?? ''; const normalizedAsk = state?.ask?.trim() ?? ''; + const showAsk = aiMode === CustomizationAIMode.Search && normalizedAsk; + return ( - {isMultiVariants && !state?.ask ? ( + {isMultiVariants && !showAsk ? ( ) : null} - {state !== null && !state.ask ? ( + {state !== null && !showAsk ? ( ) : null} - {normalizedAsk ? : null} + {showAsk ? : null} ) : null } diff --git a/packages/gitbook/src/intl/translations/de.ts b/packages/gitbook/src/intl/translations/de.ts index 865f601f8..09175cca8 100644 --- a/packages/gitbook/src/intl/translations/de.ts +++ b/packages/gitbook/src/intl/translations/de.ts @@ -88,7 +88,7 @@ export const de = { ai_chat_context_info_provided_by_the_site: 'Von der Website bereitgestellte Informationen', ai_chat_context_previous_messages: 'Vorherige Nachrichten', ai_chat_context_disclaimer: 'KI-Antworten können Fehler enthalten.', - ai_chat_input_placeholder: 'Fragen, suchen oder Aktion ausführen...', + ai_chat_input_placeholder: 'Fragen, suchen oder erklären...', send: 'Senden', actions: 'Aktionen', ai_chat_suggested_questions_title: 'Vorgeschlagene Fragen', diff --git a/packages/gitbook/src/intl/translations/en.ts b/packages/gitbook/src/intl/translations/en.ts index 00c3cbb71..c93508783 100644 --- a/packages/gitbook/src/intl/translations/en.ts +++ b/packages/gitbook/src/intl/translations/en.ts @@ -85,7 +85,7 @@ export const en = { ai_chat_context_info_provided_by_the_site: 'Info provided by the site', ai_chat_context_previous_messages: 'Previous messages', ai_chat_context_disclaimer: 'AI responses may contain mistakes.', - ai_chat_input_placeholder: 'Ask, search, or take action...', + ai_chat_input_placeholder: 'Ask, search, or explain...', send: 'Send', actions: 'Actions', ai_chat_suggested_questions_title: 'Suggested questions', diff --git a/packages/gitbook/src/intl/translations/es.ts b/packages/gitbook/src/intl/translations/es.ts index 521b9d594..8a4cfc862 100644 --- a/packages/gitbook/src/intl/translations/es.ts +++ b/packages/gitbook/src/intl/translations/es.ts @@ -89,7 +89,7 @@ export const es: TranslationLanguage = { ai_chat_context_info_provided_by_the_site: 'Información proporcionada por el sitio', ai_chat_context_previous_messages: 'Mensajes anteriores', ai_chat_context_disclaimer: 'Las respuestas de IA pueden contener errores.', - ai_chat_input_placeholder: 'Pregunta, busca o realiza una acción...', + ai_chat_input_placeholder: 'Pregunta, busca o explica...', send: 'Enviar', actions: 'Acciones', ai_chat_suggested_questions_title: 'Preguntas sugeridas', diff --git a/packages/gitbook/src/intl/translations/fr.ts b/packages/gitbook/src/intl/translations/fr.ts index e6a27b0d2..3c16134b0 100644 --- a/packages/gitbook/src/intl/translations/fr.ts +++ b/packages/gitbook/src/intl/translations/fr.ts @@ -84,7 +84,7 @@ export const fr = { ai_chat_context_info_provided_by_the_site: 'Informations fournies par le site', ai_chat_context_previous_messages: 'Messages précédents', ai_chat_context_disclaimer: 'Les réponses générées peuvent contenir des erreurs.', - ai_chat_input_placeholder: 'Rechercher…', + ai_chat_input_placeholder: 'Demander, rechercher ou expliquer...', send: 'Envoyer', actions: 'Actions', ai_chat_suggested_questions_title: 'Suggestions de questions', diff --git a/packages/gitbook/src/intl/translations/ja.ts b/packages/gitbook/src/intl/translations/ja.ts index 105c757c7..a20ecd824 100644 --- a/packages/gitbook/src/intl/translations/ja.ts +++ b/packages/gitbook/src/intl/translations/ja.ts @@ -87,7 +87,7 @@ export const ja: TranslationLanguage = { ai_chat_context_info_provided_by_the_site: 'サイトから提供された情報', ai_chat_context_previous_messages: '以前のメッセージ', ai_chat_context_disclaimer: 'AIの回答には誤りが含まれる場合があります。', - ai_chat_input_placeholder: '質問、検索、またはアクションを実行...', + ai_chat_input_placeholder: '質問、検索、または説明...', send: '送信', actions: 'アクション', ai_chat_suggested_questions_title: 'おすすめの質問', diff --git a/packages/gitbook/src/intl/translations/nl.ts b/packages/gitbook/src/intl/translations/nl.ts index e269d1b19..4402ce69b 100644 --- a/packages/gitbook/src/intl/translations/nl.ts +++ b/packages/gitbook/src/intl/translations/nl.ts @@ -87,7 +87,7 @@ export const nl: TranslationLanguage = { ai_chat_context_info_provided_by_the_site: 'Informatie verstrekt door de site', ai_chat_context_previous_messages: 'Vorige berichten', ai_chat_context_disclaimer: 'AI-antwoorden kunnen fouten bevatten.', - ai_chat_input_placeholder: 'Vraag, zoek of voer een actie uit...', + ai_chat_input_placeholder: 'Vraag, zoek of leg uit...', send: 'Versturen', actions: 'Acties', ai_chat_suggested_questions_title: 'Voorgestelde vragen', diff --git a/packages/gitbook/src/intl/translations/no.ts b/packages/gitbook/src/intl/translations/no.ts index e10c41194..1b5107552 100644 --- a/packages/gitbook/src/intl/translations/no.ts +++ b/packages/gitbook/src/intl/translations/no.ts @@ -88,7 +88,7 @@ export const no: TranslationLanguage = { ai_chat_context_info_provided_by_the_site: 'Informasjon gitt av nettstedet', ai_chat_context_previous_messages: 'Tidligere meldinger', ai_chat_context_disclaimer: 'AI-svar kan inneholde feil.', - ai_chat_input_placeholder: 'Spør, søk eller utfør en handling...', + ai_chat_input_placeholder: 'Spør, søk eller forklar...', send: 'Send', actions: 'Handlinger', ai_chat_suggested_questions_title: 'Foreslåtte spørsmål', diff --git a/packages/gitbook/src/intl/translations/pt-br.ts b/packages/gitbook/src/intl/translations/pt-br.ts index f38285810..0dea9c55e 100644 --- a/packages/gitbook/src/intl/translations/pt-br.ts +++ b/packages/gitbook/src/intl/translations/pt-br.ts @@ -87,7 +87,7 @@ export const pt_br = { ai_chat_context_info_provided_by_the_site: 'Informações fornecidas pelo site', ai_chat_context_previous_messages: 'Mensagens anteriores', ai_chat_context_disclaimer: 'Respostas de IA podem conter erros.', - ai_chat_input_placeholder: 'Pergunte, pesquise ou execute uma ação...', + ai_chat_input_placeholder: 'Pergunte, pesquise ou explique...', send: 'Enviar', actions: 'Ações', ai_chat_suggested_questions_title: 'Perguntas sugeridas', diff --git a/packages/gitbook/src/intl/translations/zh.ts b/packages/gitbook/src/intl/translations/zh.ts index 253f6d38f..d08be50b0 100644 --- a/packages/gitbook/src/intl/translations/zh.ts +++ b/packages/gitbook/src/intl/translations/zh.ts @@ -84,7 +84,7 @@ export const zh: TranslationLanguage = { ai_chat_context_info_provided_by_the_site: '网站提供的信息', ai_chat_context_previous_messages: '之前的消息', ai_chat_context_disclaimer: '人工智能的回答可能包含错误。', - ai_chat_input_placeholder: '询问、搜索或执行操作...', + ai_chat_input_placeholder: '询问、搜索或解释...', send: '发送', actions: '操作', ai_chat_suggested_questions_title: '建议的问题',