docus 5.12.1 → 5.12.3

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 (56) hide show
  1. package/app/app.config.ts +7 -16
  2. package/app/components/app/AppHeaderBody.vue +14 -1
  3. package/app/components/app/AppHeaderCenter.vue +1 -1
  4. package/app/components/docs/DocsAsideLeftBody.vue +1 -0
  5. package/app/components/docs/DocsAsideMobileBar.vue +4 -4
  6. package/app/components/docs/DocsAsideRight.vue +1 -1
  7. package/app/composables/useSubNavigation.ts +1 -1
  8. package/app/composables/useUIConfig.ts +1 -1
  9. package/app/utils/assistantTools.ts +88 -0
  10. package/i18n/locales/ar.json +15 -1
  11. package/i18n/locales/be.json +15 -1
  12. package/i18n/locales/bg.json +15 -1
  13. package/i18n/locales/bn.json +15 -1
  14. package/i18n/locales/ca.json +15 -1
  15. package/i18n/locales/ckb.json +15 -1
  16. package/i18n/locales/cs.json +15 -1
  17. package/i18n/locales/da.json +15 -1
  18. package/i18n/locales/de.json +15 -1
  19. package/i18n/locales/el.json +15 -1
  20. package/i18n/locales/en.json +15 -1
  21. package/i18n/locales/es.json +15 -1
  22. package/i18n/locales/et.json +15 -1
  23. package/i18n/locales/fi.json +15 -1
  24. package/i18n/locales/fr.json +15 -1
  25. package/i18n/locales/he.json +15 -1
  26. package/i18n/locales/hi.json +15 -1
  27. package/i18n/locales/hy.json +15 -1
  28. package/i18n/locales/id.json +15 -1
  29. package/i18n/locales/it.json +15 -1
  30. package/i18n/locales/ja.json +15 -1
  31. package/i18n/locales/kk.json +15 -1
  32. package/i18n/locales/km.json +15 -1
  33. package/i18n/locales/ko.json +15 -1
  34. package/i18n/locales/ky.json +15 -1
  35. package/i18n/locales/lb.json +15 -1
  36. package/i18n/locales/ms.json +15 -1
  37. package/i18n/locales/nb.json +15 -1
  38. package/i18n/locales/nl.json +15 -1
  39. package/i18n/locales/pl.json +15 -1
  40. package/i18n/locales/pt-BR.json +15 -1
  41. package/i18n/locales/ro.json +15 -1
  42. package/i18n/locales/ru.json +15 -1
  43. package/i18n/locales/si.json +15 -1
  44. package/i18n/locales/sl.json +15 -1
  45. package/i18n/locales/sv.json +15 -1
  46. package/i18n/locales/tr.json +15 -1
  47. package/i18n/locales/uk.json +15 -1
  48. package/i18n/locales/ur.json +15 -1
  49. package/i18n/locales/vi.json +15 -1
  50. package/i18n/locales/zh-CN.json +15 -1
  51. package/modules/assistant/runtime/components/AssistantFloatingInput.vue +13 -2
  52. package/modules/assistant/runtime/components/AssistantIndicator.vue +15 -6
  53. package/modules/assistant/runtime/components/AssistantPanel.vue +20 -42
  54. package/modules/assistant/runtime/server/api/search.ts +14 -16
  55. package/modules/css.ts +3 -1
  56. package/package.json +16 -16
@@ -43,7 +43,6 @@
43
43
  "close": "වසන්න",
44
44
  "expand": "පුළුල් කරන්න",
45
45
  "collapse": "හකුළන්න",
46
- "thinking": "සිතමින්...",
47
46
  "askMeAnything": "ඕන දෙයක් අහන්න",
48
47
  "askMeAnythingDescription": "ලේඛන සැරිසැරීමට, සංකල්ප තේරුම් ගැනීමට සහ පිළිතුරු සෙවීමට උදවු ලබා ගන්න.",
49
48
  "faq": "නිතර අසන පැන",
@@ -52,7 +51,22 @@
52
51
  "explainWithAi": "AI සමඟ පැහැදිලි කරන්න",
53
52
  "toolListPages": "ලැයිස්තුගත ලේඛන පිටු",
54
53
  "toolReadPage": "කියවන්න",
54
+ "tools": {
55
+ "searching": "සොයමින්",
56
+ "searched": "සෙව්වා",
57
+ "reading": "කියවමින්",
58
+ "read": "කියෙව්වා",
59
+ "finding": "සොයමින්",
60
+ "found": "හමු විය",
61
+ "opening": "විවෘත කරමින්",
62
+ "opened": "විවෘත කළා",
63
+ "generating": "ජනනය කරමින්",
64
+ "generated": "ජනනය කළා",
65
+ "creating": "සාදමින්",
66
+ "created": "සෑදුවා"
67
+ },
55
68
  "loading": {
69
+ "thinking": "සිතමින්...",
56
70
  "searching": "ලේඛන සෙවීම",
57
71
  "reading": "ලේඛන හරහා කියවීම",
58
72
  "analyzing": "අන්තර්ගතය විශ්ලේෂණය කිරීම",
@@ -43,7 +43,6 @@
43
43
  "close": "Zapri",
44
44
  "expand": "Razširi",
45
45
  "collapse": "Strni",
46
- "thinking": "Razmišljanje...",
47
46
  "askMeAnything": "Vprašaj karkoli",
48
47
  "askMeAnythingDescription": "Poiščite pomoč pri krmarjenju po dokumentaciji, razumevanju konceptov in iskanju odgovorov.",
49
48
  "faq": "Pogosta vprašanja",
@@ -52,7 +51,22 @@
52
51
  "explainWithAi": "Razloži z AI",
53
52
  "toolListPages": "Navedene strani dokumentacije",
54
53
  "toolReadPage": "Preberi",
54
+ "tools": {
55
+ "searching": "Iskanje",
56
+ "searched": "Najdeno",
57
+ "reading": "Branje",
58
+ "read": "Prebrano",
59
+ "finding": "Iskanje",
60
+ "found": "Najdeno",
61
+ "opening": "Odpiranje",
62
+ "opened": "Odprto",
63
+ "generating": "Generiranje",
64
+ "generated": "Generirano",
65
+ "creating": "Ustvarjanje",
66
+ "created": "Ustvarjeno"
67
+ },
55
68
  "loading": {
69
+ "thinking": "Razmišljanje...",
56
70
  "searching": "Iskanje po dokumentaciji",
57
71
  "reading": "Prebiranje dokumentov",
58
72
  "analyzing": "Analiza vsebine",
@@ -43,7 +43,6 @@
43
43
  "close": "Stäng",
44
44
  "expand": "Expandera",
45
45
  "collapse": "Kollapsa",
46
- "thinking": "Funderar...",
47
46
  "askMeAnything": "Fråga vad som helst",
48
47
  "askMeAnythingDescription": "Få hjälp med att navigera i dokumentationen, förstå begrepp och hitta svar.",
49
48
  "faq": "FAQ",
@@ -52,7 +51,22 @@
52
51
  "explainWithAi": "Förklara med AI",
53
52
  "toolListPages": "Listade dokumentationssidor",
54
53
  "toolReadPage": "Läs",
54
+ "tools": {
55
+ "searching": "Söker",
56
+ "searched": "Sökt",
57
+ "reading": "Läser",
58
+ "read": "Läst",
59
+ "finding": "Söker",
60
+ "found": "Hittade",
61
+ "opening": "Öppnar",
62
+ "opened": "Öppnad",
63
+ "generating": "Genererar",
64
+ "generated": "Genererad",
65
+ "creating": "Skapar",
66
+ "created": "Skapad"
67
+ },
55
68
  "loading": {
69
+ "thinking": "Funderar...",
56
70
  "searching": "Söker i dokumentationen",
57
71
  "reading": "Läser igenom dokumenten",
58
72
  "analyzing": "Analysera innehållet",
@@ -43,7 +43,6 @@
43
43
  "close": "Kapat",
44
44
  "expand": "Genişlet",
45
45
  "collapse": "Daralt",
46
- "thinking": "Düşünüyorum...",
47
46
  "askMeAnything": "Her şeyi sor",
48
47
  "askMeAnythingDescription": "Belgelerde gezinme, kavramları anlama ve yanıt bulma konusunda yardım alın.",
49
48
  "faq": "SSS",
@@ -52,7 +51,22 @@
52
51
  "explainWithAi": "Yapay zeka ile açıklayın",
53
52
  "toolListPages": "Listelenen dokümantasyon sayfaları",
54
53
  "toolReadPage": "Oku",
54
+ "tools": {
55
+ "searching": "Aranıyor",
56
+ "searched": "Arandı",
57
+ "reading": "Okunuyor",
58
+ "read": "Okundu",
59
+ "finding": "Aranıyor",
60
+ "found": "Bulundu",
61
+ "opening": "Açılıyor",
62
+ "opened": "Açıldı",
63
+ "generating": "Üretiliyor",
64
+ "generated": "Üretildi",
65
+ "creating": "Oluşturuluyor",
66
+ "created": "Oluşturuldu"
67
+ },
55
68
  "loading": {
69
+ "thinking": "Düşünüyorum...",
56
70
  "searching": "Dokümantasyonda arama",
57
71
  "reading": "Dokümanlar okunuyor",
58
72
  "analyzing": "İçerik analiz ediliyor",
@@ -43,7 +43,6 @@
43
43
  "close": "Закрити",
44
44
  "expand": "Розгорнути",
45
45
  "collapse": "Згорнути",
46
- "thinking": "Думаючи...",
47
46
  "askMeAnything": "Запитайте будь-що",
48
47
  "askMeAnythingDescription": "Отримайте допомогу в навігації документацією, розумінні понять і пошуку відповідей.",
49
48
  "faq": "Поширені запитання",
@@ -52,7 +51,22 @@
52
51
  "explainWithAi": "Поясніть за допомогою ШІ",
53
52
  "toolListPages": "Перелічені сторінки документації",
54
53
  "toolReadPage": "Читати",
54
+ "tools": {
55
+ "searching": "Пошук",
56
+ "searched": "Знайдено",
57
+ "reading": "Читання",
58
+ "read": "Прочитано",
59
+ "finding": "Пошук",
60
+ "found": "Знайдено",
61
+ "opening": "Відкриття",
62
+ "opened": "Відкрито",
63
+ "generating": "Генерація",
64
+ "generated": "Згенеровано",
65
+ "creating": "Створення",
66
+ "created": "Створено"
67
+ },
55
68
  "loading": {
69
+ "thinking": "Думаючи...",
56
70
  "searching": "Пошук документації",
57
71
  "reading": "Читання документів",
58
72
  "analyzing": "Аналіз змісту",
@@ -43,7 +43,6 @@
43
43
  "close": "بند",
44
44
  "expand": "پھیلائیں۔",
45
45
  "collapse": "سمٹنا",
46
- "thinking": "سوچ رہا ہے...",
47
46
  "askMeAnything": "کچھ بھی پوچھو",
48
47
  "askMeAnythingDescription": "دستاویزات کو نیویگیٹ کرنے، تصورات کو سمجھنے اور جوابات تلاش کرنے میں مدد حاصل کریں۔",
49
48
  "faq": "اکثر پوچھے گئے سوالات",
@@ -52,7 +51,22 @@
52
51
  "explainWithAi": "AI کے ساتھ وضاحت کریں۔",
53
52
  "toolListPages": "درج دستاویزات کے صفحات",
54
53
  "toolReadPage": "پڑھیں",
54
+ "tools": {
55
+ "searching": "تلاش جاری",
56
+ "searched": "تلاش مکمل",
57
+ "reading": "پڑھ رہے ہیں",
58
+ "read": "پڑھا",
59
+ "finding": "تلاش جاری",
60
+ "found": "مل گیا",
61
+ "opening": "کھول رہے ہیں",
62
+ "opened": "کھولا",
63
+ "generating": "تیار کر رہے ہیں",
64
+ "generated": "تیار کیا",
65
+ "creating": "بنا رہے ہیں",
66
+ "created": "بنایا"
67
+ },
55
68
  "loading": {
69
+ "thinking": "سوچ رہا ہے...",
56
70
  "searching": "دستاویزات کی تلاش",
57
71
  "reading": "دستاویزات کے ذریعے پڑھنا",
58
72
  "analyzing": "مواد کا تجزیہ",
@@ -43,7 +43,6 @@
43
43
  "close": "Đóng",
44
44
  "expand": "Mở rộng",
45
45
  "collapse": "Thu gọn",
46
- "thinking": "Đang suy nghĩ...",
47
46
  "askMeAnything": "Hỏi bất cứ điều gì",
48
47
  "askMeAnythingDescription": "Nhận trợ giúp điều hướng tài liệu, hiểu các khái niệm và tìm câu trả lời.",
49
48
  "faq": "Câu hỏi thường gặp",
@@ -52,7 +51,22 @@
52
51
  "explainWithAi": "Giải thích bằng AI",
53
52
  "toolListPages": "Các trang tài liệu được liệt kê",
54
53
  "toolReadPage": "Đọc",
54
+ "tools": {
55
+ "searching": "Đang tìm",
56
+ "searched": "Đã tìm",
57
+ "reading": "Đang đọc",
58
+ "read": "Đã đọc",
59
+ "finding": "Đang tìm",
60
+ "found": "Đã tìm thấy",
61
+ "opening": "Đang mở",
62
+ "opened": "Đã mở",
63
+ "generating": "Đang tạo",
64
+ "generated": "Đã tạo",
65
+ "creating": "Đang tạo",
66
+ "created": "Đã tạo"
67
+ },
55
68
  "loading": {
69
+ "thinking": "Đang suy nghĩ...",
56
70
  "searching": "Tìm kiếm tài liệu",
57
71
  "reading": "Đọc qua tài liệu",
58
72
  "analyzing": "Phân tích nội dung",
@@ -43,7 +43,6 @@
43
43
  "close": "关闭",
44
44
  "expand": "展开",
45
45
  "collapse": "崩溃",
46
- "thinking": "想着……",
47
46
  "askMeAnything": "询问任何事情",
48
47
  "askMeAnythingDescription": "获取浏览文档、理解概念和寻找答案的帮助。",
49
48
  "faq": "常见问题解答",
@@ -52,7 +51,22 @@
52
51
  "explainWithAi": "用AI解释",
53
52
  "toolListPages": "列出的文档页面",
54
53
  "toolReadPage": "阅读",
54
+ "tools": {
55
+ "searching": "搜索中",
56
+ "searched": "已搜索",
57
+ "reading": "读取中",
58
+ "read": "已读取",
59
+ "finding": "搜索中",
60
+ "found": "已找到",
61
+ "opening": "打开中",
62
+ "opened": "已打开",
63
+ "generating": "生成中",
64
+ "generated": "已生成",
65
+ "creating": "创建中",
66
+ "created": "已创建"
67
+ },
55
68
  "loading": {
69
+ "thinking": "想着……",
56
70
  "searching": "搜索文档",
57
71
  "reading": "通读文档",
58
72
  "analyzing": "分析内容",
@@ -49,6 +49,18 @@ defineShortcuts(shortcuts)
49
49
  function onEscape() {
50
50
  inputRef.value?.inputRef?.blur()
51
51
  }
52
+
53
+ function onKeydown(event: KeyboardEvent) {
54
+ if (event.key === 'Enter' && !event.shiftKey && !event.altKey && !event.ctrlKey && !event.metaKey) {
55
+ event.preventDefault()
56
+ handleSubmit()
57
+ return
58
+ }
59
+
60
+ if (event.key === 'Escape') {
61
+ onEscape()
62
+ }
63
+ }
52
64
  </script>
53
65
 
54
66
  <template>
@@ -79,8 +91,7 @@ function onEscape() {
79
91
  base: 'bg-default shadow-lg rounded-xl text-base',
80
92
  trailing: 'pe-2',
81
93
  }"
82
- @keydown.enter.exact.prevent="handleSubmit"
83
- @keydown.escape="onEscape"
94
+ @keydown="onKeydown"
84
95
  >
85
96
  <template #trailing>
86
97
  <div class="flex items-center gap-2">
@@ -1,4 +1,8 @@
1
1
  <script setup lang="ts">
2
+ import { useDocusI18n } from '../../../../app/composables/useDocusI18n'
3
+
4
+ const { t } = useDocusI18n()
5
+
2
6
  const size = 4
3
7
  const dotSize = 2
4
8
  const gap = 2
@@ -32,9 +36,14 @@ function nextStep() {
32
36
  }
33
37
  }
34
38
 
35
- const statusMessages = ['Thinking...', 'Searching...', 'Reading...', 'Analyzing...']
39
+ const statusMessages = computed(() => [
40
+ t('assistant.loading.thinking'),
41
+ t('assistant.loading.searching'),
42
+ t('assistant.loading.reading'),
43
+ t('assistant.loading.analyzing'),
44
+ ])
36
45
  const currentIndex = ref(0)
37
- const displayedText = ref(statusMessages[0]!)
46
+ const displayedText = ref(statusMessages.value[0]!)
38
47
  const chars = 'abcdefghijklmnopqrstuvwxyz'
39
48
 
40
49
  function scramble(from: string, to: string) {
@@ -80,8 +89,8 @@ onMounted(() => {
80
89
  matrixInterval = setInterval(nextStep, 120)
81
90
  textInterval = setInterval(() => {
82
91
  const prev = displayedText.value
83
- currentIndex.value = (currentIndex.value + 1) % statusMessages.length
84
- scramble(prev, statusMessages[currentIndex.value]!)
92
+ currentIndex.value = (currentIndex.value + 1) % statusMessages.value.length
93
+ scramble(prev, statusMessages.value[currentIndex.value]!)
85
94
  }, 3500)
86
95
  })
87
96
 
@@ -92,7 +101,7 @@ onUnmounted(() => {
92
101
  </script>
93
102
 
94
103
  <template>
95
- <div class="flex items-center text-xs text-muted overflow-hidden">
104
+ <div class="flex items-center text-sm text-muted overflow-hidden">
96
105
  <div
97
106
  class="shrink-0 mr-2 grid"
98
107
  :style="{
@@ -111,6 +120,6 @@ onUnmounted(() => {
111
120
  />
112
121
  </div>
113
122
 
114
- <UChatShimmer :text="displayedText" class="font-mono tracking-tight" />
123
+ <UChatShimmer :text="displayedText" />
115
124
  </div>
116
125
  </template>
@@ -1,9 +1,10 @@
1
1
  <script setup lang="ts">
2
2
  import type { ToolUIPart, DynamicToolUIPart } from 'ai'
3
- import { DefaultChatTransport, isToolUIPart, isReasoningUIPart, isTextUIPart, getToolName } from 'ai'
3
+ import { DefaultChatTransport, isToolUIPart, isReasoningUIPart, isTextUIPart } from 'ai'
4
4
  import { Chat } from '@ai-sdk/vue'
5
5
  import { isPartStreaming, isToolStreaming } from '@nuxt/ui/utils/ai'
6
6
  import { useDocusI18n } from '../../../../app/composables/useDocusI18n'
7
+ import { getToolText, getToolSuffix, getToolIcon } from '../../../../app/utils/assistantTools'
7
8
  import AssistantComark from './AssistantComark'
8
9
  import AssistantIndicator from './AssistantIndicator.vue'
9
10
 
@@ -22,6 +23,12 @@ const open = computed({
22
23
  },
23
24
  })
24
25
 
26
+ const promptRef = ref<{ textareaRef?: HTMLTextAreaElement } | null>(null)
27
+ watch(open, (value) => {
28
+ if (!value) return
29
+ nextTick(() => promptRef.value?.textareaRef?.focus({ preventScroll: true }))
30
+ })
31
+
25
32
  const displayTitle = computed(() => t('assistant.title'))
26
33
  const displayPlaceholder = computed(() => t('assistant.placeholder'))
27
34
 
@@ -71,54 +78,22 @@ watch(messages, (newMessages) => {
71
78
  const canClear = computed(() => messages.value.length > 0)
72
79
 
73
80
  type ToolPart = ToolUIPart | DynamicToolUIPart
74
- type ToolState = ToolPart['state']
75
-
76
- function getToolMessage(state: ToolState, toolName: string, input: Record<string, string | undefined>) {
77
- const searchVerb = state === 'output-available' ? 'Searched' : 'Searching'
78
- const readVerb = state === 'output-available' ? 'Read' : 'Reading'
79
-
80
- return {
81
- 'list-pages': `${searchVerb} pages`,
82
- 'get-page': `${readVerb} ${input.path || '...'}`,
83
- }[toolName] || `${searchVerb} ${toolName}`
84
- }
85
-
86
- function getToolText(part: ToolPart) {
87
- return getToolMessage(part.state, getToolName(part), (part.input || {}) as Record<string, string | undefined>)
88
- }
89
-
90
- function getToolIcon(part: ToolPart): string {
91
- const toolName = getToolName(part)
92
-
93
- return {
94
- 'get-page': 'i-lucide-file-text',
95
- }[toolName] || 'i-lucide-search'
96
- }
97
81
 
98
82
  function getToolOutput(part: ToolPart): string | undefined {
99
83
  if (part.state !== 'output-available' || !part.output) return undefined
100
84
 
101
85
  const output = part.output as Record<string, unknown>
86
+ const content = (output.content ?? output) as Array<{ text?: string }> | string
102
87
 
103
- if (getToolName(part) === 'list-pages') {
104
- const content = (output.content ?? output) as Array<{ text?: string }> | string
105
- if (typeof content === 'string') return content
106
- return content
107
- ?.map(c => c.text)
108
- .filter(Boolean)
109
- .join('\n') || undefined
88
+ if (typeof content === 'string') {
89
+ return content || undefined
110
90
  }
111
91
 
112
- if (getToolName(part) === 'get-page') {
113
- const content = (output.content ?? output) as Array<{ text?: string }> | string
114
- if (typeof content === 'string') {
115
- return content.length > 500 ? `${content.slice(0, 500)}…` : content
116
- }
117
- const text = content?.map(c => c.text).filter(Boolean).join('\n') || ''
118
- return text.length > 500 ? `${text.slice(0, 500)}…` : text || undefined
92
+ if (Array.isArray(content)) {
93
+ return content.map(c => c.text).filter(Boolean).join('\n') || undefined
119
94
  }
120
95
 
121
- return JSON.stringify(output, null, 2).slice(0, 500)
96
+ return JSON.stringify(output, null, 2)
122
97
  }
123
98
 
124
99
  function onSubmit() {
@@ -214,7 +189,7 @@ defineShortcuts({
214
189
  :status="chat.status"
215
190
  compact
216
191
  class="px-0 gap-2"
217
- :user="{ ui: { container: 'max-w-full' } }"
192
+ :user="{ ui: { container: 'max-w-full pb-3' } }"
218
193
  >
219
194
  <template #indicator>
220
195
  <AssistantIndicator />
@@ -230,6 +205,7 @@ defineShortcuts({
230
205
  :text="part.text"
231
206
  :streaming="isPartStreaming(part)"
232
207
  icon="i-lucide-brain"
208
+ chevron="leading"
233
209
  >
234
210
  <AssistantComark
235
211
  :markdown="part.text"
@@ -253,14 +229,15 @@ defineShortcuts({
253
229
 
254
230
  <UChatTool
255
231
  v-else-if="isToolUIPart(part)"
256
- :text="getToolText(part)"
232
+ :text="getToolText(part, t)"
233
+ :suffix="getToolSuffix(part)"
257
234
  :icon="getToolIcon(part)"
258
235
  :streaming="isToolStreaming(part)"
259
236
  chevron="leading"
260
237
  >
261
238
  <pre
262
239
  v-if="getToolOutput(part)"
263
- class="text-xs text-dimmed whitespace-pre-wrap"
240
+ class="text-xs text-muted whitespace-pre-wrap break-all rounded-md border border-muted bg-muted p-2 max-h-64 overflow-y-auto"
264
241
  v-text="getToolOutput(part)"
265
242
  />
266
243
  </UChatTool>
@@ -302,6 +279,7 @@ defineShortcuts({
302
279
 
303
280
  <template #footer>
304
281
  <UChatPrompt
282
+ ref="promptRef"
305
283
  v-model="input"
306
284
  :error="chat.error"
307
285
  :placeholder="displayPlaceholder"
@@ -1,4 +1,4 @@
1
- import { streamText, convertToModelMessages } from 'ai'
1
+ import { streamText, convertToModelMessages, stepCountIs, smoothStream } from 'ai'
2
2
  import type { ToolSet } from 'ai'
3
3
  import { createMCPClient } from '@ai-sdk/mcp'
4
4
  import type { H3Event } from 'h3'
@@ -22,19 +22,6 @@ function createLocalFetch(event: H3Event): typeof fetch {
22
22
  }
23
23
  }
24
24
 
25
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
26
- function stopWhenResponseComplete({ steps }: { steps: { text?: string, toolCalls?: unknown[] }[] }): boolean {
27
- const lastStep = steps.at(-1)
28
- if (!lastStep) return false
29
-
30
- const hasText = Boolean(lastStep.text && lastStep.text.trim().length > 0)
31
- const hasNoToolCalls = !lastStep.toolCalls || lastStep.toolCalls.length === 0
32
-
33
- if (hasText && hasNoToolCalls) return true
34
-
35
- return steps.length >= MAX_STEPS
36
- }
37
-
38
25
  function getSystemPrompt(siteName: string) {
39
26
  return `You are the documentation assistant for ${siteName}. Help users navigate and understand the project documentation.
40
27
 
@@ -115,13 +102,24 @@ export default defineEventHandler(async (event) => {
115
102
 
116
103
  return streamText({
117
104
  model: config.assistant.model,
118
- maxOutputTokens: 4000,
105
+ maxOutputTokens: 8000,
119
106
  maxRetries: 2,
120
107
  abortSignal: abortController.signal,
121
- stopWhen: stopWhenResponseComplete,
108
+ stopWhen: stepCountIs(MAX_STEPS),
109
+ // On the last allowed step, disable tools so the model is forced to
110
+ // produce a final text answer instead of stopping mid tool-calling.
111
+ prepareStep: ({ stepNumber }) => {
112
+ return stepNumber >= MAX_STEPS - 1 ? { toolChoice: 'none' } : {}
113
+ },
114
+ providerOptions: {
115
+ gateway: {
116
+ caching: 'auto',
117
+ },
118
+ },
122
119
  system: getSystemPrompt(siteName),
123
120
  messages: await convertToModelMessages(messages),
124
121
  tools: mcpTools as ToolSet,
122
+ experimental_transform: smoothStream(),
125
123
  onFinish: closeMcp,
126
124
  onAbort: closeMcp,
127
125
  onError: closeMcp,
package/modules/css.ts CHANGED
@@ -14,6 +14,7 @@ export default defineNuxtModule({
14
14
  const resolver = createResolver(import.meta.url)
15
15
 
16
16
  const contentDir = resolve(nuxt.options.rootDir, 'content')
17
+ const appConfigPath = resolve(nuxt.options.srcDir, 'app.config.ts')
17
18
  const uiPath = resolveModulePath('@nuxt/ui', { from: import.meta.url, conditions: ['style'] })
18
19
  const tailwindPath = resolveModulePath('tailwindcss', { from: import.meta.url, conditions: ['style'] })
19
20
  const layerDir = resolver.resolve('../app')
@@ -34,13 +35,14 @@ export default defineNuxtModule({
34
35
 
35
36
  const cssTemplate = addTemplate({
36
37
  filename: 'docus.css',
38
+ write: true,
37
39
  getContents: () => {
38
40
  return `@import ${JSON.stringify(tailwindPath)};
39
41
  @import ${JSON.stringify(uiPath)};
40
42
 
41
43
  @source "${contentDir.replace(/\\/g, '/')}/**/*";
42
44
  @source "${layerDir.replace(/\\/g, '/')}/**/*";
43
- @source "../../app.config.ts";
45
+ @source "${appConfigPath.replace(/\\/g, '/')}";
44
46
  @source "${assistantDir.replace(/\\/g, '/')}/**/*";
45
47
 
46
48
  html.dark .shiki span {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "docus",
3
3
  "description": "Nuxt layer for Docus documentation theme",
4
- "version": "5.12.1",
4
+ "version": "5.12.3",
5
5
  "type": "module",
6
6
  "main": "./nuxt.config.ts",
7
7
  "repository": {
@@ -23,39 +23,39 @@
23
23
  "README.md"
24
24
  ],
25
25
  "dependencies": {
26
- "@ai-sdk/gateway": "^3.0.120",
27
- "@ai-sdk/mcp": "^1.0.43",
28
- "@ai-sdk/vue": "^3.0.191",
26
+ "@ai-sdk/gateway": "^3.0.133",
27
+ "@ai-sdk/mcp": "^1.0.52",
28
+ "@ai-sdk/vue": "^3.0.208",
29
29
  "@comark/vue": "^0.4.0",
30
- "@iconify-json/lucide": "^1.2.111",
31
- "@iconify-json/simple-icons": "^1.2.85",
32
- "@iconify-json/vscode-icons": "^1.2.53",
30
+ "@iconify-json/lucide": "^1.2.114",
31
+ "@iconify-json/simple-icons": "^1.2.87",
32
+ "@iconify-json/vscode-icons": "^1.2.59",
33
33
  "@nuxt/content": "^3.14.0",
34
34
  "@nuxt/image": "^2.0.0",
35
- "@nuxt/kit": "^4.4.7",
36
- "@nuxt/ui": "^4.8.1",
35
+ "@nuxt/kit": "^4.4.8",
36
+ "@nuxt/ui": "^4.9.0",
37
37
  "@nuxtjs/i18n": "^10.4.0",
38
38
  "@nuxtjs/mcp-toolkit": "^0.17.2",
39
39
  "@nuxtjs/mdc": "^0.22.0",
40
- "@nuxtjs/robots": "^6.0.9",
40
+ "@nuxtjs/robots": "^6.1.1",
41
41
  "@shikijs/core": "^4.2.0",
42
42
  "@shikijs/engine-javascript": "^4.2.0",
43
43
  "@shikijs/langs": "^4.2.0",
44
44
  "@shikijs/stream": "^4.2.0",
45
45
  "@shikijs/themes": "^4.2.0",
46
- "@takumi-rs/core": "^1.6.0",
46
+ "@takumi-rs/core": "^1.8.7",
47
47
  "@vueuse/core": "^14.3.0",
48
- "ai": "^6.0.191",
48
+ "ai": "^6.0.208",
49
49
  "defu": "^6.1.7",
50
- "exsolve": "^1.0.8",
50
+ "exsolve": "^1.1.0",
51
51
  "git-url-parse": "^16.1.0",
52
- "motion-v": "^2.2.1",
52
+ "motion-v": "^2.3.0",
53
53
  "nuxt-llms": "^0.2.0",
54
- "nuxt-og-image": "^6.5.1",
54
+ "nuxt-og-image": "~6.6.0",
55
55
  "pathe": "^2.0.3",
56
56
  "pkg-types": "^2.3.1",
57
57
  "scule": "^1.3.0",
58
- "tailwindcss": "^4.3.0",
58
+ "tailwindcss": "^4.3.1",
59
59
  "ufo": "^1.6.4",
60
60
  "yaml": "^2.9.0",
61
61
  "zod": "^4.4.3",