import type { UIMessage } from 'ai'import { useAppConfig, useRuntimeConfig } from '#imports'import { createSharedComposable, useLocalStorage } from '@vueuse/core'import { computed, ref, watch } from 'vue'import type { FaqCategory, FaqQuestions, LocalizedFaqQuestions } from '../types' function normalizeFaqQuestions(questions: FaqQuestions): FaqCategory[] { if (!questions || (Array.isArray(questions) && questions.length === 0)) { return [] } if (typeof questions[0] === 'string') { return [{ category: 'Questions', items: questions as string[], }] } return questions as FaqCategory[]} export const useAssistant = createSharedComposable(() => { const config = useRuntimeConfig() const appConfig = useAppConfig() const assistantRuntimeConfig = config.public.assistant as { enabled?: boolean } | undefined const assistantConfig = appConfig.assistant as { faqQuestions?: FaqQuestions | LocalizedFaqQuestions } | undefined const docusRuntimeConfig = appConfig.docus as { locale?: string } | undefined const isEnabled = computed(() => assistantRuntimeConfig?.enabled ?? false) const storageOpen = useLocalStorage('assistant-open', false) const messages = useLocalStorage<UIMessage[]>('assistant-messages', []) const isOpen = ref(false) const isStudioExpanded = ref(false) onNuxtReady(() => { nextTick(() => { isOpen.value = storageOpen.value }) isStudioExpanded.value = document.body.hasAttribute('data-expand-sidebar') const observer = new MutationObserver(() => { isStudioExpanded.value = document.body.hasAttribute('data-expand-sidebar') }) observer.observe(document.body, { attributes: true, attributeFilter: ['data-expand-sidebar'] }) }) watch(isOpen, (value) => { storageOpen.value = value }) const faqQuestions = computed<FaqCategory[]>(() => { const faqConfig = assistantConfig?.faqQuestions if (!faqConfig) return [] if (!Array.isArray(faqConfig)) { const localizedConfig = faqConfig as LocalizedFaqQuestions const currentLocale = docusRuntimeConfig?.locale || 'en' const defaultLocale = config.public.i18n?.defaultLocale || 'en' const questions = localizedConfig[currentLocale] || localizedConfig[defaultLocale] || Object.values(localizedConfig)[0] return normalizeFaqQuestions(questions || []) } return normalizeFaqQuestions(faqConfig) }) function open(initialMessage?: string, clearPrevious = false) { if (isStudioExpanded.value) return if (clearPrevious) { messages.value = [] } if (initialMessage) { messages.value = [...messages.value, { id: String(Date.now()), role: 'user' as const, parts: [{ type: 'text' as const, text: initialMessage }], }] } isOpen.value = true } function close() { isOpen.value = false } function toggle() { if (isStudioExpanded.value) return isOpen.value = !isOpen.value } return { isEnabled, isOpen, isStudioExpanded, messages, faqQuestions, open, close, toggle, }})
import type { UIMessage } from 'ai'
import { useAppConfig, useRuntimeConfig } from '#imports'
import { createSharedComposable, useLocalStorage } from '@vueuse/core'
import { computed, ref, watch } from 'vue'
import type { FaqCategory, FaqQuestions, LocalizedFaqQuestions } from '../types'
function normalizeFaqQuestions(questions: FaqQuestions): FaqCategory[] {
if (!questions || (Array.isArray(questions) && questions.length === 0)) {
return []
}
if (typeof questions[0] === 'string') {
return [{
category: 'Questions',
items: questions as string[],
}]
}
return questions as FaqCategory[]
}
export const useAssistant = createSharedComposable(() => {
const config = useRuntimeConfig()
const appConfig = useAppConfig()
const assistantRuntimeConfig = config.public.assistant as { enabled?: boolean } | undefined
const assistantConfig = appConfig.assistant as { faqQuestions?: FaqQuestions | LocalizedFaqQuestions } | undefined
const docusRuntimeConfig = appConfig.docus as { locale?: string } | undefined
const isEnabled = computed(() => assistantRuntimeConfig?.enabled ?? false)
const storageOpen = useLocalStorage('assistant-open', false)
const messages = useLocalStorage<UIMessage[]>('assistant-messages', [])
const isOpen = ref(false)
const isStudioExpanded = ref(false)
onNuxtReady(() => {
nextTick(() => {
isOpen.value = storageOpen.value
})
isStudioExpanded.value = document.body.hasAttribute('data-expand-sidebar')
const observer = new MutationObserver(() => {
isStudioExpanded.value = document.body.hasAttribute('data-expand-sidebar')
})
observer.observe(document.body, { attributes: true, attributeFilter: ['data-expand-sidebar'] })
})
watch(isOpen, (value) => {
storageOpen.value = value
})
const faqQuestions = computed<FaqCategory[]>(() => {
const faqConfig = assistantConfig?.faqQuestions
if (!faqConfig) return []
if (!Array.isArray(faqConfig)) {
const localizedConfig = faqConfig as LocalizedFaqQuestions
const currentLocale = docusRuntimeConfig?.locale || 'en'
const defaultLocale = config.public.i18n?.defaultLocale || 'en'
const questions = localizedConfig[currentLocale]
|| localizedConfig[defaultLocale]
|| Object.values(localizedConfig)[0]
return normalizeFaqQuestions(questions || [])
}
return normalizeFaqQuestions(faqConfig)
})
function open(initialMessage?: string, clearPrevious = false) {
if (isStudioExpanded.value) return
if (clearPrevious) {
messages.value = []
}
if (initialMessage) {
messages.value = [...messages.value, {
id: String(Date.now()),
role: 'user' as const,
parts: [{ type: 'text' as const, text: initialMessage }],
}]
}
isOpen.value = true
}
function close() {
isOpen.value = false
}
function toggle() {
if (isStudioExpanded.value) return
isOpen.value = !isOpen.value
}
return {
isEnabled,
isOpen,
isStudioExpanded,
messages,
faqQuestions,
open,
close,
toggle,
}
})