From 450948646a2ce19032dba9511f57397eb128b41b Mon Sep 17 00:00:00 2001 From: Benjamin Canac Date: Fri, 9 May 2025 18:26:28 +0200 Subject: [PATCH 1/3] docs(app): implement AI search --- docs/app/app.vue | 20 +-- docs/app/components/Search.client.vue | 199 ++++++++++++++++++++++++ docs/app/components/prose/PreStream.vue | 44 ++++++ docs/app/composables/useHighlighter.ts | 21 +++ docs/app/composables/useSearchLinks.ts | 66 -------- docs/app/error.vue | 20 +-- docs/package.json | 1 + docs/server/api/chat.ts | 27 +++- pnpm-lock.yaml | 21 +++ 9 files changed, 311 insertions(+), 108 deletions(-) create mode 100644 docs/app/components/Search.client.vue create mode 100644 docs/app/components/prose/PreStream.vue create mode 100644 docs/app/composables/useHighlighter.ts delete mode 100644 docs/app/composables/useSearchLinks.ts diff --git a/docs/app/app.vue b/docs/app/app.vue index be2d15ccd5..ea4542b4db 100644 --- a/docs/app/app.vue +++ b/docs/app/app.vue @@ -12,7 +12,6 @@ const { data: files } = useLazyAsyncData('search', () => queryCollectionSearchSe }) const links = useLinks() -const searchLinks = useSearchLinks() const color = computed(() => colorMode.value === 'dark' ? (colors as any)[appConfig.ui.colors.neutral][900] : 'white') const radius = computed(() => `:root { --ui-radius: ${appConfig.theme.radius}rem; }`) const blackAsPrimary = computed(() => appConfig.theme.blackAsPrimary ? `:root { --ui-primary: black; } .dark { --ui-primary: white; }` : ':root {}') @@ -42,7 +41,6 @@ useServerSeoMeta({ useFaviconFromTheme() -const { frameworks, modules } = useSharedData() const { mappedNavigation, filteredNavigation } = useContentNavigation(navigation) provide('navigation', mappedNavigation) @@ -65,23 +63,7 @@ provide('navigation', mappedNavigation) diff --git a/docs/app/components/Search.client.vue b/docs/app/components/Search.client.vue new file mode 100644 index 0000000000..8cea9fdf9e --- /dev/null +++ b/docs/app/components/Search.client.vue @@ -0,0 +1,199 @@ + + + diff --git a/docs/app/components/prose/PreStream.vue b/docs/app/components/prose/PreStream.vue new file mode 100644 index 0000000000..63b224e06e --- /dev/null +++ b/docs/app/components/prose/PreStream.vue @@ -0,0 +1,44 @@ + + + diff --git a/docs/app/composables/useHighlighter.ts b/docs/app/composables/useHighlighter.ts new file mode 100644 index 0000000000..dd66e5f324 --- /dev/null +++ b/docs/app/composables/useHighlighter.ts @@ -0,0 +1,21 @@ +import { createHighlighter, type HighlighterGeneric } from 'shiki' +import { createJavaScriptRegexEngine } from 'shiki/engine-javascript.mjs' + +let highlighter: HighlighterGeneric | null = null + +let promise: Promise> | null = null + +export const useHighlighter = async () => { + if (!promise) { + promise = createHighlighter({ + langs: ['vue', 'js', 'ts', 'css', 'html', 'json', 'yaml', 'markdown', 'bash'], + themes: ['material-theme-palenight', 'material-theme-lighter'], + engine: createJavaScriptRegexEngine() + }) + } + if (!highlighter) { + highlighter = await promise + } + + return highlighter +} diff --git a/docs/app/composables/useSearchLinks.ts b/docs/app/composables/useSearchLinks.ts deleted file mode 100644 index 995046dad2..0000000000 --- a/docs/app/composables/useSearchLinks.ts +++ /dev/null @@ -1,66 +0,0 @@ -export function useSearchLinks() { - return [{ - label: 'Docs', - icon: 'i-lucide-square-play', - to: '/getting-started' - }, { - label: 'Components', - icon: 'i-lucide-square-code', - to: '/components' - }, { - icon: 'i-lucide-sparkles', - label: 'Pro > Features', - description: 'A collection of premium Vue components.', - to: '/pro' - }, { - icon: 'i-lucide-credit-card', - label: 'Pro > Pricing', - description: 'Free in development, buy when ready to launch.', - to: '/pro/pricing' - }, { - icon: 'i-lucide-panels-top-left', - label: 'Pro > Templates', - description: 'Official templates made with Nuxt UI Pro.', - to: '/pro/templates' - }, { - icon: 'i-lucide-circle-check', - label: 'Pro > Activate', - description: 'Enable Nuxt UI Pro in your production projects.', - to: '/pro/activate' - }, { - label: 'Figma', - icon: 'i-simple-icons-figma', - to: '/figma' - }, { - icon: 'i-lucide-presentation', - label: 'Community > Showcase', - description: 'Check out some of the amazing projects built with Nuxt UI.', - to: '/showcase' - }, { - label: 'Community > Contribution', - description: 'A comprehensive guide on contributing to Nuxt UI, including project structure, development workflow, and best practices.', - icon: 'i-lucide-git-pull-request-arrow', - to: '/getting-started/contribution' - }, { - label: 'Community > Roadmap', - description: 'Track our development progress in real-time.', - icon: 'i-lucide-map', - to: '/roadmap' - }, { - label: 'Community > Devtools', - description: 'Integrate Nuxt UI with Nuxt Devtools with Compodium.', - icon: 'i-lucide-code', - to: 'https://github.com/romhml/compodium', - target: '_blank' - }, { - label: 'Community > Team', - description: 'Meet the team behind Nuxt UI.', - icon: 'i-lucide-users', - to: '/team' - }, { - label: 'Releases', - icon: 'i-lucide-rocket', - to: 'https://github.com/nuxt/ui/releases', - target: '_blank' - }] -} diff --git a/docs/app/error.vue b/docs/app/error.vue index 644eaf74ef..8e908141ff 100644 --- a/docs/app/error.vue +++ b/docs/app/error.vue @@ -15,7 +15,6 @@ const { data: files } = useLazyAsyncData('search', () => queryCollectionSearchSe }) const links = useLinks() -const searchLinks = useSearchLinks() const color = computed(() => colorMode.value === 'dark' ? (colors as any)[appConfig.ui.colors.neutral][900] : 'white') const radius = computed(() => `:root { --ui-radius: ${appConfig.theme.radius}rem; }`) const blackAsPrimary = computed(() => appConfig.theme.blackAsPrimary ? `:root { --ui-primary: black; } .dark { --ui-primary: white; }` : ':root {}') @@ -49,7 +48,6 @@ useServerSeoMeta({ useFaviconFromTheme() -const { frameworks, modules } = useSharedData() const { mappedNavigation, filteredNavigation } = useContentNavigation(navigation) provide('navigation', mappedNavigation) @@ -67,22 +65,6 @@ provide('navigation', mappedNavigation)