diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e7df96c..b3c1b636 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,52 @@ +## [2.0.0-alpha.17](https://github.com/vuejs/vitepress/compare/v2.0.0-alpha.16...v2.0.0-alpha.17) (2026-03-19) + +### Bug Fixes + +- **build:** `processIncludes` no longer swallows errors ([fb21fdf](https://github.com/vuejs/vitepress/commit/fb21fdf6759b7c88ac98456820b75286936fbf87)) +- **build:** deterministic local search indexing and non-blocking initial scan (closes [#4081](https://github.com/vuejs/vitepress/issues/4081)) ([fc0f203](https://github.com/vuejs/vitepress/commit/fc0f203f0ac451ec039850b920f0684c1695d161)) +- **client:** await `clipboard.writeText` so that fallback works in non-secure contexts ([#5119](https://github.com/vuejs/vitepress/issues/5119)) ([b544df8](https://github.com/vuejs/vitepress/commit/b544df802d256aef10d4da94631967b0fcab68d2)) +- disable brotli compression in preview server ([ca1e875](https://github.com/vuejs/vitepress/commit/ca1e87512924acdd8d39ca0f530a424ad933b701)) +- **theme:** add `rel="alternate"` and `hreflang` to links in language menu ([#5108](https://github.com/vuejs/vitepress/issues/5108)) ([f09593b](https://github.com/vuejs/vitepress/commit/f09593b073e42fa9d8de1e744a9f2a0e5c8170fc)) +- **theme:** add margin to images in vp-doc (closes [#5136](https://github.com/vuejs/vitepress/issues/5136)) ([896e49a](https://github.com/vuejs/vitepress/commit/896e49a777bb983de2cbef82c1c295db2dba3dbe)) +- **theme:** close sidebar on resize (closes [#5145](https://github.com/vuejs/vitepress/issues/5145)) ([17696c3](https://github.com/vuejs/vitepress/commit/17696c358b88335c4f6bb516bbcdf774aaf7deee)) +- **theme:** disable text autospace in `
` (closes [#5115](https://github.com/vuejs/vitepress/issues/5115)) ([21a5fb6](https://github.com/vuejs/vitepress/commit/21a5fb6a96549564fcf698d0e7256181eac3919b))
+- **theme:** enable `line-break: strict` for `
  • ` too ([f811d58](https://github.com/vuejs/vitepress/commit/f811d581a164fa330b17cdb85270effa39bdb3fd)) +- **theme:** keep pnum,tnum features in Inter ([f1bef95](https://github.com/vuejs/vitepress/commit/f1bef95eee453bb18cc160741a11513678015cd6)), closes [#5096](https://github.com/vuejs/vitepress/issues/5096) +- **theme:** navbar menu group is incorrectly highlighted ([#5113](https://github.com/vuejs/vitepress/issues/5113)) ([b28667b](https://github.com/vuejs/vitepress/commit/b28667b6b03d50c4be0d509ec5e7d780f29f8042)) +- **theme:** strip system-ui from font-family-base ([#4988](https://github.com/vuejs/vitepress/issues/4988)) ([8096eac](https://github.com/vuejs/vitepress/commit/8096eaca4f1d9eef00101e42f7159da1e12ca3de)) + +### Features + +- **theme:** add `home-hero-actions-before-actions` slot ([#5151](https://github.com/vuejs/vitepress/issues/5151)) ([74a73d8](https://github.com/vuejs/vitepress/commit/74a73d869ca61f2c64f9dcbd9912a0461e8d4bfe)) +- support text-fragments ([#5140](https://github.com/vuejs/vitepress/issues/5140)) ([44e2675](https://github.com/vuejs/vitepress/commit/44e2675889588b54dea771a0c82e2db64739364c)) +- **theme:** add Japanese translation for "copied" label ([#5106](https://github.com/vuejs/vitepress/issues/5106)) ([a01bba0](https://github.com/vuejs/vitepress/commit/a01bba0cad70bb089918adab105f0a5b5ff85918)) +- **theme:** enable text-autospace and text-spacing-trim (closes [#4996](https://github.com/vuejs/vitepress/issues/4996)) ([d7effff](https://github.com/vuejs/vitepress/commit/d7effffdbb9ea6bdeda31e26dea0f1ed32ddd51e)) +- **theme:** support specifying list of details in home page features (closes [#5101](https://github.com/vuejs/vitepress/issues/5101)) ([3146ce4](https://github.com/vuejs/vitepress/commit/3146ce48ed3ad684ddfbf1194de3a2200082eb92)) + +### BREAKING CHANGES + +Previously, the `` syntax silently ignored errors when files did not exist. This behavior was originally intended as an escape hatch while documenting includes, but better solutions now exist using Shiki transformers. + +For most users, no code changes are required. If you now see errors, it means your includes are broken and were earlier not being reported. + +Users who intentionally reference non-existent files or want to document includes without resolving them can configure `markdown.codeTransformers` with a `postprocess` hook. See `docs/.vitepress/config.ts` in this repo for an example. + +## [2.0.0-alpha.16](https://github.com/vuejs/vitepress/compare/v2.0.0-alpha.15...v2.0.0-alpha.16) (2026-01-31) + +### Bug Fixes + +- always log error when failed to fetch page ([66cf64e](https://github.com/vuejs/vitepress/commit/66cf64e6d127dd8473e582d11e1133acda6c3bc8)) +- **theme:** add fallback for `heroImageSlotExists` ([#5076](https://github.com/vuejs/vitepress/issues/5076)) ([f119b18](https://github.com/vuejs/vitepress/commit/f119b18e39b545f39e29358913fe9ed1fd69bc55)) +- **theme:** align badges in h1 and h2 ([#5087](https://github.com/vuejs/vitepress/issues/5087)) (closes [#5063](https://github.com/vuejs/vitepress/issues/5063)) ([b200865](https://github.com/vuejs/vitepress/commit/b2008654ffaf0d8f8c425e88592e9ed2f8791506)) +- **theme:** highlight active link in mobile nav menu ([#5086](https://github.com/vuejs/vitepress/issues/5086)) (closes [#5068](https://github.com/vuejs/vitepress/issues/5068), closes [#5074](https://github.com/vuejs/vitepress/issues/5074)) ([923aa90](https://github.com/vuejs/vitepress/commit/923aa902523739bfb9d77aed376ebc73c32eeb33)) +- **theme:** overflow clip is buggy on safari ([8ed6ea0](https://github.com/vuejs/vitepress/commit/8ed6ea048cb49256e3302de2de0edfbe635afd32)), closes [#5050](https://github.com/vuejs/vitepress/issues/5050) [#5039](https://github.com/vuejs/vitepress/issues/5039) [#5027](https://github.com/vuejs/vitepress/issues/5027) +- **theme:** remove margin between code groups and blocks in markdown containers ([a28e171](https://github.com/vuejs/vitepress/commit/a28e171604605713a221d0eb2bbdce211930d94f)), closes [#5099](https://github.com/vuejs/vitepress/issues/5099) + +### Features + +- **theme:** upgrade DocSearch to 4.5 with sidepanel ([#5092](https://github.com/vuejs/vitepress/issues/5092)) ([0d646a6](https://github.com/vuejs/vitepress/commit/0d646a66cd44e97adef516a6a36e03365f179906)) +- **theme:** use `@layer __vitepress_base` to wrap the styles in base.css ([#4905](https://github.com/vuejs/vitepress/issues/4905)) ([f8d8c0d](https://github.com/vuejs/vitepress/commit/f8d8c0d712fba4728c750e1f44c5ba9596979ba1)) + ## [2.0.0-alpha.15](https://github.com/vuejs/vitepress/compare/v2.0.0-alpha.14...v2.0.0-alpha.15) (2025-11-22) ### Bug Fixes diff --git a/README.md b/README.md index c6533b30..02700308 100644 --- a/README.md +++ b/README.md @@ -26,3 +26,14 @@ Please make sure to read the [Contributing Guide](https://github.com/vuejs/vitep [MIT](https://github.com/vuejs/vitepress/blob/main/LICENSE) Copyright (c) 2019-present, Yuxi (Evan) You + +## Special Thanks + +This project would not be possible without the support of these amazing companies and tools. + +| | | | +| ------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| VoidZero's logo | **[VoidZero](https://voidzero.dev)** | The company behind Vite, Vitest, Rolldown, and Oxc. VoidZero has been a long-time sponsor of VitePress. VitePress itself is built on top of this ecosystem of high-performance JavaScript tooling. | +| StackBlitz's logo | **[StackBlitz](https://stackblitz.com)** | Creators of WebContainers and a browser-based development platform. Their technology powers the VitePress playground and provides instant preview releases for our pull requests and commits. | +| Algolia's logo | **[Algolia](https://algolia.com)** | An AI-powered search platform that provides fast, scalable search. Algolia powers DocSearch for VitePress. Their team has been a long-time partner in building and maintaining our search integration. | +| BrowserStack's logo | **[BrowserStack](https://browserstack.com)** | A cloud-based testing platform for websites and mobile apps. BrowserStack provides the cross-browser and real-device infrastructure used to ensure VitePress works consistently across all browsers. | diff --git a/__tests__/e2e/.vitepress/theme/components/CustomLayout.vue b/__tests__/e2e/.vitepress/theme/components/CustomLayout.vue new file mode 100644 index 00000000..4a42af44 --- /dev/null +++ b/__tests__/e2e/.vitepress/theme/components/CustomLayout.vue @@ -0,0 +1,14 @@ + + + diff --git a/__tests__/e2e/.vitepress/theme/components/HomeHeroCopy.vue b/__tests__/e2e/.vitepress/theme/components/HomeHeroCopy.vue new file mode 100644 index 00000000..ecc3ba92 --- /dev/null +++ b/__tests__/e2e/.vitepress/theme/components/HomeHeroCopy.vue @@ -0,0 +1,210 @@ + + + + + diff --git a/__tests__/e2e/.vitepress/theme/index.ts b/__tests__/e2e/.vitepress/theme/index.ts index d2629777..9b5c079c 100644 --- a/__tests__/e2e/.vitepress/theme/index.ts +++ b/__tests__/e2e/.vitepress/theme/index.ts @@ -1,10 +1,12 @@ import type { Theme } from 'vitepress' import DefaultTheme from 'vitepress/theme' +import CustomLayout from './components/CustomLayout.vue' import ApiPreference from './components/ApiPreference.vue' import NavVersion from './components/NavVersion.vue' export default { extends: DefaultTheme, + Layout: CustomLayout, enhanceApp({ app }) { app.component('ApiPreference', ApiPreference) app.component('NavVersion', NavVersion) diff --git a/__tests__/e2e/index.md b/__tests__/e2e/index.md index f35cb331..2805de36 100644 --- a/__tests__/e2e/index.md +++ b/__tests__/e2e/index.md @@ -6,6 +6,10 @@ title: VitePress hero: name: VitePress text: Vite & Vue Powered Static Site Generator + image: + src: /vitepress-logo.svg + alt: VitePress + actions: - theme: brand text: Examples diff --git a/__tests__/e2e/local-search/local-search.test.ts b/__tests__/e2e/local-search/local-search.test.ts index 492136b6..9496dc6b 100644 --- a/__tests__/e2e/local-search/local-search.test.ts +++ b/__tests__/e2e/local-search/local-search.test.ts @@ -4,7 +4,7 @@ describe('local search', () => { }) test('exclude content from search results', async () => { - await page.locator('#local-search button').click() + await page.locator('.VPNavBarSearchButton').click() const input = await page.waitForSelector('input#localsearch-input') await input.type('local') diff --git a/__tests__/e2e/public/vitepress-logo.svg b/__tests__/e2e/public/vitepress-logo.svg new file mode 120000 index 00000000..e2b80355 --- /dev/null +++ b/__tests__/e2e/public/vitepress-logo.svg @@ -0,0 +1 @@ +../../../art/vitepress-logo.svg \ No newline at end of file diff --git a/__tests__/tsconfig.json b/__tests__/tsconfig.json index 42258a4e..b8fef70a 100644 --- a/__tests__/tsconfig.json +++ b/__tests__/tsconfig.json @@ -5,8 +5,9 @@ "baseUrl": ".", "types": ["node", "vitest/globals"], "paths": { + "client/*": ["../src/client/*"], "node/*": ["../src/node/*"], - "client/*": ["../src/client/*"] + "shared/*": ["../src/shared/*"] } } } diff --git a/__tests__/unit/client/theme-default/support/docsearch.test.ts b/__tests__/unit/client/theme-default/support/docsearch.test.ts new file mode 100644 index 00000000..4da8f113 --- /dev/null +++ b/__tests__/unit/client/theme-default/support/docsearch.test.ts @@ -0,0 +1,196 @@ +import { + buildAskAiConfig, + hasAskAi, + hasKeywordSearch, + mergeLangFacetFilters, + validateCredentials +} from 'client/theme-default/support/docsearch' + +describe('client/theme-default/support/docsearch', () => { + describe('mergeLangFacetFilters', () => { + test('adds a lang facet filter when none is provided', () => { + expect(mergeLangFacetFilters(undefined, 'en')).toEqual(['lang:en']) + }) + + test('replaces existing lang facet filters', () => { + expect(mergeLangFacetFilters('lang:fr', 'en')).toEqual(['lang:en']) + expect(mergeLangFacetFilters(['foo', 'lang:fr'], 'en')).toEqual([ + 'foo', + 'lang:en' + ]) + }) + + test('handles nested facet filters (OR conditions)', () => { + expect( + mergeLangFacetFilters([['tag:foo', 'tag:bar'], 'lang:fr'], 'en') + ).toEqual([['tag:foo', 'tag:bar'], 'lang:en']) + }) + + test('removes empty nested arrays', () => { + expect(mergeLangFacetFilters([['lang:fr'], 'other'], 'en')).toEqual([ + 'other', + 'lang:en' + ]) + }) + + test('handles multiple lang filters in nested arrays', () => { + expect( + mergeLangFacetFilters([['lang:fr', 'tag:foo'], 'bar'], 'en') + ).toEqual([['tag:foo'], 'bar', 'lang:en']) + }) + }) + + describe('hasKeywordSearch', () => { + test('returns true when all credentials are provided', () => { + expect( + hasKeywordSearch({ + appId: 'app', + apiKey: 'key', + indexName: 'index' + }) + ).toBe(true) + }) + + test('returns false when any credential is missing', () => { + expect( + hasKeywordSearch({ + appId: undefined, + apiKey: 'key', + indexName: 'index' + }) + ).toBe(false) + expect( + hasKeywordSearch({ + appId: 'app', + apiKey: undefined, + indexName: 'index' + }) + ).toBe(false) + expect( + hasKeywordSearch({ + appId: 'app', + apiKey: 'key', + indexName: undefined + }) + ).toBe(false) + }) + }) + + describe('hasAskAi', () => { + test('returns true for valid string assistantId', () => { + expect(hasAskAi('assistant123')).toBe(true) + }) + + test('returns false for empty string assistantId', () => { + expect(hasAskAi('')).toBe(false) + }) + + test('returns true for object with assistantId', () => { + expect(hasAskAi({ assistantId: 'assistant123' } as any)).toBe(true) + }) + + test('returns false for object without assistantId', () => { + expect(hasAskAi({ assistantId: null } as any)).toBe(false) + expect(hasAskAi({} as any)).toBe(false) + }) + + test('returns false for undefined', () => { + expect(hasAskAi(undefined)).toBe(false) + }) + }) + + describe('validateCredentials', () => { + test('validates complete credentials', () => { + const result = validateCredentials({ + appId: 'app', + apiKey: 'key', + indexName: 'index' + }) + expect(result.valid).toBe(true) + expect(result.appId).toBe('app') + expect(result.apiKey).toBe('key') + expect(result.indexName).toBe('index') + }) + + test('invalidates incomplete credentials', () => { + expect( + validateCredentials({ + appId: undefined, + apiKey: 'key', + indexName: 'index' + }).valid + ).toBe(false) + }) + }) + + describe('buildAskAiConfig', () => { + test('builds config from string assistantId', () => { + const result = buildAskAiConfig( + 'assistant123', + { + appId: 'app', + apiKey: 'key', + indexName: 'index' + } as any, + 'en' + ) + expect(result.assistantId).toBe('assistant123') + expect(result.appId).toBe('app') + expect(result.apiKey).toBe('key') + expect(result.indexName).toBe('index') + }) + + test('builds config from object with overrides', () => { + const result = buildAskAiConfig( + { + assistantId: 'assistant123', + appId: 'custom-app', + apiKey: 'custom-key', + indexName: 'custom-index' + } as any, + { + appId: 'default-app', + apiKey: 'default-key', + indexName: 'default-index' + } as any, + 'en' + ) + expect(result.assistantId).toBe('assistant123') + expect(result.appId).toBe('custom-app') + expect(result.apiKey).toBe('custom-key') + expect(result.indexName).toBe('custom-index') + }) + + test('merges facet filters with lang', () => { + const result = buildAskAiConfig( + { + assistantId: 'assistant123', + searchParameters: { + facetFilters: ['tag:docs'] + } + } as any, + { + appId: 'app', + apiKey: 'key', + indexName: 'index' + } as any, + 'en' + ) + expect(result.searchParameters?.facetFilters).toContain('tag:docs') + expect(result.searchParameters?.facetFilters).toContain('lang:en') + }) + + test('always adds lang facet filter to searchParameters', () => { + const result = buildAskAiConfig( + 'assistant123', + { + appId: 'app', + apiKey: 'key', + indexName: 'index' + } as any, + 'en' + ) + expect(result.searchParameters?.facetFilters).toEqual(['lang:en']) + }) + }) +}) diff --git a/__tests__/unit/client/theme-default/support/utils.test.ts b/__tests__/unit/client/theme-default/support/utils.test.ts index 880d4232..aefc9b8e 100644 --- a/__tests__/unit/client/theme-default/support/utils.test.ts +++ b/__tests__/unit/client/theme-default/support/utils.test.ts @@ -1,4 +1,7 @@ -import { ensureStartingSlash } from 'client/theme-default/support/utils' +import { + ensureStartingSlash, + isLinkExternal +} from 'client/theme-default/support/utils' describe('client/theme-default/utils', () => { describe('ensureStartingSlash', () => { @@ -9,4 +12,22 @@ describe('client/theme-default/utils', () => { expect(ensureStartingSlash('/path/nested')).toBe('/path/nested') }) }) + + describe('isLinkExternal', () => { + test('it detects external links by default', () => { + expect(isLinkExternal('https://vite.dev')).toBe(true) + expect(isLinkExternal('/guide/')).toBe(false) + }) + + test('it treats _blank targets as external by default', () => { + expect(isLinkExternal('/guide/', '_blank')).toBe(true) + }) + + test('it allows callers to override external detection', () => { + expect(isLinkExternal('https://cn.vite.dev', undefined, false)).toBe( + false + ) + expect(isLinkExternal('/guide/', undefined, true)).toBe(true) + }) + }) }) diff --git a/__tests__/unit/node/markdown/plugins/highlight.test.ts b/__tests__/unit/node/markdown/plugins/highlight.test.ts new file mode 100644 index 00000000..7454dcc1 --- /dev/null +++ b/__tests__/unit/node/markdown/plugins/highlight.test.ts @@ -0,0 +1,26 @@ +import { highlight } from 'node/markdown/plugins/highlight' + +describe('node/markdown/plugins/highlight', () => { + test('passes color replacements through markdown options', async () => { + const [render, dispose] = await highlight( + { light: 'github-light', dark: 'github-dark' }, + { + colorReplacements: { + 'github-light': { + '#005cc5': '#000000' + } + } + } + ) + + try { + const html = await render('const a = 1', 'js', '') + + expect(html).toContain('--shiki-light:#000000') + expect(html).toContain('--shiki-dark:#79B8FF') + expect(html).not.toContain('--shiki-light:#005CC5') + } finally { + dispose() + } + }) +}) diff --git a/__tests__/unit/node/markdown/plugins/link.test.ts b/__tests__/unit/node/markdown/plugins/link.test.ts new file mode 100644 index 00000000..e5e09978 --- /dev/null +++ b/__tests__/unit/node/markdown/plugins/link.test.ts @@ -0,0 +1,51 @@ +import { slugify } from '@mdit-vue/shared' +import { MarkdownItAsync } from 'markdown-it-async' +import { linkPlugin } from 'node/markdown/plugins/link' + +describe('node/markdown/plugins/link', () => { + const md = new MarkdownItAsync() + linkPlugin(md, {}, '/', slugify) + + test('preserves text-fragment hashes on markdown links', async () => { + const html = await md.renderAsync( + '[58-61](/resources/server/user#:~:text=58*,time%20authentication%20token)', + { cleanUrls: false } + ) + + expect(html).toContain( + 'href="/resources/server/user.html#:~:text=58*,time%20authentication%20token"' + ) + }) + + // https://web.dev/articles/text-fragments#mixing_element_and_text_fragments + test('preserves mixed element and text-fragment hashes', async () => { + const html = await md.renderAsync( + '[Section](/guide/getting-started#Hello%20World:~:text=Hello%20World)', + { cleanUrls: false } + ) + + expect(html).toContain( + 'href="/guide/getting-started.html#hello-world:~:text=Hello%20World"' + ) + }) + + test('continues to normalize regular heading hashes', async () => { + const html = await md.renderAsync( + '[Section](/guide/getting-started#Hello%20World)', + { cleanUrls: false } + ) + + expect(html).toContain('href="/guide/getting-started.html#hello-world"') + }) + + test('does not break encoding for text-fragments', async () => { + const html = await md.renderAsync( + '[Section](/foo?title=Cat&oldid=916388819#:~:text=Claws-,Like%20almost,the%20Felidae%2C,-cats)', + { cleanUrls: false } + ) + + expect(html).toContain( + 'href="/foo.html?title=Cat&oldid=916388819#:~:text=Claws-,Like%20almost,the%20Felidae%2C,-cats"' + ) + }) +}) diff --git a/__tests__/unit/node/markdown/plugins/snippet.test.ts b/__tests__/unit/node/markdown/plugins/snippet.test.ts index aa940784..ba4b343e 100644 --- a/__tests__/unit/node/markdown/plugins/snippet.test.ts +++ b/__tests__/unit/node/markdown/plugins/snippet.test.ts @@ -3,7 +3,6 @@ import { findRegion, rawPathToToken } from 'node/markdown/plugins/snippet' -import { expect } from 'vitest' const removeEmptyKeys = >(obj: T) => { return Object.fromEntries( diff --git a/__tests__/unit/vitest.config.ts b/__tests__/unit/vitest.config.ts index b028d4ee..93eb40e1 100644 --- a/__tests__/unit/vitest.config.ts +++ b/__tests__/unit/vitest.config.ts @@ -12,6 +12,7 @@ export default defineConfig({ { find: '@siteData', replacement: resolve(dir, './shims.ts') }, { find: 'client', replacement: resolve(dir, '../../src/client') }, { find: 'node', replacement: resolve(dir, '../../src/node') }, + { find: 'shared', replacement: resolve(dir, '../../src/shared') }, { find: /^vitepress$/, replacement: resolve(dir, '../../src/client/index.js') diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index 7be361c8..b8093713 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -11,6 +11,20 @@ import { import llmstxt from 'vitepress-plugin-llms' const prod = !!process.env.NETLIFY +const siteUrl = 'https://vitepress.dev' + +const ogImage = new URL('/vitepress-og.jpg', siteUrl).href + +const localeToOgLocaleMap: Record = { + root: 'en_US', + zh: 'zh_CN', + pt: 'pt_BR', + ru: 'ru_RU', + es: 'es_ES', + ko: 'ko_KR', + fa: 'fa_IR', + ja: 'ja_JP' +} export default defineConfig({ title: 'VitePress', @@ -26,10 +40,13 @@ export default defineConfig({ markdown: { math: true, codeTransformers: [ - // We use `[!!code` in demo to prevent transformation, here we revert it back. + // We use `[!!code` and `@@include` in demo to prevent transformation, + // here we revert it back. { postprocess(code) { - return code.replace(/\[\!\!code/g, '[!code') + return code + .replaceAll('[!!code', '[!code') + .replaceAll('@@include', '@include') } } ], @@ -68,41 +85,18 @@ export default defineConfig({ }, sitemap: { - hostname: 'https://vitepress.dev', + hostname: siteUrl, transformItems(items) { return items.filter((item) => !item.url.includes('migration')) } }, + // prettier-ignore head: [ - [ - 'link', - { rel: 'icon', type: 'image/svg+xml', href: '/vitepress-logo-mini.svg' } - ], - [ - 'link', - { rel: 'icon', type: 'image/png', href: '/vitepress-logo-mini.png' } - ], + ['link', { rel: 'icon', type: 'image/svg+xml', href: '/vitepress-logo-mini.svg' }], + ['link', { rel: 'icon', type: 'image/png', href: '/vitepress-logo-mini.png' }], ['meta', { name: 'theme-color', content: '#5f67ee' }], - ['meta', { property: 'og:type', content: 'website' }], - ['meta', { property: 'og:site_name', content: 'VitePress' }], - [ - 'meta', - { - property: 'og:image', - content: 'https://vitepress.dev/vitepress-og.jpg' - } - ], - ['meta', { property: 'og:url', content: 'https://vitepress.dev/' }], - [ - 'script', - { - src: 'https://cdn.usefathom.com/script.js', - 'data-site': 'AZBRSFGG', - 'data-spa': 'auto', - defer: '' - } - ] + ['script', { src: 'https://cdn.usefathom.com/script.js', 'data-site': 'AZBRSFGG', 'data-spa': 'auto', defer: '' }] ], themeConfig: { @@ -118,7 +112,10 @@ export default defineConfig({ appId: '8J64VVRP8K', apiKey: '52f578a92b88ad6abde815aae2b0ad7c', indexName: 'vitepress', - askAi: 'YaVSonfX5bS8' + askAi: { + assistantId: 'YaVSonfX5bS8', + sidePanel: true + } } }, @@ -147,28 +144,35 @@ export default defineConfig({ firebase: 'logos:firebase' } }), - prod && - llmstxt({ - workDir: 'en', - ignoreFiles: ['index.md'] - }) + prod && llmstxt({ workDir: 'en', ignoreFiles: ['index.md'] }) ], experimental: { enableNativePlugin: true } }, - transformPageData: prod - ? (pageData, ctx) => { - const site = resolveSiteDataByRoute( - ctx.siteConfig.site, - pageData.relativePath - ) - const title = `${pageData.title || site.title} | ${pageData.description || site.description}` - ;((pageData.frontmatter.head ??= []) as HeadConfig[]).push( - ['meta', { property: 'og:locale', content: site.lang }], - ['meta', { property: 'og:title', content: title }] - ) - } - : undefined + // prettier-ignore + transformPageData: prod ? (pageData, ctx) => { + const url = new URL(pageData.relativePath.replace(/(?:(^|\/)index)?\.md$/, '$1'), siteUrl).href + const site = resolveSiteDataByRoute(ctx.siteConfig.site, pageData.relativePath) + const title = pageData.title ? `${pageData.title} | VitePress` : site.title + const description = pageData.description || site.description + const locale = localeToOgLocaleMap[site.localeIndex || 'root'] + + ;((pageData.frontmatter.head ??= []) as HeadConfig[]).push( + ['meta', { property: 'og:url', content: url }], + ['meta', { property: 'og:title', content: title }], + ['meta', { property: 'og:description', content: description }], + ['meta', { property: 'og:type', content: 'website' }], + ['meta', { property: 'og:locale', content: locale }], + ['meta', { property: 'og:site_name', content: 'VitePress' }], + ['meta', { property: 'og:image', content: ogImage }], + ['meta', { property: 'og:image:secure_url', content: ogImage }], + ['meta', { property: 'og:image:type', content: 'image/jpeg' }], + ['meta', { property: 'og:image:width', content: '1280' }], + ['meta', { property: 'og:image:height', content: '640' }], + ['meta', { property: 'og:image:alt', content: 'VitePress' }], + ['link', { rel: 'canonical', href: url }] + ) + } : undefined }) diff --git a/docs/en/guide/asset-handling.md b/docs/en/guide/asset-handling.md index eeedc8c0..00819d1a 100644 --- a/docs/en/guide/asset-handling.md +++ b/docs/en/guide/asset-handling.md @@ -1,3 +1,7 @@ +--- +description: Learn how to reference and handle static assets such as images, media, and fonts in VitePress. +--- + # Asset Handling ## Referencing Static Assets diff --git a/docs/en/guide/cms.md b/docs/en/guide/cms.md index 8694cf0a..0bff6746 100644 --- a/docs/en/guide/cms.md +++ b/docs/en/guide/cms.md @@ -1,5 +1,6 @@ --- outline: deep +description: Connect VitePress to a headless CMS using dynamic routes and data loaders. --- # Connecting to a CMS diff --git a/docs/en/guide/custom-theme.md b/docs/en/guide/custom-theme.md index 96943c9f..c0ce8597 100644 --- a/docs/en/guide/custom-theme.md +++ b/docs/en/guide/custom-theme.md @@ -1,3 +1,7 @@ +--- +description: Create and use a custom theme in VitePress to fully control the look and feel of your site. +--- + # Using a Custom Theme ## Theme Resolving diff --git a/docs/en/guide/data-loading.md b/docs/en/guide/data-loading.md index 10ba8b57..1c11e469 100644 --- a/docs/en/guide/data-loading.md +++ b/docs/en/guide/data-loading.md @@ -1,3 +1,7 @@ +--- +description: Load arbitrary data at build time using VitePress data loaders and import it from pages or components. +--- + # Build-Time Data Loading VitePress provides a feature called **data loaders** that allows you to load arbitrary data and import it from pages or components. The data loading is executed **only at build time**: the resulting data will be serialized as JSON in the final JavaScript bundle. diff --git a/docs/en/guide/deploy.md b/docs/en/guide/deploy.md index 48e8c9ea..9c01d0ee 100644 --- a/docs/en/guide/deploy.md +++ b/docs/en/guide/deploy.md @@ -1,5 +1,6 @@ --- outline: deep +description: Deploy your VitePress site to popular platforms like Netlify, Vercel, GitHub Pages, and more. --- # Deploy Your VitePress Site @@ -105,7 +106,7 @@ Note: the `vercel.json` file should be placed at the root of your **repository** ## Platform Guides -### Netlify / Vercel / Cloudflare Pages / AWS Amplify / Render +### Netlify / Vercel / Cloudflare Pages / AWS Amplify / Render {#generic} Set up a new project and change these settings using your dashboard: @@ -205,7 +206,7 @@ Don't enable options like _Auto Minify_ for HTML code. It will remove comments f 2. Create a file named `.gitlab-ci.yml` in the root of your project with the content below. This will build and deploy your site whenever you make changes to your content: ```yaml [.gitlab-ci.yml] - image: node:18 + image: node:24 pages: cache: paths: @@ -221,7 +222,9 @@ Don't enable options like _Auto Minify_ for HTML code. It will remove comments f - main ``` -### Azure Static Web Apps + + +### Azure 1. Follow the [official documentation](https://docs.microsoft.com/en-us/azure/static-web-apps/build-configuration). @@ -231,6 +234,10 @@ Don't enable options like _Auto Minify_ for HTML code. It will remove comments f - **`output_location`**: `docs/.vitepress/dist` - **`app_build_command`**: `npm run docs:build` +### CloudRay + +You can deploy your VitePress project with [CloudRay](https://cloudray.io/) by following these [instructions](https://cloudray.io/articles/how-to-deploy-vitepress-site). + ### Firebase 1. Create `firebase.json` and `.firebaserc` at the root of your project: @@ -262,14 +269,6 @@ Don't enable options like _Auto Minify_ for HTML code. It will remove comments f firebase deploy ``` -### Surge - -1. After running `npm run docs:build`, run this command to deploy: - - ```sh - npx surge docs/.vitepress/dist - ``` - ### Heroku 1. Follow documentation and guide given in [`heroku-buildpack-static`](https://elements.heroku.com/buildpacks/heroku/heroku-buildpack-static). @@ -282,11 +281,11 @@ Don't enable options like _Auto Minify_ for HTML code. It will remove comments f } ``` -### Edgio +### Hostinger -Refer [Creating and Deploying a VitePress App To Edgio](https://docs.edg.io/guides/vitepress). +You can deploy your VitePress project with [Hostinger](https://www.hostinger.com/web-apps-hosting) by following these [instructions](https://www.hostinger.com/support/how-to-deploy-a-nodejs-website-in-hostinger/). While configuring build settings, choose VitePress as the framework and adjust the root directory to `./docs`. -### Kinsta Static Site Hosting +### Kinsta You can deploy your VitePress website on [Kinsta](https://kinsta.com/static-site-hosting/) by following these [instructions](https://kinsta.com/docs/vitepress-static-site-example/). @@ -294,9 +293,13 @@ You can deploy your VitePress website on [Kinsta](https://kinsta.com/static-site You can deploy your VitePress project to [Stormkit](https://www.stormkit.io) by following these [instructions](https://stormkit.io/blog/how-to-deploy-vitepress). -### CloudRay +### Surge -You can deploy your VitePress project with [CloudRay](https://cloudray.io/) by following these [instructions](https://cloudray.io/articles/how-to-deploy-vitepress-site). +1. After running `npm run docs:build`, run this command to deploy: + + ```sh + npx surge docs/.vitepress/dist + ``` ### Nginx diff --git a/docs/en/guide/extending-default-theme.md b/docs/en/guide/extending-default-theme.md index 4d18d8c5..5ff15f5b 100644 --- a/docs/en/guide/extending-default-theme.md +++ b/docs/en/guide/extending-default-theme.md @@ -1,5 +1,6 @@ --- outline: deep +description: Customize and extend the VitePress default theme with custom CSS, components, layouts, and slots. --- # Extending the Default Theme @@ -190,6 +191,7 @@ Full list of slots available in the default theme layout: - `home-hero-info-before` - `home-hero-info` - `home-hero-info-after` + - `home-hero-actions-before-actions` - `home-hero-actions-after` - `home-hero-image` - `home-hero-after` diff --git a/docs/en/guide/frontmatter.md b/docs/en/guide/frontmatter.md index c1856ca4..dc205816 100644 --- a/docs/en/guide/frontmatter.md +++ b/docs/en/guide/frontmatter.md @@ -1,3 +1,7 @@ +--- +description: Learn how to use YAML frontmatter in VitePress Markdown files to control page-level metadata and behavior. +--- + # Frontmatter ## Usage diff --git a/docs/en/guide/getting-started.md b/docs/en/guide/getting-started.md index 79cb3180..e1dc739a 100644 --- a/docs/en/guide/getting-started.md +++ b/docs/en/guide/getting-started.md @@ -1,3 +1,7 @@ +--- +description: Get up and running with VitePress. Learn how to install, scaffold, and start developing your documentation site. +--- + # Getting Started ## Try It Online @@ -8,7 +12,7 @@ You can try VitePress directly in your browser on [StackBlitz](https://vitepress ### Prerequisites -- [Node.js](https://nodejs.org/) version 18 or higher. +- [Node.js](https://nodejs.org/) version 20 or higher. - Terminal for accessing VitePress via its command line interface (CLI). - Text Editor with [Markdown](https://en.wikipedia.org/wiki/Markdown) syntax support. - [VSCode](https://code.visualstudio.com/) is recommended, along with the [official Vue extension](https://marketplace.visualstudio.com/items?itemName=Vue.volar). diff --git a/docs/en/guide/i18n.md b/docs/en/guide/i18n.md index f53acef6..8a57bb78 100644 --- a/docs/en/guide/i18n.md +++ b/docs/en/guide/i18n.md @@ -1,3 +1,7 @@ +--- +description: Set up internationalization (i18n) in VitePress to support multiple languages for your site. +--- + # Internationalization To use the built-in i18n features, one needs to create a directory structure as follows: diff --git a/docs/en/guide/markdown.md b/docs/en/guide/markdown.md index a1acfe3d..505bbb00 100644 --- a/docs/en/guide/markdown.md +++ b/docs/en/guide/markdown.md @@ -1,3 +1,7 @@ +--- +description: VitePress built-in Markdown extensions including custom containers, code blocks with syntax highlighting, line highlighting, code groups, and more. +--- + # Markdown Extensions VitePress comes with built in Markdown Extensions. @@ -811,7 +815,7 @@ For example, you can include a relative markdown file using this: ## Basics - + ``` **Part file** (`parts/basics.md`) @@ -847,7 +851,7 @@ It also supports selecting a line range: ## Basics - + ``` **Part file** (`parts/basics.md`) @@ -883,8 +887,8 @@ You can also use a [VS Code region](https://code.visualstudio.com/docs/editor/co ## Basics - - + + ``` **Part file** (`parts/basics.md`) @@ -935,7 +939,7 @@ You can include the `My Base Section` section like this: ```md ## My Extended Section - + ``` **Equivalent code** @@ -959,7 +963,7 @@ Here, `my-base-section` is the generated id of the heading element. In case it's and include it like this: ```md - + ``` ## Math Equations diff --git a/docs/en/guide/mpa-mode.md b/docs/en/guide/mpa-mode.md index 74b6c297..35f7e4d0 100644 --- a/docs/en/guide/mpa-mode.md +++ b/docs/en/guide/mpa-mode.md @@ -1,3 +1,7 @@ +--- +description: Enable MPA (Multi-Page Application) mode in VitePress for zero-JavaScript pages with better initial performance. +--- + # MPA Mode MPA (Multi-Page Application) mode can be enabled via the command line via `vitepress build --mpa`, or via config through the `mpa: true` option. diff --git a/docs/en/guide/routing.md b/docs/en/guide/routing.md index bacdbf6b..2631c632 100644 --- a/docs/en/guide/routing.md +++ b/docs/en/guide/routing.md @@ -1,5 +1,6 @@ --- outline: deep +description: Understand VitePress file-based routing, dynamic routes, clean URLs, and path rewrites. --- # Routing @@ -121,7 +122,7 @@ Alternatively, you can directly use the anchor tag syntax: ::: -## Generating Clean URL +## Generating Clean URLs ::: warning Server Support Required To serve clean URLs with VitePress, server-side support is required. @@ -260,6 +261,30 @@ The generated HTML pages will be: └─ bar.html ``` +### Type-safe loader with `defineRoutes` + +If you are using TypeScript, you can wrap the loader with `defineRoutes` from `vitepress` to get type hints for route hooks such as `paths`, `watch`, and `transformPageData`: + +```ts +// packages/[pkg].paths.ts +import { defineRoutes } from 'vitepress' + +export default defineRoutes({ + watch: ['../data/**/*.json'], + async paths() { + return [ + { params: { pkg: 'foo' } }, + { params: { pkg: 'bar' } } + ] + }, + async transformPageData(pageData) { + pageData.title = `${pageData.title} · Packages` + } +}) +``` + +`defineRoutes` is optional, but recommended when authoring `.paths.ts` files. + ### Multiple Params A dynamic route can contain multiple params: diff --git a/docs/en/guide/sitemap-generation.md b/docs/en/guide/sitemap-generation.md index 4d52984f..86751bdd 100644 --- a/docs/en/guide/sitemap-generation.md +++ b/docs/en/guide/sitemap-generation.md @@ -1,3 +1,7 @@ +--- +description: Generate a sitemap.xml file for your VitePress site to improve search engine discoverability. +--- + # Sitemap Generation VitePress comes with out-of-the-box support for generating a `sitemap.xml` file for your site. To enable it, add the following to your `.vitepress/config.js`: diff --git a/docs/en/guide/ssr-compat.md b/docs/en/guide/ssr-compat.md index c1b1ee07..99171ad6 100644 --- a/docs/en/guide/ssr-compat.md +++ b/docs/en/guide/ssr-compat.md @@ -1,5 +1,6 @@ --- outline: deep +description: Ensure your VitePress theme components and custom code are compatible with server-side rendering. --- # SSR Compatibility diff --git a/docs/en/guide/using-vue.md b/docs/en/guide/using-vue.md index 8420ea7f..a9b9a389 100644 --- a/docs/en/guide/using-vue.md +++ b/docs/en/guide/using-vue.md @@ -1,3 +1,7 @@ +--- +description: Use Vue components and dynamic templating features directly inside Markdown files in VitePress. +--- + # Using Vue in Markdown In VitePress, each Markdown file is compiled into HTML and then processed as a [Vue Single-File Component](https://vuejs.org/guide/scaling-up/sfc.html). This means you can use any Vue features inside the Markdown, including dynamic templating, using Vue components, or arbitrary in-page Vue component logic by adding a ` +```md + - # Hello - ``` +# Hello +``` ` - ``` +console.log(params.value) + +``` ### 生コンテンツのレンダリング {#rendering-raw-content} @@ -363,23 +364,23 @@ VitePress は静的サイトジェネレーターなので、生成可能なペ 代わりに、各パスオブジェクトの `content` プロパティでコンテンツを渡せます: - ```js - export default { - async paths() { - const posts = await (await fetch('https://my-cms.com/blog-posts')).json() - - return posts.map((post) => { - return { - params: { id: post.id }, - content: post.content // 生の Markdown または HTML - } - }) - } - } - ``` +```js +export default { + async paths() { + const posts = await (await fetch('https://my-cms.com/blog-posts')).json() + + return posts.map((post) => { + return { + params: { id: post.id }, + content: post.content // 生の Markdown または HTML + } + }) + } +} +``` そのうえで、Markdown ファイル内で次の特別な構文を使って、そのコンテンツを埋め込みます: - ```md - - ``` +```md + +``` diff --git a/docs/ja/guide/sitemap-generation.md b/docs/ja/guide/sitemap-generation.md index db44942c..d74084ca 100644 --- a/docs/ja/guide/sitemap-generation.md +++ b/docs/ja/guide/sitemap-generation.md @@ -1,14 +1,18 @@ +--- +description: VitePressサイトのsitemap.xmlを生成し、検索エンジンからの発見性を向上させる方法。 +--- + # サイトマップ生成 {#sitemap-generation} VitePress には、サイト用の `sitemap.xml` を生成する機能が標準で用意されています。有効化するには、`.vitepress/config.js` に次を追加します。 - ```ts - export default { - sitemap: { - hostname: 'https://example.com' - } - } - ``` +```ts +export default { + sitemap: { + hostname: 'https://example.com' + } +} +``` `siteamp.xml` に `` タグを含めるには、[`lastUpdated`](../reference/default-theme-last-updated) オプションを有効にします。 @@ -16,43 +20,43 @@ VitePress には、サイト用の `sitemap.xml` を生成する機能が標準 サイトマップ生成は [`sitemap`](https://www.npmjs.com/package/sitemap) モジュールで行われます。設定ファイルの `sitemap` に、このモジュールがサポートする任意のオプションを渡せます。指定した値はそのまま `SitemapStream` コンストラクタに渡されます。詳しくは [`sitemap` のドキュメント](https://www.npmjs.com/package/sitemap#options-you-can-pass) を参照してください。例: - ```ts - export default { - sitemap: { - hostname: 'https://example.com', - lastmodDateOnly: false - } - } - ``` +```ts +export default { + sitemap: { + hostname: 'https://example.com', + lastmodDateOnly: false + } +} +``` 設定で `base` を使っている場合は、`hostname` にもそれを付与してください: - ```ts - export default { - base: '/my-site/', - sitemap: { - hostname: 'https://example.com/my-site/' - } - } - ``` +```ts +export default { + base: '/my-site/', + sitemap: { + hostname: 'https://example.com/my-site/' + } +} +``` ## `transformItems` フック {#transformitems-hook} `siteamp.xml` に書き出す直前にサイトマップ項目を加工するには、`sitemap.transformItems` フックを使います。このフックはサイトマップ項目の配列を受け取り、配列を返す必要があります。例: - ```ts - export default { - sitemap: { - hostname: 'https://example.com', - transformItems: (items) => { - // 既存項目の追加・変更・フィルタリングが可能 - items.push({ - url: '/extra-page', - changefreq: 'monthly', - priority: 0.8 - }) - return items - } - } - } - ``` +```ts +export default { + sitemap: { + hostname: 'https://example.com', + transformItems: (items) => { + // 既存項目の追加・変更・フィルタリングが可能 + items.push({ + url: '/extra-page', + changefreq: 'monthly', + priority: 0.8 + }) + return items + } + } +} +``` diff --git a/docs/ja/guide/ssr-compat.md b/docs/ja/guide/ssr-compat.md index d756d6d1..ec94a4a4 100644 --- a/docs/ja/guide/ssr-compat.md +++ b/docs/ja/guide/ssr-compat.md @@ -1,5 +1,6 @@ --- outline: deep +description: VitePressのテーマコンポーネントやカスタムコードをサーバーサイドレンダリング(SSR)に対応させる方法。 --- # SSR 互換性 {#ssr-compatibility} @@ -12,11 +13,11 @@ VitePress は本番ビルド時に、Node.js 上で Vue のサーバーサイド SSR に適さないコンポーネント(例:カスタムディレクティブを含むなど)を使用・デモする場合は、組み込みの `` コンポーネントでラップできます。 - ```md - - - - ``` +```md + + + +``` ## インポート時に Browser API にアクセスするライブラリ {#libraries-that-access-browser-api-on-import} @@ -24,112 +25,112 @@ SSR に適さないコンポーネント(例:カスタムディレクティ ### mounted フック内でのインポート {#importing-in-mounted-hook} - ```vue - - ``` +onMounted(() => { + import('./lib-that-access-window-on-import').then((module) => { + // ここでコードを利用 + }) +}) + +``` ### 条件付きインポート {#conditional-import} [`import.meta.env.SSR`](https://vitejs.dev/guide/env-and-mode.html#env-variables) フラグ(Vite の環境変数の一部)を使って、依存関係を条件付きでインポートすることもできます。 - ```js - if (!import.meta.env.SSR) { - import('./lib-that-access-window-on-import').then((module) => { - // ここでコードを利用 - }) - } - ``` +```js +if (!import.meta.env.SSR) { + import('./lib-that-access-window-on-import').then((module) => { + // ここでコードを利用 + }) +} +``` [`Theme.enhanceApp`](./custom-theme#theme-interface) は非同期にできるため、**インポート時に Browser API に触れる Vue プラグイン** を条件付きでインポート・登録できます。 - ```js [.vitepress/theme/index.js] - /** @type {import('vitepress').Theme} */ - export default { - // ... - async enhanceApp({ app }) { - if (!import.meta.env.SSR) { - const plugin = await import('plugin-that-access-window-on-import') - app.use(plugin.default) - } - } - } - ``` +```js [.vitepress/theme/index.js] +/** @type {import('vitepress').Theme} */ +export default { + // ... + async enhanceApp({ app }) { + if (!import.meta.env.SSR) { + const plugin = await import('plugin-that-access-window-on-import') + app.use(plugin.default) + } + } +} +``` TypeScript を使う場合: - ```ts [.vitepress/theme/index.ts] - import type { Theme } from 'vitepress' +```ts [.vitepress/theme/index.ts] +import type { Theme } from 'vitepress' - export default { - // ... - async enhanceApp({ app }) { - if (!import.meta.env.SSR) { - const plugin = await import('plugin-that-access-window-on-import') - app.use(plugin.default) - } - } - } satisfies Theme - ``` +export default { + // ... + async enhanceApp({ app }) { + if (!import.meta.env.SSR) { + const plugin = await import('plugin-that-access-window-on-import') + app.use(plugin.default) + } + } +} satisfies Theme +``` ### `defineClientComponent` VitePress は、**インポート時に Browser API にアクセスする Vue コンポーネント** を読み込むためのユーティリティを提供します。 - ```vue - +const ClientComp = defineClientComponent(() => { + return import('component-that-access-window-on-import') +}) + - - ``` + +``` ターゲットコンポーネントに props / children / slots を渡すこともできます。 - ```vue - - - - ``` +```vue + + + +``` ターゲットコンポーネントは、ラッパーコンポーネントの mounted フックで初めてインポートされます。 diff --git a/docs/ja/guide/using-vue.md b/docs/ja/guide/using-vue.md index 65f2383e..9cbc7608 100644 --- a/docs/ja/guide/using-vue.md +++ b/docs/ja/guide/using-vue.md @@ -1,3 +1,7 @@ +--- +description: VitePressのMarkdownファイル内でVueコンポーネントや動的テンプレート機能を直接使用する方法。 +--- + # MarkdownでVueを使う {#using-vue-in-markdown} VitePress では、各 Markdown ファイルはまず HTML にコンパイルされ、その後 [Vue の単一ファイルコンポーネント(SFC)](https://vuejs.org/guide/scaling-up/sfc.html) として処理されます。つまり、Markdown 内で Vue のあらゆる機能が使えます。動的テンプレート、Vue コンポーネントの利用、` +const count = ref(0) + - ## Markdown コンテンツ +## Markdown コンテンツ - 現在の値: {{ count }} +現在の値: {{ count }} - + - - ``` + +``` ::: warning Markdown での ` - ``` +```vue + +``` ## Teleport の利用 {#using-teleports} @@ -231,15 +235,15 @@ VitePress は CSS プリプロセッサ(`.scss`、`.sass`、`.less`、`.styl` <<< @/components/ModalDemo.vue ::: - ```md - - -
    - // ... -
    -
    -
    - ``` +```md + + +
    + // ... +
    +
    +
    +``` +const { hasSidebar } = useLayout() + - - ``` + +``` diff --git a/docs/ja/reference/default-theme-edit-link.md b/docs/ja/reference/default-theme-edit-link.md index 03573944..1edac327 100644 --- a/docs/ja/reference/default-theme-edit-link.md +++ b/docs/ja/reference/default-theme-edit-link.md @@ -1,60 +1,64 @@ +--- +description: GitHubやGitLabでページの編集リンクをドキュメントページに表示する方法。 +--- + # 編集リンク {#edit-link} ## サイトレベルの設定 {#site-level-config} 編集リンクは、GitHub や GitLab などの Git 管理サービスでそのページを編集できるリンクを表示します。有効化するには、設定に `themeConfig.editLink` オプションを追加します。 - ```js - export default { - themeConfig: { - editLink: { - pattern: 'https://github.com/vuejs/vitepress/edit/main/docs/:path' - } - } - } - ``` +```js +export default { + themeConfig: { + editLink: { + pattern: 'https://github.com/vuejs/vitepress/edit/main/docs/:path' + } + } +} +``` `pattern` オプションはリンクの URL 構造を定義します。`:path` はページパスに置き換えられます。 また、引数に [`PageData`](./runtime-api#usedata) を受け取り、URL 文字列を返す純粋関数を指定することもできます。 - ```js - export default { - themeConfig: { - editLink: { - pattern: ({ filePath }) => { - if (filePath.startsWith('packages/')) { - return `https://github.com/acme/monorepo/edit/main/${filePath}` - } else { - return `https://github.com/acme/monorepo/edit/main/docs/${filePath}` - } - } - } - } - } - ``` +```js +export default { + themeConfig: { + editLink: { + pattern: ({ filePath }) => { + if (filePath.startsWith('packages/')) { + return `https://github.com/acme/monorepo/edit/main/${filePath}` + } else { + return `https://github.com/acme/monorepo/edit/main/docs/${filePath}` + } + } + } + } +} +``` この関数はブラウザでシリアライズされ実行されるため、副作用を持たず、スコープ外のものへアクセスしないでください。 既定では、ドキュメント下部に「Edit this page」というリンクテキストが表示されます。`text` オプションでこの文言をカスタマイズできます。 - ```js - export default { - themeConfig: { - editLink: { - pattern: 'https://github.com/vuejs/vitepress/edit/main/docs/:path', - text: 'GitHub でこのページを編集' - } - } - } - ``` +```js +export default { + themeConfig: { + editLink: { + pattern: 'https://github.com/vuejs/vitepress/edit/main/docs/:path', + text: 'GitHub でこのページを編集' + } + } +} +``` ## フロントマターでの設定 {#frontmatter-config} ページごとに無効化するには、フロントマターで `editLink` オプションを使用します。 - ```yaml - --- - editLink: false - --- - ``` +```yaml +--- +editLink: false +--- +``` diff --git a/docs/ja/reference/default-theme-footer.md b/docs/ja/reference/default-theme-footer.md index f8226c89..44783ba4 100644 --- a/docs/ja/reference/default-theme-footer.md +++ b/docs/ja/reference/default-theme-footer.md @@ -1,3 +1,7 @@ +--- +description: VitePressページ下部に表示されるグローバルフッターの設定方法。 +--- + # フッター {#footer} `themeConfig.footer` を設定すると、ページ下部にグローバルフッターが表示されます。 diff --git a/docs/ja/reference/default-theme-home-page.md b/docs/ja/reference/default-theme-home-page.md index e472a478..ccf11620 100644 --- a/docs/ja/reference/default-theme-home-page.md +++ b/docs/ja/reference/default-theme-home-page.md @@ -1,3 +1,7 @@ +--- +description: VitePressデフォルトテーマのホームページレイアウトをヒーローセクション、フィーチャー、カスタムコンテンツで設定する方法。 +--- + # ホームページ {#home-page} VitePress のデフォルトテーマにはホームページ用レイアウトが用意されています([このサイトのトップページ](../) でも使われています)。[フロントマター](./frontmatter-config) に `layout: home` を指定すれば、任意のページで利用できます。 @@ -186,3 +190,4 @@ hero: npm init npx vitepress init ``` +```` diff --git a/docs/ja/reference/default-theme-last-updated.md b/docs/ja/reference/default-theme-last-updated.md index 82dfc6de..51c38374 100644 --- a/docs/ja/reference/default-theme-last-updated.md +++ b/docs/ja/reference/default-theme-last-updated.md @@ -1,3 +1,7 @@ +--- +description: Gitコミット履歴に基づいてVitePressページに最終更新日時を表示する方法。 +--- + # 最終更新日時 {#last-updated} ページ右下に、コンテンツの最終更新時刻を表示できます。有効化するには、設定に `lastUpdated` オプションを追加します。 diff --git a/docs/ja/reference/default-theme-layout.md b/docs/ja/reference/default-theme-layout.md index e241d03e..7a6b9059 100644 --- a/docs/ja/reference/default-theme-layout.md +++ b/docs/ja/reference/default-theme-layout.md @@ -1,3 +1,7 @@ +--- +description: VitePressデフォルトテーマのdoc、page、homeレイアウトの選択と使い方。 +--- + # レイアウト {#layout} ページの [フロントマター](./frontmatter-config) の `layout` オプションでページのレイアウトを選択できます。利用可能なレイアウトは `doc`、`page`、`home` の 3 種類です。何も指定しない場合は `doc` として扱われます。 diff --git a/docs/ja/reference/default-theme-nav.md b/docs/ja/reference/default-theme-nav.md index 207bf9e9..3ac7ec17 100644 --- a/docs/ja/reference/default-theme-nav.md +++ b/docs/ja/reference/default-theme-nav.md @@ -1,3 +1,7 @@ +--- +description: VitePressデフォルトテーマのナビゲーションバーにサイトタイトル、ロゴ、メニューリンクを設定する方法。 +--- + # ナビゲーション {#nav} ナビはページ上部に表示されるナビゲーションバーです。サイトタイトル、グローバルメニューリンクなどを含みます。 diff --git a/docs/ja/reference/default-theme-prev-next-links.md b/docs/ja/reference/default-theme-prev-next-links.md index 7b15d699..5a1e7783 100644 --- a/docs/ja/reference/default-theme-prev-next-links.md +++ b/docs/ja/reference/default-theme-prev-next-links.md @@ -1,3 +1,7 @@ +--- +description: VitePressドキュメントページ下部に表示される「前のページ」「次のページ」リンクのカスタマイズ方法。 +--- + # 前/次リンク {#prev-next-links} ドキュメントのフッターに表示される「前のページ」「次のページ」のテキストとリンクをカスタマイズできます。サイドバーに表示しているタイトルとは別の文言を使いたい場合や、フッターを無効化したり、サイドバーに含まれていないページへリンクしたい場合に便利です。 diff --git a/docs/ja/reference/default-theme-search.md b/docs/ja/reference/default-theme-search.md index e15de4ef..e2042f75 100644 --- a/docs/ja/reference/default-theme-search.md +++ b/docs/ja/reference/default-theme-search.md @@ -1,5 +1,6 @@ --- outline: deep +description: VitePressサイトにローカル検索またはAlgolia検索を設定する方法。 --- # 検索 {#search} @@ -29,6 +30,7 @@ export default defineConfig({ - - - +- ### i18n {#local-search-i18n} @@ -43,25 +45,25 @@ export default defineConfig({ provider: 'local', options: { locales: { - zh: { // 既定ロケールの文言も翻訳したい場合はこれを `root` に + ja: { // 既定ロケールを翻訳する場合は `root` にしてください translations: { button: { - buttonText: '搜索', - buttonAriaLabel: '搜索' + buttonText: '検索', + buttonAriaLabel: '検索' }, modal: { - displayDetails: '显示详细列表', - resetButtonTitle: '重置搜索', - backButtonTitle: '关闭搜索', - noResultsText: '没有结果', + displayDetails: '詳細一覧を表示', + resetButtonTitle: '検索をリセット', + backButtonTitle: '検索を閉じる', + noResultsText: '結果が見つかりません', footer: { - selectText: '选择', - selectKeyAriaLabel: '输入', - navigateText: '导航', - navigateUpKeyAriaLabel: '上箭头', - navigateDownKeyAriaLabel: '下箭头', - closeText: '关闭', - closeKeyAriaLabel: 'esc' + selectText: '選択', + selectKeyAriaLabel: 'Enter', + navigateText: '移動', + navigateUpKeyAriaLabel: '上矢印', + navigateDownKeyAriaLabel: '下矢印', + closeText: '閉じる', + closeKeyAriaLabel: 'Esc' } } } @@ -73,7 +75,7 @@ export default defineConfig({ }) ``` -### miniSearch のオプション {#mini-search-options} +### miniSearch のオプション {#minisearch-options} MiniSearch の設定例です。 @@ -178,7 +180,7 @@ export default defineConfig({ 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 (await md.renderAsync(`# ${env.frontmatter.title}`)) + html return html } } @@ -212,6 +214,19 @@ export default defineConfig({ 多言語検索の設定例です。 +
    +クリックして展開 + +<<< @/snippets/algolia-i18n.ts + +
    + +詳しくは[公式 Algolia ドキュメント](https://docsearch.algolia.com/docs/api#translations)を参照してください。すぐに始めるには、このサイトで使っている翻訳を[GitHub リポジトリ](https://github.com/search?q=repo:vuejs/vitepress+%22function+searchOptions%22&type=code)からコピーすることもできます。 + +### Algolia Ask AI のサポート {#ask-ai} + +**Ask AI** を有効にするには、`options` 内に `askAi` オプション(またはその一部)を指定します。 + ```ts import { defineConfig } from 'vitepress' @@ -223,79 +238,15 @@ export default defineConfig({ 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: '搜索提供者' - } - } - } - } + // askAi: "あなたのアシスタントID" + // または + askAi: { + // 最低限、Algolia から受け取った assistantId を指定する必要があります + assistantId: 'XXXYYY', + // 任意の上書き — 省略した場合は上位の appId/apiKey/indexName を再利用 + // apiKey: '...', + // appId: '...', + // indexName: '...' } } } @@ -303,11 +254,13 @@ export default defineConfig({ }) ``` -[これらのオプション](https://github.com/vuejs/vitepress/blob/main/types/docsearch.d.ts) は上書きできます。詳細は Algolia の公式ドキュメントを参照してください。 +::: warning 注意 +キーワード検索を既定にして Ask AI を使わない場合は、`askAi` を指定しないでください。 +::: -### Algolia Ask AI のサポート {#ask-ai} +### Ask AI サイドパネル {#ask-ai-side-panel} -**Ask AI** を有効にするには、`options` 内に `askAi` オプション(またはその一部)を指定します。 +DocSearch v4.5+ はオプションの **Ask AI サイドパネル**をサポートしています。有効にすると、デフォルトで **Ctrl/Cmd+I** で開くことができます。[サイドパネル API リファレンス](https://docsearch.algolia.com/docs/sidepanel/api-reference)にオプションの完全なリストがあります。 ```ts import { defineConfig } from 'vitepress' @@ -320,15 +273,18 @@ export default defineConfig({ appId: '...', apiKey: '...', indexName: '...', - // askAi: "YOUR-ASSISTANT-ID" - // または askAi: { - // 少なくとも Algolia から受け取った assistantId を指定 assistantId: 'XXXYYY', - // 任意の上書き — 省略時は上位の appId/apiKey/indexName を再利用 - // apiKey: '...', - // appId: '...', - // indexName: '...' + sidePanel: { + // @docsearch/sidepanel-js SidepanelProps API をミラー + panel: { + variant: 'floating', // または 'inline' + side: 'right', + width: '360px', + expandedWidth: '580px', + suggestedQuestions: true + } + } } } } @@ -336,116 +292,70 @@ export default defineConfig({ }) ``` -::: warning 注意 -キーワード検索を既定にして Ask AI を使わない場合は、`askAi` を指定しないでください。 -::: +キーボードショートカットを無効にする必要がある場合は、サイドパネルの `keyboardShortcuts` オプションを使用してください: -Ask AI UI の翻訳は `options.translations.modal.askAiScreen` と `options.translations.resultsScreen` にあります。すべてのキーは[型定義](https://github.com/vuejs/vitepress/blob/main/types/docsearch.d.ts)を参照してください。 +```ts +import { defineConfig } from 'vitepress' -### クローラー設定 {#crawler-config} +export default defineConfig({ + themeConfig: { + search: { + provider: 'algolia', + options: { + appId: '...', + apiKey: '...', + indexName: '...', + askAi: { + assistantId: 'XXXYYY', + sidePanel: { + keyboardShortcuts: { + 'Ctrl/Cmd+I': false + } + } + } + } + } + } +}) +``` -このサイトで使用している設定を元にした例です。 +#### モード (auto / sidePanel / hybrid / modal) {#ask-ai-mode} + +VitePress がキーワード検索と Ask AI を統合する方法をオプションで制御できます: + +- `mode: 'auto'`(デフォルト):キーワード検索が設定されている場合は `hybrid` を推論し、それ以外の場合は Ask AI サイドパネルが設定されている場合は `sidePanel` を推論します。 +- `mode: 'sidePanel'`:サイドパネルのみを強制(キーワード検索ボタンを非表示)。 +- `mode: 'hybrid'`:キーワード検索モーダル + Ask AI サイドパネルを有効化(キーワード検索設定が必要)。 +- `mode: 'modal'`:Ask AI を DocSearch モーダル内に保持(サイドパネルを設定した場合でも)。 + +#### Ask AI のみ(キーワード検索なし) {#ask-ai-only} + +**Ask AI サイドパネルのみ**を使用する場合は、トップレベルのキーワード検索設定を省略し、`askAi` の下に認証情報を提供できます: ```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 - }) +import { defineConfig } from 'vitepress' + +export default defineConfig({ + themeConfig: { + search: { + provider: 'algolia', + options: { + mode: 'sidePanel', + askAi: { + assistantId: 'XXXYYY', + appId: '...', + apiKey: '...', + indexName: '...', + sidePanel: 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' - } } }) ``` + +### クローラー設定 {#crawler-config} + +このサイトで使用している設定を元にした例です。 + +<<< @/snippets/algolia-crawler.js diff --git a/docs/ja/reference/default-theme-sidebar.md b/docs/ja/reference/default-theme-sidebar.md index ddd87383..1f29e629 100644 --- a/docs/ja/reference/default-theme-sidebar.md +++ b/docs/ja/reference/default-theme-sidebar.md @@ -1,3 +1,7 @@ +--- +description: VitePressデフォルトテーマのサイドバーナビゲーションをグループ、折りたたみセクション、複数サイドバーで設定する方法。 +--- + # サイドバー {#sidebar} サイドバーはドキュメントの主要なナビゲーションブロックです。[`themeConfig.sidebar`](./default-theme-config#sidebar) でメニューを設定できます。 diff --git a/docs/ja/reference/default-theme-team-page.md b/docs/ja/reference/default-theme-team-page.md index 5f02d4d0..4de34036 100644 --- a/docs/ja/reference/default-theme-team-page.md +++ b/docs/ja/reference/default-theme-team-page.md @@ -1,3 +1,7 @@ +--- +description: VitePress組み込みのチームコンポーネントを使い、メンバープロフィール付きのチームページを作成する方法。 +--- + diff --git a/src/client/theme-default/components/VPContent.vue b/src/client/theme-default/components/VPContent.vue index b0eb5fc8..42e11fc8 100644 --- a/src/client/theme-default/components/VPContent.vue +++ b/src/client/theme-default/components/VPContent.vue @@ -1,4 +1,5 @@ diff --git a/src/client/theme-default/components/VPFeature.vue b/src/client/theme-default/components/VPFeature.vue index ff36fe97..e0e49b18 100644 --- a/src/client/theme-default/components/VPFeature.vue +++ b/src/client/theme-default/components/VPFeature.vue @@ -41,8 +41,10 @@ defineProps<{ />

    -

    - +
      +
    • +
    +