@docusaurus/theme-search-algolia 3.8.1 → 3.9.0-canary-6403

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9,7 +9,62 @@ import {translate} from '@docusaurus/Translate';
9
9
 
10
10
  import type {DocSearchTranslations} from '@docsearch/react';
11
11
 
12
- const translations: DocSearchTranslations & {placeholder: string} = {
12
+ // TODO Docusaurus v4: require DocSearch v4
13
+ // This needs to be cleaned after the upgrade
14
+ // Docusaurus v3 was made compatible with both DocSearch v3 and v4
15
+ // This implies that labels have been kept retro-compatible with v3
16
+ // Once we upgrade, we should be able to rely on v4 types only
17
+ // and remove v3 retro-compatibility labels that do not exist anymore in v4
18
+ const translations: DocSearchTranslations & {
19
+ placeholder: string;
20
+ modal: {
21
+ searchBox: {
22
+ placeholderText: string;
23
+ placeholderTextAskAi: string;
24
+ placeholderTextAskAiStreaming: string;
25
+ enterKeyHintAskAi: string;
26
+ searchInputLabel: string;
27
+ backToKeywordSearchButtonText: string;
28
+ backToKeywordSearchButtonAriaLabel: string;
29
+ enterKeyHint: string;
30
+ clearButtonTitle: string;
31
+ clearButtonAriaLabel: string;
32
+ closeButtonText: string;
33
+ resetButtonTitle: string;
34
+ resetButtonAriaLabel: string;
35
+ cancelButtonText: string;
36
+ cancelButtonAriaLabel: string;
37
+ closeButtonAriaLabel: string;
38
+ };
39
+ startScreen: {
40
+ recentConversationsTitle: string;
41
+ removeRecentConversationButtonTitle: string;
42
+ };
43
+ resultsScreen: {
44
+ askAiPlaceholder: string;
45
+ };
46
+ askAiScreen: {
47
+ disclaimerText: string;
48
+ relatedSourcesText: string;
49
+ thinkingText: string;
50
+ copyButtonText: string;
51
+ copyButtonCopiedText: string;
52
+ copyButtonTitle: string;
53
+ likeButtonTitle: string;
54
+ dislikeButtonTitle: string;
55
+ thanksForFeedbackText: string;
56
+ preToolCallText: string;
57
+ duringToolCallText: string;
58
+ afterToolCallText: string;
59
+ };
60
+ footer: {
61
+ submitQuestionText: string;
62
+ poweredByText: string;
63
+ backToSearchText: string;
64
+ searchByText: string;
65
+ };
66
+ };
67
+ } = {
13
68
  button: {
14
69
  buttonText: translate({
15
70
  id: 'theme.SearchBar.label',
@@ -44,6 +99,69 @@ const translations: DocSearchTranslations & {placeholder: string} = {
44
99
  message: 'Cancel',
45
100
  description: 'The label and ARIA label for search box cancel button',
46
101
  }),
102
+
103
+ // v4
104
+ clearButtonTitle: translate({
105
+ id: 'theme.SearchModal.searchBox.resetButtonTitle',
106
+ message: 'Clear the query',
107
+ description: 'The label and ARIA label for search box reset button',
108
+ }),
109
+ clearButtonAriaLabel: translate({
110
+ id: 'theme.SearchModal.searchBox.resetButtonTitle',
111
+ message: 'Clear the query',
112
+ description: 'The label and ARIA label for search box reset button',
113
+ }),
114
+ closeButtonText: translate({
115
+ id: 'theme.SearchModal.searchBox.cancelButtonText',
116
+ message: 'Cancel',
117
+ description: 'The label and ARIA label for search box cancel button',
118
+ }),
119
+ closeButtonAriaLabel: translate({
120
+ id: 'theme.SearchModal.searchBox.cancelButtonText',
121
+ message: 'Cancel',
122
+ description: 'The label and ARIA label for search box cancel button',
123
+ }),
124
+ placeholderText: translate({
125
+ id: 'theme.SearchModal.searchBox.placeholderText',
126
+ message: 'Search docs',
127
+ description: 'The placeholder text for the main search input field',
128
+ }),
129
+ placeholderTextAskAi: translate({
130
+ id: 'theme.SearchModal.searchBox.placeholderTextAskAi',
131
+ message: 'Ask another question...',
132
+ description: 'The placeholder text when in AI question mode',
133
+ }),
134
+ placeholderTextAskAiStreaming: translate({
135
+ id: 'theme.SearchModal.searchBox.placeholderTextAskAiStreaming',
136
+ message: 'Answering...',
137
+ description:
138
+ 'The placeholder text for search box when AI is streaming an answer',
139
+ }),
140
+ enterKeyHint: translate({
141
+ id: 'theme.SearchModal.searchBox.enterKeyHint',
142
+ message: 'search',
143
+ description: 'The hint for the search box enter key text',
144
+ }),
145
+ enterKeyHintAskAi: translate({
146
+ id: 'theme.SearchModal.searchBox.enterKeyHintAskAi',
147
+ message: 'enter',
148
+ description: 'The hint for the Ask AI search box enter key text',
149
+ }),
150
+ searchInputLabel: translate({
151
+ id: 'theme.SearchModal.searchBox.searchInputLabel',
152
+ message: 'Search',
153
+ description: 'The ARIA label for search input',
154
+ }),
155
+ backToKeywordSearchButtonText: translate({
156
+ id: 'theme.SearchModal.searchBox.backToKeywordSearchButtonText',
157
+ message: 'Back to keyword search',
158
+ description: 'The text for back to keyword search button',
159
+ }),
160
+ backToKeywordSearchButtonAriaLabel: translate({
161
+ id: 'theme.SearchModal.searchBox.backToKeywordSearchButtonAriaLabel',
162
+ message: 'Back to keyword search',
163
+ description: 'The ARIA label for back to keyword search button',
164
+ }),
47
165
  },
48
166
  startScreen: {
49
167
  recentSearchesTitle: translate({
@@ -54,17 +172,17 @@ const translations: DocSearchTranslations & {placeholder: string} = {
54
172
  noRecentSearchesText: translate({
55
173
  id: 'theme.SearchModal.startScreen.noRecentSearchesText',
56
174
  message: 'No recent searches',
57
- description: 'The text when no recent searches',
175
+ description: 'The text when there are no recent searches',
58
176
  }),
59
177
  saveRecentSearchButtonTitle: translate({
60
178
  id: 'theme.SearchModal.startScreen.saveRecentSearchButtonTitle',
61
179
  message: 'Save this search',
62
- description: 'The label for save recent search button',
180
+ description: 'The title for save recent search button',
63
181
  }),
64
182
  removeRecentSearchButtonTitle: translate({
65
183
  id: 'theme.SearchModal.startScreen.removeRecentSearchButtonTitle',
66
184
  message: 'Remove this search from history',
67
- description: 'The label for remove recent search button',
185
+ description: 'The title for remove recent search button',
68
186
  }),
69
187
  favoriteSearchesTitle: translate({
70
188
  id: 'theme.SearchModal.startScreen.favoriteSearchesTitle',
@@ -74,91 +192,178 @@ const translations: DocSearchTranslations & {placeholder: string} = {
74
192
  removeFavoriteSearchButtonTitle: translate({
75
193
  id: 'theme.SearchModal.startScreen.removeFavoriteSearchButtonTitle',
76
194
  message: 'Remove this search from favorites',
77
- description: 'The label for remove favorite search button',
195
+ description: 'The title for remove favorite search button',
196
+ }),
197
+ recentConversationsTitle: translate({
198
+ id: 'theme.SearchModal.startScreen.recentConversationsTitle',
199
+ message: 'Recent conversations',
200
+ description: 'The title for recent conversations',
201
+ }),
202
+ removeRecentConversationButtonTitle: translate({
203
+ id: 'theme.SearchModal.startScreen.removeRecentConversationButtonTitle',
204
+ message: 'Remove this conversation from history',
205
+ description: 'The title for remove recent conversation button',
78
206
  }),
79
207
  },
80
208
  errorScreen: {
81
209
  titleText: translate({
82
210
  id: 'theme.SearchModal.errorScreen.titleText',
83
211
  message: 'Unable to fetch results',
84
- description: 'The title for error screen of search modal',
212
+ description: 'The title for error screen',
85
213
  }),
86
214
  helpText: translate({
87
215
  id: 'theme.SearchModal.errorScreen.helpText',
88
216
  message: 'You might want to check your network connection.',
89
- description: 'The help text for error screen of search modal',
217
+ description: 'The help text for error screen',
218
+ }),
219
+ },
220
+ resultsScreen: {
221
+ askAiPlaceholder: translate({
222
+ id: 'theme.SearchModal.resultsScreen.askAiPlaceholder',
223
+ message: 'Ask AI: ',
224
+ description: 'The placeholder text for Ask AI input',
225
+ }),
226
+ },
227
+ askAiScreen: {
228
+ disclaimerText: translate({
229
+ id: 'theme.SearchModal.askAiScreen.disclaimerText',
230
+ message:
231
+ 'Answers are generated with AI which can make mistakes. Verify responses.',
232
+ description: 'The disclaimer text for AI answers',
233
+ }),
234
+ relatedSourcesText: translate({
235
+ id: 'theme.SearchModal.askAiScreen.relatedSourcesText',
236
+ message: 'Related sources',
237
+ description: 'The text for related sources',
238
+ }),
239
+ thinkingText: translate({
240
+ id: 'theme.SearchModal.askAiScreen.thinkingText',
241
+ message: 'Thinking...',
242
+ description: 'The text when AI is thinking',
243
+ }),
244
+ copyButtonText: translate({
245
+ id: 'theme.SearchModal.askAiScreen.copyButtonText',
246
+ message: 'Copy',
247
+ description: 'The text for copy button',
248
+ }),
249
+ copyButtonCopiedText: translate({
250
+ id: 'theme.SearchModal.askAiScreen.copyButtonCopiedText',
251
+ message: 'Copied!',
252
+ description: 'The text for copy button when copied',
253
+ }),
254
+ copyButtonTitle: translate({
255
+ id: 'theme.SearchModal.askAiScreen.copyButtonTitle',
256
+ message: 'Copy',
257
+ description: 'The title for copy button',
258
+ }),
259
+ likeButtonTitle: translate({
260
+ id: 'theme.SearchModal.askAiScreen.likeButtonTitle',
261
+ message: 'Like',
262
+ description: 'The title for like button',
263
+ }),
264
+ dislikeButtonTitle: translate({
265
+ id: 'theme.SearchModal.askAiScreen.dislikeButtonTitle',
266
+ message: 'Dislike',
267
+ description: 'The title for dislike button',
268
+ }),
269
+ thanksForFeedbackText: translate({
270
+ id: 'theme.SearchModal.askAiScreen.thanksForFeedbackText',
271
+ message: 'Thanks for your feedback!',
272
+ description: 'The text for thanks for feedback',
273
+ }),
274
+ preToolCallText: translate({
275
+ id: 'theme.SearchModal.askAiScreen.preToolCallText',
276
+ message: 'Searching...',
277
+ description: 'The text before tool call',
278
+ }),
279
+ duringToolCallText: translate({
280
+ id: 'theme.SearchModal.askAiScreen.duringToolCallText',
281
+ message: 'Searching for ',
282
+ description: 'The text during tool call',
283
+ }),
284
+ afterToolCallText: translate({
285
+ id: 'theme.SearchModal.askAiScreen.afterToolCallText',
286
+ message: 'Searched for',
287
+ description: 'The text after tool call',
90
288
  }),
91
289
  },
92
290
  footer: {
93
291
  selectText: translate({
94
292
  id: 'theme.SearchModal.footer.selectText',
95
- message: 'to select',
96
- description: 'The explanatory text of the action for the enter key',
293
+ message: 'Select',
294
+ description: 'The select text for footer',
295
+ }),
296
+ submitQuestionText: translate({
297
+ id: 'theme.SearchModal.footer.submitQuestionText',
298
+ message: 'Submit question',
299
+ description: 'The submit question text for footer',
97
300
  }),
98
301
  selectKeyAriaLabel: translate({
99
302
  id: 'theme.SearchModal.footer.selectKeyAriaLabel',
100
303
  message: 'Enter key',
101
- description:
102
- 'The ARIA label for the Enter key button that makes the selection',
304
+ description: 'The ARIA label for select key in footer',
103
305
  }),
104
306
  navigateText: translate({
105
307
  id: 'theme.SearchModal.footer.navigateText',
106
- message: 'to navigate',
107
- description:
108
- 'The explanatory text of the action for the Arrow up and Arrow down key',
308
+ message: 'Navigate',
309
+ description: 'The navigate text for footer',
109
310
  }),
110
311
  navigateUpKeyAriaLabel: translate({
111
312
  id: 'theme.SearchModal.footer.navigateUpKeyAriaLabel',
112
313
  message: 'Arrow up',
113
- description:
114
- 'The ARIA label for the Arrow up key button that makes the navigation',
314
+ description: 'The ARIA label for navigate up key in footer',
115
315
  }),
116
316
  navigateDownKeyAriaLabel: translate({
117
317
  id: 'theme.SearchModal.footer.navigateDownKeyAriaLabel',
118
318
  message: 'Arrow down',
119
- description:
120
- 'The ARIA label for the Arrow down key button that makes the navigation',
319
+ description: 'The ARIA label for navigate down key in footer',
121
320
  }),
122
321
  closeText: translate({
123
322
  id: 'theme.SearchModal.footer.closeText',
124
- message: 'to close',
125
- description: 'The explanatory text of the action for Escape key',
323
+ message: 'Close',
324
+ description: 'The close text for footer',
126
325
  }),
127
326
  closeKeyAriaLabel: translate({
128
327
  id: 'theme.SearchModal.footer.closeKeyAriaLabel',
129
328
  message: 'Escape key',
130
- description:
131
- 'The ARIA label for the Escape key button that close the modal',
329
+ description: 'The ARIA label for close key in footer',
330
+ }),
331
+ poweredByText: translate({
332
+ id: 'theme.SearchModal.footer.searchByText',
333
+ message: 'Powered by',
334
+ description: "The 'Powered by' text for footer",
132
335
  }),
133
336
  searchByText: translate({
134
337
  id: 'theme.SearchModal.footer.searchByText',
135
- message: 'Search by',
136
- description: 'The text explain that the search is making by Algolia',
338
+ message: 'Powered by',
339
+ description: "The 'Powered by' text for footer",
340
+ }),
341
+ backToSearchText: translate({
342
+ id: 'theme.SearchModal.footer.backToSearchText',
343
+ message: 'Back to search',
344
+ description: 'The back to search text for footer',
137
345
  }),
138
346
  },
139
347
  noResultsScreen: {
140
348
  noResultsText: translate({
141
349
  id: 'theme.SearchModal.noResultsScreen.noResultsText',
142
- message: 'No results for',
143
- description:
144
- 'The text explains that there are no results for the following search',
350
+ message: 'No results found for',
351
+ description: 'The text when there are no results',
145
352
  }),
146
353
  suggestedQueryText: translate({
147
354
  id: 'theme.SearchModal.noResultsScreen.suggestedQueryText',
148
355
  message: 'Try searching for',
149
- description:
150
- 'The text for the suggested query when no results are found for the following search',
356
+ description: 'The text for suggested query',
151
357
  }),
152
358
  reportMissingResultsText: translate({
153
359
  id: 'theme.SearchModal.noResultsScreen.reportMissingResultsText',
154
360
  message: 'Believe this query should return results?',
155
- description:
156
- 'The text for the question where the user thinks there are missing results',
361
+ description: 'The text for reporting missing results',
157
362
  }),
158
363
  reportMissingResultsLinkText: translate({
159
364
  id: 'theme.SearchModal.noResultsScreen.reportMissingResultsLinkText',
160
365
  message: 'Let us know.',
161
- description: 'The text for the link to report missing results',
366
+ description: 'The link text for reporting missing results',
162
367
  }),
163
368
  },
164
369
  },
@@ -5,9 +5,27 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
 
8
+ // TODO Docusaurus v4: remove after we drop support for DocSearch v3
9
+ declare module '@docsearch/react/button';
10
+ declare module '@docsearch/react/useDocSearchKeyboardEvents';
11
+ declare module '@docsearch/react/version';
12
+
8
13
  declare module '@docusaurus/theme-search-algolia' {
9
- import type {DeepPartial} from 'utility-types';
14
+ import type {DeepPartial, Overwrite} from 'utility-types';
15
+
10
16
  import type {DocSearchProps} from '@docsearch/react';
17
+ import type {FacetFilters} from 'algoliasearch/lite';
18
+
19
+ // The config after normalization (e.g. AskAI string -> object)
20
+ export type AskAiConfig = {
21
+ indexName: string;
22
+ apiKey: string;
23
+ appId: string;
24
+ assistantId: string;
25
+ searchParameters?: {
26
+ facetFilters?: FacetFilters;
27
+ };
28
+ };
11
29
 
12
30
  // DocSearch props that Docusaurus exposes directly through props forwarding
13
31
  type DocusaurusDocSearchProps = Pick<
@@ -20,9 +38,15 @@ declare module '@docusaurus/theme-search-algolia' {
20
38
  | 'searchParameters'
21
39
  | 'insights'
22
40
  | 'initialQuery'
23
- >;
41
+ > & {
42
+ // Docusaurus normalizes the AskAI config to an object
43
+ askAi?: AskAiConfig;
44
+ };
45
+
46
+ export type ThemeConfigAlgolia = DocusaurusDocSearchProps & {
47
+ // TODO Docusaurus v4: upgrade to DocSearch v4, migrate indexName to indices
48
+ indexName: string;
24
49
 
25
- type ThemeConfigAlgolia = DocusaurusDocSearchProps & {
26
50
  // Docusaurus custom options, not coming from DocSearch
27
51
  contextualSearch: boolean;
28
52
  externalUrlRegex?: string;
@@ -33,21 +57,23 @@ declare module '@docusaurus/theme-search-algolia' {
33
57
  };
34
58
  };
35
59
 
36
- export type ThemeConfig = DocusaurusDocSearchProps & {
60
+ export type ThemeConfig = {
37
61
  algolia: ThemeConfigAlgolia;
38
62
  };
39
63
 
40
- export type UserThemeConfig = DeepPartial<ThemeConfig>;
41
- }
42
-
43
- declare module '@docusaurus/theme-search-algolia/client' {
44
- import type {ThemeConfig} from '@docusaurus/theme-search-algolia';
45
-
46
- export function useAlgoliaThemeConfig(): ThemeConfig;
47
-
48
- export function useAlgoliaContextualFacetFilters(): [string, string[]];
49
-
50
- export function useSearchResultUrlProcessor(): (url: string) => string;
64
+ export type UserThemeConfig = {
65
+ algolia?: Overwrite<
66
+ DeepPartial<ThemeConfigAlgolia>,
67
+ {
68
+ // Required fields:
69
+ appId: ThemeConfigAlgolia['appId'];
70
+ apiKey: ThemeConfigAlgolia['apiKey'];
71
+ indexName: ThemeConfigAlgolia['indexName'];
72
+ // askAi also accepts a shorter string form
73
+ askAi?: string | AskAiConfig;
74
+ }
75
+ >;
76
+ };
51
77
  }
52
78
 
53
79
  declare module '@theme/SearchPage' {
@@ -65,6 +91,15 @@ declare module '@theme/SearchBar' {
65
91
  declare module '@theme/SearchTranslations' {
66
92
  import type {DocSearchTranslations} from '@docsearch/react';
67
93
 
68
- const translations: DocSearchTranslations & {placeholder: string};
94
+ const translations: DocSearchTranslations & {
95
+ placeholder: string;
96
+ // TODO Docusaurus v4: cleanup after we drop support for DocSearch v3
97
+ modal?: {
98
+ searchBox?: {
99
+ placeholderText?: string;
100
+ placeholderTextAskAi?: string;
101
+ };
102
+ };
103
+ };
69
104
  export default translations;
70
105
  }
@@ -7,22 +7,27 @@
7
7
 
8
8
  import {escapeRegexp} from '@docusaurus/utils';
9
9
  import {Joi} from '@docusaurus/utils-validation';
10
+ import {docSearchV3} from './docSearchVersion';
11
+ import type {ThemeConfigValidationContext} from '@docusaurus/types';
10
12
  import type {
11
13
  ThemeConfig,
12
- ThemeConfigValidationContext,
13
- } from '@docusaurus/types';
14
+ ThemeConfigAlgolia,
15
+ } from '@docusaurus/theme-search-algolia';
14
16
 
15
17
  export const DEFAULT_CONFIG = {
16
18
  // Enabled by default, as it makes sense in most cases
17
19
  // see also https://github.com/facebook/docusaurus/issues/5880
18
20
  contextualSearch: true,
19
-
20
21
  searchParameters: {},
21
22
  searchPagePath: 'search',
22
- };
23
+ } satisfies Partial<ThemeConfigAlgolia>;
24
+
25
+ const FacetFiltersSchema = Joi.array().items(
26
+ Joi.alternatives().try(Joi.string(), Joi.array().items(Joi.string())),
27
+ );
23
28
 
24
29
  export const Schema = Joi.object<ThemeConfig>({
25
- algolia: Joi.object({
30
+ algolia: Joi.object<ThemeConfigAlgolia>({
26
31
  // Docusaurus attributes
27
32
  contextualSearch: Joi.boolean().default(DEFAULT_CONFIG.contextualSearch),
28
33
  externalUrlRegex: Joi.string().optional(),
@@ -33,7 +38,9 @@ export const Schema = Joi.object<ThemeConfig>({
33
38
  }),
34
39
  apiKey: Joi.string().required(),
35
40
  indexName: Joi.string().required(),
36
- searchParameters: Joi.object()
41
+ searchParameters: Joi.object({
42
+ facetFilters: FacetFiltersSchema.optional(),
43
+ })
37
44
  .default(DEFAULT_CONFIG.searchParameters)
38
45
  .unknown(),
39
46
  searchPagePath: Joi.alternatives()
@@ -53,15 +60,86 @@ export const Schema = Joi.object<ThemeConfig>({
53
60
  }).required(),
54
61
  to: Joi.string().required(),
55
62
  }).optional(),
63
+ // Ask AI configuration (DocSearch v4 only)
64
+ askAi: Joi.alternatives()
65
+ .try(
66
+ // Simple string format (assistantId only)
67
+ Joi.string(),
68
+ // Full configuration object
69
+ Joi.object({
70
+ indexName: Joi.string().required(),
71
+ apiKey: Joi.string().required(),
72
+ appId: Joi.string().required(),
73
+ assistantId: Joi.string().required(),
74
+ searchParameters: Joi.object({
75
+ facetFilters: FacetFiltersSchema.optional(),
76
+ }).optional(),
77
+ }),
78
+ )
79
+ .custom(
80
+ (
81
+ askAiInput: string | ThemeConfigAlgolia['askAi'] | undefined,
82
+ helpers,
83
+ ) => {
84
+ if (!askAiInput) {
85
+ return askAiInput;
86
+ }
87
+ const algolia: ThemeConfigAlgolia = helpers.state.ancestors[0];
88
+ const algoliaFacetFilters = algolia.searchParameters?.facetFilters;
89
+ if (typeof askAiInput === 'string') {
90
+ return {
91
+ assistantId: askAiInput,
92
+ indexName: algolia.indexName,
93
+ apiKey: algolia.apiKey,
94
+ appId: algolia.appId,
95
+ ...(algoliaFacetFilters
96
+ ? {
97
+ searchParameters: {
98
+ facetFilters: algoliaFacetFilters,
99
+ },
100
+ }
101
+ : {}),
102
+ } satisfies ThemeConfigAlgolia['askAi'];
103
+ }
104
+
105
+ if (
106
+ askAiInput.searchParameters?.facetFilters === undefined &&
107
+ algoliaFacetFilters
108
+ ) {
109
+ askAiInput.searchParameters = askAiInput.searchParameters ?? {};
110
+ askAiInput.searchParameters.facetFilters = algoliaFacetFilters;
111
+ }
112
+ return askAiInput;
113
+ },
114
+ )
115
+ .optional()
116
+ .messages({
117
+ 'alternatives.types':
118
+ 'askAi must be either a string (assistantId) or an object with indexName, apiKey, appId, and assistantId',
119
+ }),
56
120
  })
57
121
  .label('themeConfig.algolia')
58
122
  .required()
59
- .unknown(), // DocSearch 3 is still alpha: don't validate the rest for now
123
+ .unknown(),
60
124
  });
61
125
 
126
+ // TODO Docusaurus v4: remove this check when we drop DocSearch v3
127
+ function ensureAskAISupported(themeConfig: ThemeConfig) {
128
+ // enforce DocsSearch v4 requirement when AskAI is configured
129
+ if (themeConfig.algolia.askAi && docSearchV3) {
130
+ throw new Error(
131
+ 'The askAi feature is only supported in DocSearch v4. ' +
132
+ 'Please upgrade to DocSearch v4 by installing "@docsearch/react": "^4.0.0" ' +
133
+ 'or remove the askAi configuration from your theme config.',
134
+ );
135
+ }
136
+ }
137
+
62
138
  export function validateThemeConfig({
63
139
  validate,
64
- themeConfig,
140
+ themeConfig: themeConfigInput,
65
141
  }: ThemeConfigValidationContext<ThemeConfig>): ThemeConfig {
66
- return validate(Schema, themeConfig);
142
+ const themeConfig = validate(Schema, themeConfigInput);
143
+ ensureAskAISupported(themeConfig);
144
+ return themeConfig;
67
145
  }