docus 5.4.4 → 5.5.1

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.
Files changed (32) hide show
  1. package/app/app.vue +16 -6
  2. package/app/components/app/AppHeader.vue +5 -0
  3. package/app/components/docs/DocsAsideRightBottom.vue +28 -1
  4. package/app/composables/useSeo.ts +207 -0
  5. package/app/pages/[[lang]]/[...slug].vue +17 -9
  6. package/app/plugins/i18n.ts +28 -11
  7. package/app/templates/landing.vue +4 -10
  8. package/app/types/index.d.ts +49 -0
  9. package/app/utils/navigation.ts +31 -0
  10. package/i18n/locales/en.json +27 -0
  11. package/i18n/locales/fr.json +28 -1
  12. package/modules/assistant/README.md +213 -0
  13. package/modules/assistant/index.ts +100 -0
  14. package/modules/assistant/runtime/components/AssistantChat.vue +21 -0
  15. package/modules/assistant/runtime/components/AssistantChatDisabled.vue +3 -0
  16. package/modules/assistant/runtime/components/AssistantFloatingInput.vue +110 -0
  17. package/modules/assistant/runtime/components/AssistantLoading.vue +164 -0
  18. package/modules/assistant/runtime/components/AssistantMatrix.vue +92 -0
  19. package/modules/assistant/runtime/components/AssistantPanel.vue +329 -0
  20. package/modules/assistant/runtime/components/AssistantPreStream.vue +46 -0
  21. package/modules/assistant/runtime/composables/useAssistant.ts +107 -0
  22. package/modules/assistant/runtime/composables/useHighlighter.ts +34 -0
  23. package/modules/assistant/runtime/server/api/search.ts +111 -0
  24. package/modules/assistant/runtime/types.ts +7 -0
  25. package/modules/config.ts +6 -4
  26. package/modules/css.ts +6 -2
  27. package/modules/markdown-rewrite.ts +130 -0
  28. package/nuxt.config.ts +22 -1
  29. package/nuxt.schema.ts +63 -0
  30. package/package.json +24 -15
  31. package/server/routes/sitemap.xml.ts +93 -0
  32. package/utils/meta.ts +9 -3
package/app/app.vue CHANGED
@@ -5,6 +5,7 @@ import * as nuxtUiLocales from '@nuxt/ui/locale'
5
5
  const { seo } = useAppConfig()
6
6
  const site = useSiteConfig()
7
7
  const { locale, locales, isEnabled, switchLocalePath } = useDocusI18n()
8
+ const { isEnabled: isAssistantEnabled, panelWidth: assistantPanelWidth, shouldPushContent } = useAssistant()
8
9
 
9
10
  const nuxtUiLocale = computed(() => nuxtUiLocales[locale.value as keyof typeof nuxtUiLocales] || nuxtUiLocales.en)
10
11
  const lang = computed(() => nuxtUiLocale.value.code)
@@ -47,7 +48,7 @@ const { data: navigation } = await useAsyncData(() => `navigation_${collectionNa
47
48
  transform: (data: ContentNavigationItem[]) => {
48
49
  const rootResult = data.find(item => item.path === '/docs')?.children || data || []
49
50
 
50
- return rootResult.find(item => item.path === `/${locale.value}`)?.children || rootResult
51
+ return rootResult.find((item: ContentNavigationItem) => item.path === `/${locale.value}`)?.children || rootResult
51
52
  },
52
53
  watch: [locale],
53
54
  })
@@ -63,17 +64,26 @@ provide('navigation', navigation)
63
64
  <UApp :locale="nuxtUiLocale">
64
65
  <NuxtLoadingIndicator color="var(--ui-primary)" />
65
66
 
66
- <AppHeader v-if="$route.meta.header !== false" />
67
- <NuxtLayout>
68
- <NuxtPage />
69
- </NuxtLayout>
70
- <AppFooter v-if="$route.meta.footer !== false" />
67
+ <div
68
+ class="transition-[margin-right] duration-200 ease-linear will-change-[margin-right]"
69
+ :style="{ marginRight: shouldPushContent ? `${assistantPanelWidth}px` : '0' }"
70
+ >
71
+ <AppHeader v-if="$route.meta.header !== false" />
72
+ <NuxtLayout>
73
+ <NuxtPage />
74
+ </NuxtLayout>
75
+ <AppFooter v-if="$route.meta.footer !== false" />
76
+ </div>
71
77
 
72
78
  <ClientOnly>
73
79
  <LazyUContentSearch
74
80
  :files="files"
75
81
  :navigation="navigation"
76
82
  />
83
+ <template v-if="isAssistantEnabled">
84
+ <LazyAssistantPanel />
85
+ <LazyAssistantFloatingInput />
86
+ </template>
77
87
  </ClientOnly>
78
88
  </UApp>
79
89
  </template>
@@ -4,6 +4,7 @@ import { useDocusI18n } from '../../composables/useDocusI18n'
4
4
  const appConfig = useAppConfig()
5
5
  const site = useSiteConfig()
6
6
 
7
+ const { isEnabled: isAssistantEnabled } = useAssistant()
7
8
  const { localePath, isEnabled, locales } = useDocusI18n()
8
9
 
9
10
  const links = computed(() => appConfig.github && appConfig.github.url
@@ -33,6 +34,10 @@ const links = computed(() => appConfig.github && appConfig.github.url
33
34
  <template #right>
34
35
  <AppHeaderCTA />
35
36
 
37
+ <template v-if="isAssistantEnabled">
38
+ <AssistantChat />
39
+ </template>
40
+
36
41
  <template v-if="isEnabled && locales.length > 1">
37
42
  <ClientOnly>
38
43
  <LanguageSelect />
@@ -1,18 +1,45 @@
1
1
  <script setup lang="ts">
2
+ const route = useRoute()
3
+
4
+ const pageUrl = route.path
2
5
  const appConfig = useAppConfig()
3
6
  const { t } = useDocusI18n()
7
+ const { isEnabled, open } = useAssistant()
8
+
9
+ const showExplainWithAi = computed(() => {
10
+ return isEnabled.value && appConfig.assistant?.explainWithAi !== false
11
+ })
12
+
13
+ const explainIcon = computed(() => appConfig.assistant?.icons?.explain || 'i-lucide-brain')
4
14
  </script>
5
15
 
6
16
  <template>
7
17
  <div
8
- v-if="appConfig.toc?.bottom?.links?.length"
18
+ v-if="appConfig.toc?.bottom?.links?.length || showExplainWithAi"
9
19
  class="hidden lg:block space-y-6"
10
20
  >
11
21
  <USeparator type="dashed" />
12
22
 
13
23
  <UPageLinks
24
+ v-if="appConfig.toc?.bottom?.links?.length"
14
25
  :title="appConfig.toc?.bottom?.title || t('docs.links')"
15
26
  :links="appConfig.toc?.bottom?.links"
16
27
  />
28
+
29
+ <USeparator
30
+ v-if="appConfig.toc?.bottom?.links?.length && showExplainWithAi"
31
+ type="dashed"
32
+ />
33
+
34
+ <UButton
35
+ v-if="showExplainWithAi"
36
+ :icon="explainIcon"
37
+ :label="t('assistant.explainWithAi')"
38
+ size="sm"
39
+ variant="link"
40
+ class="p-0 text-sm"
41
+ color="neutral"
42
+ @click="open(`Explain the page ${pageUrl}`, true)"
43
+ />
17
44
  </div>
18
45
  </template>
@@ -0,0 +1,207 @@
1
+ import type { MaybeRefOrGetter } from 'vue'
2
+ import type { BreadcrumbItem } from '../utils/navigation'
3
+ import { joinURL, withoutTrailingSlash } from 'ufo'
4
+
5
+ export interface UseSeoOptions {
6
+ /**
7
+ * Page title
8
+ */
9
+ title: MaybeRefOrGetter<string | undefined>
10
+ /**
11
+ * Page description
12
+ */
13
+ description: MaybeRefOrGetter<string | undefined>
14
+ /**
15
+ * Page type for og:type (default: 'article' for docs, 'website' for landing)
16
+ */
17
+ type?: MaybeRefOrGetter<'website' | 'article'>
18
+ /**
19
+ * Custom OG image URL (absolute)
20
+ */
21
+ ogImage?: MaybeRefOrGetter<string | undefined>
22
+ /**
23
+ * Published date for article schema
24
+ */
25
+ publishedAt?: MaybeRefOrGetter<string | undefined>
26
+ /**
27
+ * Modified date for article schema
28
+ */
29
+ modifiedAt?: MaybeRefOrGetter<string | undefined>
30
+ /**
31
+ * Breadcrumb items for BreadcrumbList schema
32
+ */
33
+ breadcrumbs?: MaybeRefOrGetter<BreadcrumbItem[] | undefined>
34
+ }
35
+
36
+ /**
37
+ * Composable for comprehensive SEO setup including:
38
+ * - Meta tags (title, description, og:*, twitter:*)
39
+ * - Canonical URLs
40
+ * - Hreflang tags for i18n
41
+ * - JSON-LD structured data
42
+ */
43
+ export function useSeo(options: UseSeoOptions) {
44
+ const route = useRoute()
45
+ const site = useSiteConfig()
46
+ const { locale, locales, isEnabled: isI18nEnabled, switchLocalePath } = useDocusI18n()
47
+
48
+ const title = computed(() => toValue(options.title))
49
+ const description = computed(() => toValue(options.description))
50
+ const type = computed(() => toValue(options.type) || 'article')
51
+ const ogImage = computed(() => toValue(options.ogImage))
52
+ const publishedAt = computed(() => toValue(options.publishedAt))
53
+ const modifiedAt = computed(() => toValue(options.modifiedAt))
54
+ const breadcrumbs = computed(() => toValue(options.breadcrumbs))
55
+
56
+ // Build canonical URL
57
+ const canonicalUrl = computed(() => {
58
+ if (!site.url) return undefined
59
+ return joinURL(site.url, route.path)
60
+ })
61
+
62
+ // Base URL for building other URLs
63
+ const baseUrl = computed(() => site.url ? withoutTrailingSlash(site.url) : '')
64
+
65
+ // Set meta tags
66
+ useSeoMeta({
67
+ title,
68
+ description,
69
+ ogTitle: title,
70
+ ogDescription: description,
71
+ ogType: type,
72
+ ogUrl: canonicalUrl,
73
+ ogLocale: computed(() => isI18nEnabled.value ? locale.value : undefined),
74
+ })
75
+
76
+ // Set canonical link
77
+ useHead({
78
+ link: computed(() => {
79
+ const links: Array<{ rel: string, href?: string, hreflang?: string }> = []
80
+
81
+ // Canonical URL
82
+ if (canonicalUrl.value) {
83
+ links.push({
84
+ rel: 'canonical',
85
+ href: canonicalUrl.value,
86
+ })
87
+ }
88
+
89
+ // Hreflang tags for i18n
90
+ if (isI18nEnabled.value && baseUrl.value) {
91
+ for (const loc of locales) {
92
+ const localePath = switchLocalePath(loc.code)
93
+ if (localePath) {
94
+ links.push({
95
+ rel: 'alternate',
96
+ hreflang: loc.code,
97
+ href: joinURL(baseUrl.value, localePath),
98
+ })
99
+ }
100
+ }
101
+
102
+ // x-default hreflang (points to default locale)
103
+ const defaultLocalePath = switchLocalePath(locales[0]?.code || 'en')
104
+ if (defaultLocalePath) {
105
+ links.push({
106
+ rel: 'alternate',
107
+ hreflang: 'x-default',
108
+ href: joinURL(baseUrl.value, defaultLocalePath),
109
+ })
110
+ }
111
+ }
112
+
113
+ return links
114
+ }),
115
+ })
116
+
117
+ // Custom OG image handling
118
+ if (ogImage.value) {
119
+ useSeoMeta({
120
+ ogImage: ogImage.value,
121
+ twitterImage: ogImage.value,
122
+ })
123
+ }
124
+
125
+ // JSON-LD structured data
126
+ useHead({
127
+ script: computed(() => {
128
+ const scripts: Array<{ type: string, innerHTML: string }> = []
129
+
130
+ if (!baseUrl.value || !title.value) return scripts
131
+
132
+ const pageUrl = joinURL(baseUrl.value, route.path)
133
+
134
+ // Article schema for documentation pages
135
+ if (type.value === 'article') {
136
+ const articleSchema: Record<string, unknown> = {
137
+ '@context': 'https://schema.org',
138
+ '@type': 'Article',
139
+ 'headline': title.value,
140
+ 'description': description.value,
141
+ 'url': pageUrl,
142
+ 'mainEntityOfPage': {
143
+ '@type': 'WebPage',
144
+ '@id': pageUrl,
145
+ },
146
+ }
147
+
148
+ if (publishedAt.value) {
149
+ articleSchema.datePublished = publishedAt.value
150
+ }
151
+
152
+ if (modifiedAt.value) {
153
+ articleSchema.dateModified = modifiedAt.value
154
+ }
155
+
156
+ if (site.name) {
157
+ articleSchema.publisher = {
158
+ '@type': 'Organization',
159
+ 'name': site.name,
160
+ }
161
+ }
162
+
163
+ scripts.push({
164
+ type: 'application/ld+json',
165
+ innerHTML: JSON.stringify(articleSchema),
166
+ })
167
+ }
168
+
169
+ // WebSite schema for landing pages
170
+ if (type.value === 'website') {
171
+ const websiteSchema: Record<string, unknown> = {
172
+ '@context': 'https://schema.org',
173
+ '@type': 'WebSite',
174
+ 'name': site.name || title.value,
175
+ 'description': description.value,
176
+ 'url': baseUrl.value,
177
+ }
178
+
179
+ scripts.push({
180
+ type: 'application/ld+json',
181
+ innerHTML: JSON.stringify(websiteSchema),
182
+ })
183
+ }
184
+
185
+ // BreadcrumbList schema for navigation
186
+ if (breadcrumbs.value && breadcrumbs.value.length > 0) {
187
+ const breadcrumbSchema = {
188
+ '@context': 'https://schema.org',
189
+ '@type': 'BreadcrumbList',
190
+ 'itemListElement': breadcrumbs.value.map((item, index) => ({
191
+ '@type': 'ListItem',
192
+ 'position': index + 1,
193
+ 'name': item.title,
194
+ 'item': joinURL(baseUrl.value, item.path),
195
+ })),
196
+ }
197
+
198
+ scripts.push({
199
+ type: 'application/ld+json',
200
+ innerHTML: JSON.stringify(breadcrumbSchema),
201
+ })
202
+ }
203
+
204
+ return scripts
205
+ }),
206
+ })
207
+ }
@@ -11,6 +11,7 @@ const route = useRoute()
11
11
  const { locale, isEnabled, t } = useDocusI18n()
12
12
  const appConfig = useAppConfig()
13
13
  const navigation = inject<Ref<ContentNavigationItem[]>>('navigation')
14
+ const { shouldPushContent: shouldHideToc } = useAssistant()
14
15
 
15
16
  const collectionName = computed(() => isEnabled.value ? `docs_${locale.value}` : 'docs')
16
17
 
@@ -30,14 +31,16 @@ if (!page.value) {
30
31
  const title = page.value.seo?.title || page.value.title
31
32
  const description = page.value.seo?.description || page.value.description
32
33
 
33
- useSeoMeta({
34
+ const headline = ref(findPageHeadline(navigation?.value, page.value?.path))
35
+ const breadcrumbs = computed(() => findPageBreadcrumbs(navigation?.value, page.value?.path || ''))
36
+
37
+ useSeo({
34
38
  title,
35
- ogTitle: title,
36
39
  description,
37
- ogDescription: description,
40
+ type: 'article',
41
+ modifiedAt: (page.value as unknown as Record<string, unknown>).modifiedAt as string | undefined,
42
+ breadcrumbs,
38
43
  })
39
-
40
- const headline = ref(findPageHeadline(navigation?.value, page.value?.path))
41
44
  watch(() => navigation?.value, () => {
42
45
  headline.value = findPageHeadline(navigation?.value, page.value?.path) || headline.value
43
46
  })
@@ -62,10 +65,16 @@ const editLink = computed(() => {
62
65
  `${page.value?.stem}.${page.value?.extension}`,
63
66
  ].filter(Boolean).join('/')
64
67
  })
68
+
69
+ // Add the page path to the prerender list
70
+ addPrerenderPath(`/raw${route.path}.md`)
65
71
  </script>
66
72
 
67
73
  <template>
68
- <UPage v-if="page">
74
+ <UPage
75
+ v-if="page"
76
+ :key="`page-${shouldHideToc}`"
77
+ >
69
78
  <UPageHeader
70
79
  :title="page.title"
71
80
  :description="page.description"
@@ -92,9 +101,8 @@ const editLink = computed(() => {
92
101
  :value="page"
93
102
  />
94
103
 
95
- <USeparator>
104
+ <USeparator v-if="github">
96
105
  <div
97
- v-if="github"
98
106
  class="flex items-center gap-2 text-sm text-muted"
99
107
  >
100
108
  <UButton
@@ -124,7 +132,7 @@ const editLink = computed(() => {
124
132
  </UPageBody>
125
133
 
126
134
  <template
127
- v-if="page?.body?.toc?.links?.length"
135
+ v-if="page?.body?.toc?.links?.length && !shouldHideToc"
128
136
  #right
129
137
  >
130
138
  <UContentToc
@@ -1,5 +1,10 @@
1
- import en from '../../i18n/locales/en.json'
2
1
  import type { RouteLocationNormalized } from 'vue-router'
2
+ import { consola } from 'consola'
3
+
4
+ const log = consola.withTag('Docus')
5
+
6
+ // Lazy import functions for locale files (bundled but not eagerly loaded)
7
+ const localeFiles = import.meta.glob<{ default: Record<string, unknown> }>('../../i18n/locales/*.json')
3
8
 
4
9
  export default defineNuxtPlugin(async () => {
5
10
  const nuxtApp = useNuxtApp()
@@ -8,19 +13,31 @@ export default defineNuxtPlugin(async () => {
8
13
 
9
14
  // If i18n is not enabled, fetch and provide the configured locale in app config
10
15
  if (!i18nConfig) {
11
- let locale = 'en'
12
- let resolvedMessages: Record<string, unknown> = en
13
-
14
16
  const appConfig = useAppConfig()
15
- const configuredLocale = appConfig.docus.locale
16
- if (configuredLocale !== 'en') {
17
- const localeMessages = await import(`../../i18n/locales/${configuredLocale}.json`)
18
- if (!localeMessages) {
19
- console.warn(`[Docus] Missing locale file for "${configuredLocale}". Falling back to "en".`)
17
+ const configuredLocale = appConfig.docus.locale || 'en'
18
+
19
+ let locale = configuredLocale
20
+ let resolvedMessages: Record<string, unknown>
21
+
22
+ // Try to load the requested locale file
23
+ const localeKey = `../../i18n/locales/${configuredLocale}.json`
24
+ const localeLoader = localeFiles[localeKey]
25
+
26
+ if (localeLoader) {
27
+ const localeModule = await localeLoader()
28
+ resolvedMessages = localeModule.default
29
+ }
30
+ else {
31
+ log.warn(`Missing locale file for "${configuredLocale}". Falling back to "en".`)
32
+ locale = 'en'
33
+ const fallbackKey = '../../i18n/locales/en.json'
34
+ const fallbackLoader = localeFiles[fallbackKey]
35
+ if (fallbackLoader) {
36
+ const fallbackModule = await fallbackLoader()
37
+ resolvedMessages = fallbackModule.default
20
38
  }
21
39
  else {
22
- locale = configuredLocale
23
- resolvedMessages = localeMessages
40
+ resolvedMessages = {} as Record<string, unknown>
24
41
  }
25
42
  }
26
43
 
@@ -15,20 +15,14 @@ if (!page.value) {
15
15
  const title = page.value.seo?.title || page.value.title
16
16
  const description = page.value.seo?.description || page.value.description
17
17
 
18
- useSeoMeta({
18
+ useSeo({
19
19
  title,
20
20
  description,
21
- ogTitle: title,
22
- ogDescription: description,
21
+ type: 'website',
22
+ ogImage: page.value?.seo?.ogImage as string | undefined,
23
23
  })
24
24
 
25
- if (page.value?.seo?.ogImage) {
26
- useSeoMeta({
27
- ogImage: page.value.seo.ogImage,
28
- twitterImage: page.value.seo.ogImage,
29
- })
30
- }
31
- else {
25
+ if (!page.value?.seo?.ogImage) {
32
26
  defineOgImageComponent('Landing', {
33
27
  title,
34
28
  description,
@@ -1,3 +1,7 @@
1
+ import type { FaqQuestions, LocalizedFaqQuestions } from '../../modules/assistant/runtime/types'
2
+
3
+ export type { FaqCategory, FaqQuestions, LocalizedFaqQuestions } from '../../modules/assistant/runtime/types'
4
+
1
5
  declare module 'nuxt/schema' {
2
6
  interface AppConfig {
3
7
  docus: {
@@ -36,6 +40,51 @@ declare module 'nuxt/schema' {
36
40
  branch: string
37
41
  rootDir?: string
38
42
  } | false
43
+ assistant?: {
44
+ /**
45
+ * Show the floating input at the bottom of documentation pages.
46
+ * @default true
47
+ */
48
+ floatingInput?: boolean
49
+ /**
50
+ * Show the "Explain with AI" button in the documentation sidebar.
51
+ * @default true
52
+ */
53
+ explainWithAi?: boolean
54
+ /**
55
+ * FAQ questions to display in the chat slideover.
56
+ * Can be a simple array of strings, an array of categories, or a locale-based object.
57
+ * @example Simple format: ['How to install?', 'How to configure?']
58
+ * @example Category format: [{ category: 'Getting Started', items: ['How to install?'] }]
59
+ * @example Localized format: { en: ['How to install?'], fr: ['Comment installer ?'] }
60
+ */
61
+ faqQuestions?: FaqQuestions | LocalizedFaqQuestions
62
+ /**
63
+ * Keyboard shortcuts configuration.
64
+ */
65
+ shortcuts?: {
66
+ /**
67
+ * Shortcut to focus the floating input.
68
+ * @default 'meta_i'
69
+ */
70
+ focusInput?: string
71
+ }
72
+ /**
73
+ * Icons configuration.
74
+ */
75
+ icons?: {
76
+ /**
77
+ * Icon for the assistant trigger button and slideover header.
78
+ * @default 'i-lucide-sparkles'
79
+ */
80
+ trigger?: string
81
+ /**
82
+ * Icon for the "Explain with AI" button.
83
+ * @default 'i-lucide-brain'
84
+ */
85
+ explain?: string
86
+ }
87
+ }
39
88
  }
40
89
  }
41
90
 
@@ -5,3 +5,34 @@ export const flattenNavigation = (items?: ContentNavigationItem[]): ContentNavig
5
5
  ? flattenNavigation(item.children)
6
6
  : [item],
7
7
  ) || []
8
+
9
+ export interface BreadcrumbItem {
10
+ title: string
11
+ path: string
12
+ }
13
+
14
+ /**
15
+ * Find breadcrumb path to a page in the navigation tree
16
+ */
17
+ export function findPageBreadcrumbs(
18
+ navigation: ContentNavigationItem[] | undefined,
19
+ path: string,
20
+ currentPath: BreadcrumbItem[] = [],
21
+ ): BreadcrumbItem[] | undefined {
22
+ if (!navigation) return undefined
23
+
24
+ for (const item of navigation) {
25
+ const itemPath = [...currentPath, { title: item.title, path: item.path }]
26
+
27
+ if (item.path === path) {
28
+ return itemPath
29
+ }
30
+
31
+ if (item.children) {
32
+ const found = findPageBreadcrumbs(item.children, path, itemPath)
33
+ if (found) return found
34
+ }
35
+ }
36
+
37
+ return undefined
38
+ }
@@ -18,5 +18,32 @@
18
18
  "toc": "On this page",
19
19
  "report": "Report an issue",
20
20
  "edit": "Edit this page"
21
+ },
22
+ "assistant": {
23
+ "title": "Ask AI",
24
+ "placeholder": "Ask a question...",
25
+ "tooltip": "Ask AI a question",
26
+ "tryAsking": "Try asking a question",
27
+ "askAnything": "Ask anything...",
28
+ "clearChat": "Clear chat",
29
+ "close": "Close",
30
+ "expand": "Expand",
31
+ "collapse": "Collapse",
32
+ "thinking": "Thinking...",
33
+ "askMeAnything": "Ask anything",
34
+ "askMeAnythingDescription": "Get help navigating the documentation, understanding concepts, and finding answers.",
35
+ "faq": "FAQ",
36
+ "chatCleared": "Chat is cleared on refresh",
37
+ "lineBreak": "Line break",
38
+ "explainWithAi": "Explain with AI",
39
+ "toolListPages": "Listed documentation pages",
40
+ "toolReadPage": "Read",
41
+ "loading": {
42
+ "searching": "Searching the documentation",
43
+ "reading": "Reading through the docs",
44
+ "analyzing": "Analyzing the content",
45
+ "finding": "Finding the best answer",
46
+ "finished": "Sources used"
47
+ }
21
48
  }
22
49
  }
@@ -18,5 +18,32 @@
18
18
  "toc": "Sur cette page",
19
19
  "report": "Signaler un problème",
20
20
  "edit": "Éditer cette page"
21
+ },
22
+ "assistant": {
23
+ "title": "Demander à l'IA",
24
+ "placeholder": "Posez une question...",
25
+ "tooltip": "Poser une question à l'IA",
26
+ "tryAsking": "Essayez de poser une question",
27
+ "askAnything": "Demandez n'importe quoi...",
28
+ "clearChat": "Effacer le chat",
29
+ "close": "Fermer",
30
+ "expand": "Agrandir",
31
+ "collapse": "Réduire",
32
+ "thinking": "Réflexion...",
33
+ "askMeAnything": "Posez une question",
34
+ "askMeAnythingDescription": "Obtenez de l'aide pour naviguer dans la documentation, comprendre des concepts et trouver des réponses.",
35
+ "faq": "FAQ",
36
+ "chatCleared": "Le chat est effacé au rechargement",
37
+ "lineBreak": "Retour à la ligne",
38
+ "explainWithAi": "Expliquer avec l'IA",
39
+ "toolListPages": "Pages de documentation listées",
40
+ "toolReadPage": "Lecture de",
41
+ "loading": {
42
+ "searching": "Recherche dans la documentation",
43
+ "reading": "Lecture des documents",
44
+ "analyzing": "Analyse du contenu",
45
+ "finding": "Recherche de la meilleure réponse",
46
+ "finished": "Sources utilisées"
47
+ }
21
48
  }
22
- }
49
+ }