--- outline: deep --- # 検索 {#search} ## ローカル検索 {#local-search} VitePress は、[minisearch](https://github.com/lucaong/minisearch/) によるブラウザ内インデックスを使った曖昧一致の全文検索をサポートします。有効化するには、`.vitepress/config.ts` で `themeConfig.search.provider` を `'local'` に設定します。 ```ts import { defineConfig } from 'vitepress' export default defineConfig({ themeConfig: { search: { provider: 'local' } } }) ``` 表示例: ![screenshot of the search modal](/search.png) 代わりに [Algolia DocSearch](#algolia-search) や、次のコミュニティ製プラグインを使うこともできます。 - - - ### i18n {#local-search-i18n} 多言語検索を行う設定例です。 ```ts import { defineConfig } from 'vitepress' export default defineConfig({ themeConfig: { search: { provider: 'local', options: { locales: { zh: { // 既定ロケールの文言も翻訳したい場合はこれを `root` に translations: { button: { buttonText: '搜索', buttonAriaLabel: '搜索' }, modal: { displayDetails: '显示详细列表', resetButtonTitle: '重置搜索', backButtonTitle: '关闭搜索', noResultsText: '没有结果', footer: { selectText: '选择', selectKeyAriaLabel: '输入', navigateText: '导航', navigateUpKeyAriaLabel: '上箭头', navigateDownKeyAriaLabel: '下箭头', closeText: '关闭', closeKeyAriaLabel: 'esc' } } } } } } } } }) ``` ### miniSearch のオプション {#mini-search-options} MiniSearch の設定例です。 ```ts import { defineConfig } from 'vitepress' export default defineConfig({ themeConfig: { search: { provider: 'local', options: { miniSearch: { /** * @type {Pick} */ options: { /* ... */ }, /** * @type {import('minisearch').SearchOptions} * @default * { fuzzy: 0.2, prefix: true, boost: { title: 4, text: 2, titles: 1 } } */ searchOptions: { /* ... */ } } } } } }) ``` 詳しくは [MiniSearch のドキュメント](https://lucaong.github.io/minisearch/classes/MiniSearch.MiniSearch.html) を参照してください。 ### コンテンツレンダラーのカスタマイズ {#custom-content-renderer} インデックス前に Markdown コンテンツをレンダリングする関数をカスタマイズできます。 ```ts import { defineConfig } from 'vitepress' export default defineConfig({ themeConfig: { search: { provider: 'local', options: { /** * @param {string} src * @param {import('vitepress').MarkdownEnv} env * @param {import('markdown-it-async')} md */ async _render(src, env, md) { // HTML 文字列を返す } } } } }) ``` この関数はクライアント側のサイトデータからは除外されるため、Node.js の API を使用できます。 #### 例: 検索対象からページを除外する {#example-excluding-pages-from-search} フロントマターに `search: false` を追加すると、そのページを検索対象から除外できます。あるいは次のようにもできます。 ```ts import { defineConfig } from 'vitepress' export default defineConfig({ themeConfig: { search: { provider: 'local', options: { async _render(src, env, md) { const html = await md.renderAsync(src, env) if (env.frontmatter?.search === false) return '' if (env.relativePath.startsWith('some/path')) return '' return html } } } } }) ``` ::: warning 注意 カスタムの `_render` 関数を提供する場合、`search: false` の処理は自分で行う必要があります。また、`env` は `md.renderAsync` の呼び出し前には完全ではないため、`frontmatter` などの任意プロパティのチェックはその後に行ってください。 ::: #### 例: コンテンツの変換 — 見出しアンカーを追加 {#example-transforming-content-adding-anchors} ```ts import { defineConfig } from 'vitepress' export default defineConfig({ themeConfig: { search: { provider: 'local', options: { async _render(src, env, md) { const html = await md.renderAsync(src, env) if (env.frontmatter?.title) return await md.renderAsync(`# ${env.frontmatter.title}`) + html return html } } } } }) ``` ## Algolia 検索 {#algolia-search} VitePress は [Algolia DocSearch](https://docsearch.algolia.com/docs/what-is-docsearch) によるサイト検索をサポートします。導入は公式のガイドを参照してください。`.vitepress/config.ts` では最低限次の設定が必要です。 ```ts import { defineConfig } from 'vitepress' export default defineConfig({ themeConfig: { search: { provider: 'algolia', options: { appId: '...', apiKey: '...', indexName: '...' } } } }) ``` ### i18n {#algolia-search-i18n} 多言語検索の設定例です。 ```ts import { defineConfig } from 'vitepress' export default defineConfig({ themeConfig: { search: { provider: 'algolia', options: { appId: '...', apiKey: '...', indexName: '...', locales: { zh: { placeholder: '搜索文档', translations: { button: { buttonText: '搜索文档', buttonAriaLabel: '搜索文档' }, modal: { searchBox: { clearButtonTitle: '清除查询条件', clearButtonAriaLabel: '清除查询条件', closeButtonText: '关闭', closeButtonAriaLabel: '关闭', placeholderText: '搜索文档', placeholderTextAskAi: '向 AI 提问:', placeholderTextAskAiStreaming: '回答中...', searchInputLabel: '搜索', backToKeywordSearchButtonText: '返回关键字搜索', backToKeywordSearchButtonAriaLabel: '返回关键字搜索' }, startScreen: { recentSearchesTitle: '搜索历史', noRecentSearchesText: '没有搜索历史', saveRecentSearchButtonTitle: '保存至搜索历史', removeRecentSearchButtonTitle: '从搜索历史中移除', favoriteSearchesTitle: '收藏', removeFavoriteSearchButtonTitle: '从收藏中移除', recentConversationsTitle: '最近的对话', removeRecentConversationButtonTitle: '从历史记录中删除对话' }, errorScreen: { titleText: '无法获取结果', helpText: '你可能需要检查你的网络连接' }, noResultsScreen: { noResultsText: '无法找到相关结果', suggestedQueryText: '你可以尝试查询', reportMissingResultsText: '你认为该查询应该有结果?', reportMissingResultsLinkText: '点击反馈' }, resultsScreen: { askAiPlaceholder: '向 AI 提问: ' }, askAiScreen: { disclaimerText: '答案由 AI 生成,可能不准确,请自行验证。', relatedSourcesText: '相关来源', thinkingText: '思考中...', copyButtonText: '复制', copyButtonCopiedText: '已复制!', copyButtonTitle: '复制', likeButtonTitle: '赞', dislikeButtonTitle: '踩', thanksForFeedbackText: '感谢你的反馈!', preToolCallText: '搜索中...', duringToolCallText: '搜索 ', afterToolCallText: '已搜索' }, footer: { selectText: '选择', submitQuestionText: '提交问题', selectKeyAriaLabel: 'Enter 键', navigateText: '切换', navigateUpKeyAriaLabel: '向上箭头', navigateDownKeyAriaLabel: '向下箭头', closeText: '关闭', backToSearchText: '返回搜索', closeKeyAriaLabel: 'Esc 键', poweredByText: '搜索提供者' } } } } } } } } }) ``` [これらのオプション](https://github.com/vuejs/vitepress/blob/main/types/docsearch.d.ts) は上書きできます。詳細は Algolia の公式ドキュメントを参照してください。 ### Algolia Ask AI のサポート {#ask-ai} **Ask AI** を有効にするには、`options` 内に `askAi` オプション(またはその一部)を指定します。 ```ts import { defineConfig } from 'vitepress' export default defineConfig({ themeConfig: { search: { provider: 'algolia', options: { appId: '...', apiKey: '...', indexName: '...', // askAi: "YOUR-ASSISTANT-ID" // または askAi: { // 少なくとも Algolia から受け取った assistantId を指定 assistantId: 'XXXYYY', // 任意の上書き — 省略時は上位の appId/apiKey/indexName を再利用 // apiKey: '...', // appId: '...', // indexName: '...' } } } } }) ``` ::: warning 注意 キーワード検索を既定にして Ask AI を使わない場合は、`askAi` を指定しないでください。 ::: Ask AI UI の翻訳は `options.translations.modal.askAiScreen` と `options.translations.resultsScreen` にあります。すべてのキーは[型定義](https://github.com/vuejs/vitepress/blob/main/types/docsearch.d.ts)を参照してください。 ### クローラー設定 {#crawler-config} このサイトで使用している設定を元にした例です。 ```ts new Crawler({ appId: '...', apiKey: '...', rateLimit: 8, startUrls: ['https://vitepress.dev/'], renderJavaScript: false, sitemaps: [], exclusionPatterns: [], ignoreCanonicalTo: false, discoveryPatterns: ['https://vitepress.dev/**'], schedule: 'at 05:10 on Saturday', actions: [ { indexName: 'vitepress', pathsToMatch: ['https://vitepress.dev/**'], recordExtractor: ({ $, helpers }) => { return helpers.docsearch({ recordProps: { lvl1: '.content h1', content: '.content p, .content li', lvl0: { selectors: 'section.has-active div h2', defaultValue: 'Documentation' }, lvl2: '.content h2', lvl3: '.content h3', lvl4: '.content h4', lvl5: '.content h5' }, indexHeadings: true }) } } ], initialIndexSettings: { vitepress: { attributesForFaceting: ['type', 'lang'], attributesToRetrieve: ['hierarchy', 'content', 'anchor', 'url'], attributesToHighlight: ['hierarchy', 'hierarchy_camel', 'content'], attributesToSnippet: ['content:10'], camelCaseAttributes: ['hierarchy', 'hierarchy_radio', 'content'], searchableAttributes: [ 'unordered(hierarchy_radio_camel.lvl0)', 'unordered(hierarchy_radio.lvl0)', 'unordered(hierarchy_radio_camel.lvl1)', 'unordered(hierarchy_radio.lvl1)', 'unordered(hierarchy_radio_camel.lvl2)', 'unordered(hierarchy_radio.lvl2)', 'unordered(hierarchy_radio_camel.lvl3)', 'unordered(hierarchy_radio.lvl3)', 'unordered(hierarchy_radio_camel.lvl4)', 'unordered(hierarchy_radio.lvl4)', 'unordered(hierarchy_radio_camel.lvl5)', 'unordered(hierarchy_radio.lvl5)', 'unordered(hierarchy_radio_camel.lvl6)', 'unordered(hierarchy_radio.lvl6)', 'unordered(hierarchy_camel.lvl0)', 'unordered(hierarchy.lvl0)', 'unordered(hierarchy_camel.lvl1)', 'unordered(hierarchy.lvl1)', 'unordered(hierarchy_camel.lvl2)', 'unordered(hierarchy.lvl2)', 'unordered(hierarchy_camel.lvl3)', 'unordered(hierarchy.lvl3)', 'unordered(hierarchy_camel.lvl4)', 'unordered(hierarchy.lvl4)', 'unordered(hierarchy_camel.lvl5)', 'unordered(hierarchy.lvl5)', 'unordered(hierarchy_camel.lvl6)', 'unordered(hierarchy.lvl6)', 'content' ], distinct: true, attributeForDistinct: 'url', customRanking: [ 'desc(weight.pageRank)', 'desc(weight.level)', 'asc(weight.position)' ], ranking: [ 'words', 'filters', 'typo', 'attribute', 'proximity', 'exact', 'custom' ], highlightPreTag: '', highlightPostTag: '', minWordSizefor1Typo: 3, minWordSizefor2Typos: 7, allowTyposOnNumericTokens: false, minProximity: 1, ignorePlurals: true, advancedSyntax: true, attributeCriteriaComputedByMinProximity: true, removeWordsIfNoResults: 'allOptional' } } }) ```