Skip to content

Commit ea37977

Browse files
authored
Fix inconsistent state between q= and ask= and keyboard navigation (#3519)
1 parent 81da82a commit ea37977

File tree

13 files changed

+52
-31
lines changed

13 files changed

+52
-31
lines changed

packages/gitbook/src/components/AI/useAIChat.tsx

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@ export type AIChatState = {
3131
*/
3232
query: string | null;
3333

34+
/**
35+
* The first query sent to the AI. This is appended to the URL when the AI chat is opened.
36+
*/
37+
initialQuery: string | null;
38+
3439
/**
3540
* Messages in the session.
3641
*/
@@ -79,6 +84,7 @@ const globalState = zustand.create<{
7984
followUpSuggestions: [],
8085
loading: false,
8186
error: false,
87+
initialQuery: null,
8288
},
8389
setState: (fn) => set((state) => ({ state: { ...state.state, ...fn(state.state) } })),
8490
};
@@ -102,17 +108,14 @@ export function useAIChatController(): AIChatController {
102108
const trackEvent = useTrackEvent();
103109
const [searchState, setSearchState] = useSearch(true);
104110

105-
// Track if we've initialized from the URL ask parameter
106-
const hasInitializedFromUrlRef = React.useRef<boolean>(false);
107-
108111
// Open AI chat and sync with search state
109112
const onOpen = React.useCallback(() => {
110-
const { messages } = globalState.getState().state;
113+
const { initialQuery } = globalState.getState().state;
111114
setState((state) => ({ ...state, opened: true }));
112115

113116
// Update search state to show ask mode with first message or current ask value
114117
setSearchState((prev) => ({
115-
ask: prev?.ask ?? messages[0]?.query ?? '',
118+
ask: prev?.ask ?? initialQuery ?? '',
116119
query: prev?.query ?? null,
117120
global: prev?.global ?? false,
118121
open: false, // Close search popover when opening chat
@@ -249,11 +252,9 @@ export function useAIChatController(): AIChatController {
249252
followUpSuggestions: [],
250253
responseId: null,
251254
error: false,
255+
initialQuery: null,
252256
}));
253257

254-
// Reset initialization flag so URL ask can be processed again
255-
hasInitializedFromUrlRef.current = false;
256-
257258
// Reset ask parameter to empty string (keeps chat open but clears content)
258259
setSearchState((prev) => ({
259260
ask: '',
@@ -277,25 +278,29 @@ export function useAIChatController(): AIChatController {
277278

278279
// Auto-post the message if ask has content
279280
if (searchState?.ask?.trim()) {
281+
const trimmedAsk = searchState.ask.trim();
282+
const { loading, initialQuery } = globalState.getState().state;
283+
280284
// Don't trigger if we're already posting a message
281-
const loading = globalState.getState().state.loading;
282285
if (loading) return;
283286

284-
// Only initialize once from URL
285-
if (hasInitializedFromUrlRef.current) return;
287+
// Only initialize once per URL ask value
288+
if (initialQuery === trimmedAsk) return;
286289

287290
// Wait for messageContextRef to be defined before proceeding
288291
if (!messageContextRef.current?.location) return;
289292

290-
hasInitializedFromUrlRef.current = true;
291-
onPostMessage({ message: searchState.ask.trim() });
293+
// Mark this ask value as processed
294+
setState((state) => ({ ...state, initialQuery: trimmedAsk }));
295+
onPostMessage({ message: trimmedAsk });
292296
}
293297
}, [
294298
searchState?.ask,
295299
searchState?.query,
296300
searchState?.open,
297301
messageContextRef,
298302
onOpen,
303+
setState,
299304
onPostMessage,
300305
]);
301306

packages/gitbook/src/components/AIChat/AIChat.tsx

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,15 @@ export function AIChatWindow(props: {
9393
block: 'start',
9494
});
9595

96+
const timeout = setTimeout(() => {
97+
if (lastUserMessageRef.current) {
98+
lastUserMessageRef.current.scrollIntoView({
99+
behavior: 'smooth',
100+
block: 'start',
101+
});
102+
}
103+
}, 100);
104+
96105
// We want the chat messages to scroll underneath the input, but they should scroll past the input when scrolling all the way down.
97106
// The best way to do this is to observe the input height and adjust the padding bottom of the scroll container accordingly.
98107
const observer = new ResizeObserver((entries) => {
@@ -103,7 +112,10 @@ export function AIChatWindow(props: {
103112
if (inputRef.current) {
104113
observer.observe(inputRef.current);
105114
}
106-
return () => observer.disconnect();
115+
return () => {
116+
observer.disconnect();
117+
clearTimeout(timeout);
118+
};
107119
}, []);
108120

109121
return (

packages/gitbook/src/components/AIChat/AIChatInput.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ export function AIChatInput(props: {
3838
// This fixes inconsistent focus behaviour across browsers
3939
const timeout = setTimeout(() => {
4040
inputRef.current?.focus();
41-
}, 50);
41+
}, 150);
4242

4343
return () => clearTimeout(timeout);
4444
}

packages/gitbook/src/components/Search/SearchContainer.tsx

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,12 @@ export function SearchContainer(props: SearchContainerProps) {
5454

5555
const onClose = React.useCallback(
5656
async (to?: string) => {
57-
if (state?.query === '') {
58-
await setSearchState(null);
59-
} else if (state) {
60-
await setSearchState({ ...state, open: false });
57+
if (state) {
58+
await setSearchState({
59+
...state,
60+
open: false,
61+
query: state.query === '' ? null : state.query,
62+
});
6163
}
6264

6365
if (to) {
@@ -131,17 +133,19 @@ export function SearchContainer(props: SearchContainerProps) {
131133
const normalizedQuery = state?.query?.trim() ?? '';
132134
const normalizedAsk = state?.ask?.trim() ?? '';
133135

136+
const showAsk = aiMode === CustomizationAIMode.Search && normalizedAsk;
137+
134138
return (
135139
<SearchAskProvider value={searchAsk}>
136140
<Popover
137141
content={
138142
// Only show content if there's a query or Ask is enabled
139143
(state?.query || aiMode !== CustomizationAIMode.None) && open ? (
140144
<React.Suspense fallback={null}>
141-
{isMultiVariants && !state?.ask ? (
145+
{isMultiVariants && !showAsk ? (
142146
<SearchScopeToggle spaceTitle={spaceTitle} />
143147
) : null}
144-
{state !== null && !state.ask ? (
148+
{state !== null && !showAsk ? (
145149
<SearchResults
146150
ref={resultsRef}
147151
query={normalizedQuery}
@@ -150,7 +154,7 @@ export function SearchContainer(props: SearchContainerProps) {
150154
spaceId={spaceId}
151155
/>
152156
) : null}
153-
{normalizedAsk ? <SearchAskAnswer query={normalizedAsk} /> : null}
157+
{showAsk ? <SearchAskAnswer query={normalizedAsk} /> : null}
154158
</React.Suspense>
155159
) : null
156160
}

packages/gitbook/src/intl/translations/de.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ export const de = {
8888
ai_chat_context_info_provided_by_the_site: 'Von der Website bereitgestellte Informationen',
8989
ai_chat_context_previous_messages: 'Vorherige Nachrichten',
9090
ai_chat_context_disclaimer: 'KI-Antworten können Fehler enthalten.',
91-
ai_chat_input_placeholder: 'Fragen, suchen oder Aktion ausführen...',
91+
ai_chat_input_placeholder: 'Fragen, suchen oder erklären...',
9292
send: 'Senden',
9393
actions: 'Aktionen',
9494
ai_chat_suggested_questions_title: 'Vorgeschlagene Fragen',

packages/gitbook/src/intl/translations/en.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ export const en = {
8585
ai_chat_context_info_provided_by_the_site: 'Info provided by the site',
8686
ai_chat_context_previous_messages: 'Previous messages',
8787
ai_chat_context_disclaimer: 'AI responses may contain mistakes.',
88-
ai_chat_input_placeholder: 'Ask, search, or take action...',
88+
ai_chat_input_placeholder: 'Ask, search, or explain...',
8989
send: 'Send',
9090
actions: 'Actions',
9191
ai_chat_suggested_questions_title: 'Suggested questions',

packages/gitbook/src/intl/translations/es.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ export const es: TranslationLanguage = {
8989
ai_chat_context_info_provided_by_the_site: 'Información proporcionada por el sitio',
9090
ai_chat_context_previous_messages: 'Mensajes anteriores',
9191
ai_chat_context_disclaimer: 'Las respuestas de IA pueden contener errores.',
92-
ai_chat_input_placeholder: 'Pregunta, busca o realiza una acción...',
92+
ai_chat_input_placeholder: 'Pregunta, busca o explica...',
9393
send: 'Enviar',
9494
actions: 'Acciones',
9595
ai_chat_suggested_questions_title: 'Preguntas sugeridas',

packages/gitbook/src/intl/translations/fr.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ export const fr = {
8484
ai_chat_context_info_provided_by_the_site: 'Informations fournies par le site',
8585
ai_chat_context_previous_messages: 'Messages précédents',
8686
ai_chat_context_disclaimer: 'Les réponses générées peuvent contenir des erreurs.',
87-
ai_chat_input_placeholder: 'Rechercher…',
87+
ai_chat_input_placeholder: 'Demander, rechercher ou expliquer...',
8888
send: 'Envoyer',
8989
actions: 'Actions',
9090
ai_chat_suggested_questions_title: 'Suggestions de questions',

packages/gitbook/src/intl/translations/ja.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ export const ja: TranslationLanguage = {
8787
ai_chat_context_info_provided_by_the_site: 'サイトから提供された情報',
8888
ai_chat_context_previous_messages: '以前のメッセージ',
8989
ai_chat_context_disclaimer: 'AIの回答には誤りが含まれる場合があります。',
90-
ai_chat_input_placeholder: '質問、検索、またはアクションを実行...',
90+
ai_chat_input_placeholder: '質問、検索、または説明...',
9191
send: '送信',
9292
actions: 'アクション',
9393
ai_chat_suggested_questions_title: 'おすすめの質問',

packages/gitbook/src/intl/translations/nl.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ export const nl: TranslationLanguage = {
8787
ai_chat_context_info_provided_by_the_site: 'Informatie verstrekt door de site',
8888
ai_chat_context_previous_messages: 'Vorige berichten',
8989
ai_chat_context_disclaimer: 'AI-antwoorden kunnen fouten bevatten.',
90-
ai_chat_input_placeholder: 'Vraag, zoek of voer een actie uit...',
90+
ai_chat_input_placeholder: 'Vraag, zoek of leg uit...',
9191
send: 'Versturen',
9292
actions: 'Acties',
9393
ai_chat_suggested_questions_title: 'Voorgestelde vragen',

0 commit comments

Comments
 (0)