diff --git a/.github/workflows/sync-orama.yml b/.github/workflows/sync-orama.yml index 7de100df4eee6..2179e77f437e1 100644 --- a/.github/workflows/sync-orama.yml +++ b/.github/workflows/sync-orama.yml @@ -43,5 +43,6 @@ jobs: run: node --run sync-orama env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - ORAMA_INDEX_ID: ${{ github.event_name == 'push' && secrets.ORAMA_PRODUCTION_INDEX_ID || secrets.ORAMA_INDEX_ID }} - ORAMA_SECRET_KEY: ${{ github.event_name == 'push' && secrets.ORAMA_PRODUCTION_SECRET_KEY || secrets.ORAMA_SECRET_KEY }} + NEW_ORAMA_PROJECT_ID: ${{ github.event_name == 'push' && secrets.NEW_ORAMA_PRODUCTION_PROJECT_ID || secrets.NEW_ORAMA_PROJECT_ID }} + NEW_ORAMA_PRIVATE_API_KEY: ${{ github.event_name == 'push' && secrets.NEW_ORAMA_PRODUCTION_PRIVATE_API_KEY || secrets.NEW_ORAMA_PRIVATE_API_KEY }} + NEW_ORAMA_DATASOURCE_ID: ${{ github.event_name == 'push' && secrets.NEW_ORAMA_PRODUCTION_DATASOURCE_ID || secrets.NEW_ORAMA_DATASOURCE_ID }} diff --git a/.gitignore b/.gitignore index 6dbefc3ed5566..3e5db9342a1f8 100644 --- a/.gitignore +++ b/.gitignore @@ -48,4 +48,7 @@ test-results playwright-report ## MacOS Ignored Files -.DS_Store \ No newline at end of file +.DS_Store + +## Other Files +.env diff --git a/apps/site/components/Common/Search/index.tsx b/apps/site/components/Common/Search/index.tsx deleted file mode 100644 index 0c12664b61826..0000000000000 --- a/apps/site/components/Common/Search/index.tsx +++ /dev/null @@ -1,145 +0,0 @@ -'use client'; - -import { OramaSearchBox, OramaSearchButton } from '@orama/react-components'; -import { useTranslations, useLocale } from 'next-intl'; -import { useTheme } from 'next-themes'; -import type { FC } from 'react'; - -import { useRouter } from '#site/navigation.mjs'; -import { - ORAMA_CLOUD_ENDPOINT, - ORAMA_CLOUD_API_KEY, - DEFAULT_ORAMA_QUERY_PARAMS, - DEFAULT_ORAMA_SUGGESTIONS, - BASE_URL, -} from '#site/next.constants.mjs'; - -type ResultMapDescription = { - path: string; - pageSectionTitle: string; -}; - -type ResultMapPath = { path: string; siteSection: string }; - -import { themeConfig, translationKeys } from './utils'; - -const uppercaseFirst = (word: string) => - word.charAt(0).toUpperCase() + word.slice(1); - -const getFormattedPath = (path: string, title: string) => - `${path - .replace(/#.+$/, '') - .split('/') - .map(element => element.replaceAll('-', ' ')) - .map(element => uppercaseFirst(element)) - .filter(Boolean) - .join(' > ')} — ${title}`; - -const SearchButton: FC = () => { - const { resolvedTheme } = useTheme(); - const t = useTranslations(); - const locale = useLocale(); - const colorScheme = resolvedTheme as 'light' | 'dark'; - const router = useRouter(); - - const sourceMap = { - title: 'pageSectionTitle', - description: 'formattedPath', - path: 'path', - }; - - const resultMap = { - ...sourceMap, - description: ({ path, pageSectionTitle }: ResultMapDescription) => - getFormattedPath(path, pageSectionTitle), - path: ({ path, siteSection }: ResultMapPath) => - siteSection.toLowerCase() === 'docs' ? `/${path}` : `/${locale}/${path}`, - section: 'siteSection', - }; - - return ( - <> - - {t('components.search.searchPlaceholder')} - - - [key, t(`components.search.${key}`)]) - )} - searchParams={DEFAULT_ORAMA_QUERY_PARAMS} - suggestions={DEFAULT_ORAMA_SUGGESTIONS} - chatMarkdownLinkHref={({ href }) => { - if (!href) { - return href; - } - - const baseURLObject = new URL(BASE_URL); - const baseURLHostName = baseURLObject.hostname; - - const searchBoxURLObject = new URL(href); - const searchBoxURLHostName = searchBoxURLObject.hostname; - const serachBoxURLPathName = searchBoxURLObject.pathname; - - // We do not want to add the locale to the url for external links and docs links - if ( - baseURLHostName !== searchBoxURLHostName || - serachBoxURLPathName.startsWith('/docs/') - ) { - return href; - } - - const URLWithLocale = new URL( - `${locale}${searchBoxURLObject.pathname}`, - searchBoxURLObject.origin - ); - - return URLWithLocale.href; - }} - onAnswerSourceClick={event => { - event.preventDefault(); - - const baseURLObject = new URL(BASE_URL); - - const { path } = event.detail.source; - - const finalPath = path.startsWith('docs/') - ? path - : `${locale}/${path}`; - - const finalURL = new URL(finalPath, baseURLObject); - - window.open(finalURL, '_blank'); - }} - onSearchResultClick={event => { - event.preventDefault(); - - const fullURLObject = new URL(event.detail.result.path, BASE_URL); - - // result.path already contains LOCALE. Locale is set to undefined here so router does not add it once again. - router.push(fullURLObject.href, { locale: undefined }); - }} - /> - > - ); -}; - -export default SearchButton; diff --git a/apps/site/components/Common/Search/utils.ts b/apps/site/components/Common/Search/utils.ts deleted file mode 100644 index dca1281ad7154..0000000000000 --- a/apps/site/components/Common/Search/utils.ts +++ /dev/null @@ -1,60 +0,0 @@ -export const themeConfig = { - typography: { - '--font-primary': 'var(--font-open-sans)', - }, - colors: { - light: { - '--text-color-primary': 'var(--color-neutral-900)', - '--text-color-accent': 'var(--color-green-600)', - '--background-color-secondary': 'var(--color-neutral-100)', - '--background-color-tertiary': 'var(--color-neutral-300)', - '--border-color-accent': 'var(--color-green-600)', - '--border-color-primary': 'var(--color-neutral-200)', - '--border-color-tertiary': 'var(--color-green-700)', - '--button-background-color-primary': 'var(--color-green-600)', - '--button-background-color-secondary': 'var(--color-white)', - '--button-background-color-secondary-hover': 'var(--color-neutral-100)', - '--button-border-color-secondary': 'var(--color-neutral-300)', - '--button-text-color-secondary': 'var(--color-neutral-900)', - '--chat-button-border-color-gradientThree': 'var(--color-green-400)', - '--chat-button-border-color-gradientFour': 'var(--color-green-700)', - '--chat-button-background-color-gradientOne': 'var(--color-green-600)', - '--chat-button-background-color-gradientTwo': 'var(--color-green-300)', - }, - dark: { - '--text-color-primary': 'var(--color-neutral-100)', - '--text-color-accent': 'var(--color-green-400)', - '--background-color-secondary': 'var(--color-neutral-950)', - '--background-color-tertiary': 'var(--color-neutral-900)', - '--border-color-accent': 'var(--color-green-400)', - '--border-color-primary': 'var(--color-neutral-900)', - '--border-color-tertiary': 'var(--color-green-300)', - '--button-background-color-primary': 'var(--color-green-400)', - '--button-background-color-secondary': 'var(--color-neutral-950)', - '--button-background-color-secondary-hover': 'var(--color-neutral-900)', - '--button-border-color-secondary': 'var(--color-neutral-900)', - '--button-text-color-secondary': 'var(--color-neutral-200)', - '--chat-button-border-color-gradientThree': 'var(--color-green-400)', - '--chat-button-border-color-gradientFour': 'var(--color-green-700)', - '--chat-button-background-color-gradientOne': 'var(--color-green-400)', - '--chat-button-background-color-gradientTwo': 'var(--color-green-800)', - }, - }, -}; - -export const translationKeys = [ - 'searchPlaceholder', - 'chatPlaceholder', - 'noResultsFoundFor', - 'suggestions', - 'seeAll', - 'addMore', - 'clearChat', - 'errorMessage', - 'disclaimer', - 'startYourSearch', - 'initErrorSearch', - 'initErrorChat', - 'chatButtonLabel', - 'searchButtonLabel', -] as const; diff --git a/apps/site/components/Common/Searchbox/ChatActions/index.module.css b/apps/site/components/Common/Searchbox/ChatActions/index.module.css new file mode 100644 index 0000000000000..a6081326ce8f9 --- /dev/null +++ b/apps/site/components/Common/Searchbox/ChatActions/index.module.css @@ -0,0 +1,44 @@ +@reference "../../../../styles/index.css"; + +.chatActionsContainer { + @apply flex + items-center + justify-end; +} + +.chatActionsList { + @apply flex + list-none + items-center + gap-2 + p-0; +} + +.chatAction { + @apply cursor-pointer + rounded-full + p-2 + text-neutral-800 + duration-300 + hover:bg-neutral-300 + focus:bg-neutral-300 + focus:outline-none + motion-safe:transition-colors + dark:text-neutral-400 + dark:hover:bg-neutral-900 + dark:focus:bg-neutral-900; + + svg { + @apply size-4; + } +} + +.chatActionIconSelected { + @apply text-green-600 + dark:text-green-400; +} + +.chatActionDisaliked { + @apply text-neutral-900 + dark:text-neutral-800; +} diff --git a/apps/site/components/Common/Searchbox/ChatActions/index.tsx b/apps/site/components/Common/Searchbox/ChatActions/index.tsx new file mode 100644 index 0000000000000..79f25bfd0e840 --- /dev/null +++ b/apps/site/components/Common/Searchbox/ChatActions/index.tsx @@ -0,0 +1,70 @@ +'use client'; + +import { + DocumentCheckIcon, + ClipboardIcon, + ArrowPathIcon, + HandThumbDownIcon, +} from '@heroicons/react/24/solid'; +import type { Interaction } from '@orama/core'; +import { ChatInteractions } from '@orama/ui/components'; +import classNames from 'classnames'; +import type { FC } from 'react'; +import { useState } from 'react'; + +import styles from './index.module.css'; + +type ChatActionsProps = { + interaction: Interaction; +}; + +export const ChatActions: FC = ({ interaction }) => { + const [isDisliked, setIsDisliked] = useState(false); + + const dislikeMessage = () => setIsDisliked(!isDisliked); + + if (!interaction.response) { + return null; + } + + return ( + + + + + + + + + + {(copied: boolean) => + copied ? ( + + ) : ( + + ) + } + + + {!interaction.loading && ( + + + + + + )} + + + ); +}; diff --git a/apps/site/components/Common/Searchbox/ChatInput/index.module.css b/apps/site/components/Common/Searchbox/ChatInput/index.module.css new file mode 100644 index 0000000000000..3cb7979d6f233 --- /dev/null +++ b/apps/site/components/Common/Searchbox/ChatInput/index.module.css @@ -0,0 +1,99 @@ +@reference "../../../../styles/index.css"; + +.textareaContainer { + @apply px-1; +} + +.textareaWrapper { + @apply flex + items-center + rounded-2xl + border + border-neutral-300 + bg-neutral-100 + py-2 + pl-3 + pr-1 + dark:border-neutral-900 + dark:bg-neutral-950; +} + +.textareaField { + @apply flex-1 + border-0 + bg-transparent + text-neutral-900 + focus:outline-none + dark:text-neutral-200; +} + +.textareaButton { + @apply cursor-pointer + rounded-xl + bg-green-600 + p-2 + text-white + duration-300 + focus:bg-green-600/75 + focus:outline-none + disabled:cursor-not-allowed + disabled:bg-neutral-200/60 + disabled:text-neutral-800 + motion-safe:transition-colors + dark:bg-green-400 + dark:text-neutral-400 + focus:dark:bg-green-400/75 + disabled:dark:bg-neutral-900/60; + + svg { + @apply size-4; + } +} + +.textareaFooter { + @apply pt-1 + text-center + text-xs + text-neutral-800 + sm:text-sm + dark:text-neutral-500; +} + +.suggestionsWrapper { + @apply mb-4 + flex + items-center + gap-2 + overflow-x-auto + px-1 + text-sm + lg:justify-center; + + &::-webkit-scrollbar { + @apply hidden; + } +} + +.suggestionsItem { + @apply flex + size-max + cursor-pointer + whitespace-nowrap + rounded-full + border + border-neutral-300 + bg-neutral-200 + px-3 + py-1 + text-neutral-900 + duration-300 + hover:bg-neutral-300 + focus:bg-neutral-300 + focus:outline-none + motion-safe:transition-colors + dark:border-neutral-900 + dark:bg-neutral-950 + dark:text-neutral-200 + dark:hover:bg-neutral-900 + dark:focus:bg-neutral-900; +} diff --git a/apps/site/components/Common/Searchbox/ChatInput/index.tsx b/apps/site/components/Common/Searchbox/ChatInput/index.tsx new file mode 100644 index 0000000000000..013d86518b611 --- /dev/null +++ b/apps/site/components/Common/Searchbox/ChatInput/index.tsx @@ -0,0 +1,77 @@ +'use client'; + +import { PaperAirplaneIcon } from '@heroicons/react/20/solid'; +import { PauseCircleIcon } from '@heroicons/react/24/solid'; +import { PromptTextArea, Suggestions } from '@orama/ui/components'; +import { useChat } from '@orama/ui/hooks'; +import { useTranslations } from 'next-intl'; +import type { FC } from 'react'; +import { useEffect, useRef } from 'react'; + +import styles from './index.module.css'; + +export const ChatInput: FC = () => { + const t = useTranslations(); + const { + context: { interactions }, + } = useChat(); + const textareaRef = useRef(null); + + const suggestions = [ + t('components.search.suggestionOne'), + t('components.search.suggestionTwo'), + t('components.search.suggestionThree'), + ]; + + const hasInteractions = !!interactions?.length; + + useEffect(() => { + const timeoutId = setTimeout(() => { + textareaRef.current?.focus(); + }, 100); + + return () => { + clearTimeout(timeoutId); + }; + }, []); + + return ( + <> + {!hasInteractions && ( + + {suggestions.map(suggestion => ( + + {suggestion} + + ))} + + )} + + + + } + className={styles.textareaButton} + > + + + + + {t('components.search.disclaimer')} + + + > + ); +}; diff --git a/apps/site/components/Common/Searchbox/ChatInteractions/index.module.css b/apps/site/components/Common/Searchbox/ChatInteractions/index.module.css new file mode 100644 index 0000000000000..2bb4384ee51f5 --- /dev/null +++ b/apps/site/components/Common/Searchbox/ChatInteractions/index.module.css @@ -0,0 +1,64 @@ +@reference "../../../../styles/index.css"; + +.chatInteractionsContainer { + @apply relative + mb-6 + flex + h-full + flex-1 + flex-col + items-start + overflow-auto + px-1; + + &::-webkit-scrollbar { + @apply size-1.5; + } + + &::-webkit-scrollbar-track { + @apply rounded-md + bg-transparent; + } + + &::-webkit-scrollbar-thumb { + @apply rounded-md + bg-neutral-900; + } +} + +.chatInteractionsWrapper { + @apply flex + w-full + flex-wrap + gap-6; + + > div { + @apply w-full; + } +} + +.scrollDownButton { + @apply absolute + bottom-36 + left-1/2 + inline-flex + -translate-x-1/2 + items-center + justify-center + rounded-xl + bg-neutral-200 + p-2 + text-neutral-900 + duration-300 + focus:bg-neutral-300 + focus:outline-none + motion-safe:transition-colors + lg:bottom-28 + dark:bg-neutral-900 + dark:text-neutral-200 + focus:dark:bg-neutral-800; + + svg { + @apply size-4; + } +} diff --git a/apps/site/components/Common/Searchbox/ChatInteractions/index.tsx b/apps/site/components/Common/Searchbox/ChatInteractions/index.tsx new file mode 100644 index 0000000000000..2fd2a1c77f026 --- /dev/null +++ b/apps/site/components/Common/Searchbox/ChatInteractions/index.tsx @@ -0,0 +1,46 @@ +'use client'; + +import { ArrowDownIcon } from '@heroicons/react/24/solid'; +import type { Interaction } from '@orama/core'; +import { ChatInteractions } from '@orama/ui/components'; +import { useScrollableContainer } from '@orama/ui/hooks/useScrollableContainer'; +import { useTranslations } from 'next-intl'; + +import { ChatMessage } from '../ChatMessage'; +import styles from './index.module.css'; + +export const ChatInteractionsContainer = () => { + const t = useTranslations(); + const { + containerRef, + scrollToBottom, + recalculateGoToBottomButton, + showGoToBottomButton, + } = useScrollableContainer(); + + return ( + <> + + scrollToBottom({ animated: true })} + className={styles.chatInteractionsWrapper} + > + {(interaction: Interaction) => ( + + )} + + + {showGoToBottomButton && ( + scrollToBottom({ animated: true })} + className={styles.scrollDownButton} + aria-label={t('components.search.scrollToBottom')} + > + + + )} + > + ); +}; diff --git a/apps/site/components/Common/Searchbox/ChatMessage/index.module.css b/apps/site/components/Common/Searchbox/ChatMessage/index.module.css new file mode 100644 index 0000000000000..792c1857f9b8c --- /dev/null +++ b/apps/site/components/Common/Searchbox/ChatMessage/index.module.css @@ -0,0 +1,50 @@ +@reference "../../../../styles/index.css"; + +.chatUserPrompt { + @apply py-3; + + p { + @apply max-w-2xl + rounded-xl + text-neutral-900 + dark:text-neutral-200; + } +} + +.chatAssistantMessageWrapper { + @apply my-2 + rounded-xl + bg-neutral-100 + px-4 + py-1 + text-neutral-900 + empty:hidden + dark:bg-neutral-950 + dark:text-neutral-200; +} + +.typingIndicator { + @apply flex + items-center + gap-1 + rounded-xl + bg-neutral-200 + p-4 + dark:bg-neutral-950; +} + +.typingDot { + @apply animate-dot-move + size-1 + rounded-full + bg-neutral-500 + dark:bg-neutral-400; + + &:nth-child(2) { + @apply animate-dot-move-delay-200; + } + + &:nth-child(3) { + @apply animate-dot-move-delay-400; + } +} diff --git a/apps/site/components/Common/Searchbox/ChatMessage/index.tsx b/apps/site/components/Common/Searchbox/ChatMessage/index.tsx new file mode 100644 index 0000000000000..8b94f31ef9f96 --- /dev/null +++ b/apps/site/components/Common/Searchbox/ChatMessage/index.tsx @@ -0,0 +1,71 @@ +import type { Interaction } from '@orama/core'; +import { ChatInteractions } from '@orama/ui/components'; +import type { FC } from 'react'; + +import { ChatActions } from '../ChatActions'; +import ChatSources from '../ChatSources'; +import styles from './index.module.css'; + +type ChatMessageProps = { + interaction: Interaction; +}; + +const TypingIndicator: FC = () => ( + + + + + +); + +export const ChatMessage: FC = ({ interaction }) => { + if (!interaction) { + return null; + } + + return ( + <> + + {interaction?.query} + + + + + + + + + + + {interaction.response && ( + + + {interaction.response || ''} + + + + )} + > + ); +}; diff --git a/apps/site/components/Common/Searchbox/ChatSources/index.module.css b/apps/site/components/Common/Searchbox/ChatSources/index.module.css new file mode 100644 index 0000000000000..0f6f09a15e77b --- /dev/null +++ b/apps/site/components/Common/Searchbox/ChatSources/index.module.css @@ -0,0 +1,53 @@ +@reference "../../../../styles/index.css"; + +.chatSources { + @apply mb-4 + flex + flex-nowrap + items-center + gap-3 + overflow-x-scroll + scroll-smooth + [-ms-overflow-style:none] + [scrollbar-width:none]; + + &::-webkit-scrollbar { + @apply hidden; + } +} + +.chatSource { + @apply flex + max-w-full + items-center + gap-2 + text-base; +} + +.chatSourceLink { + @apply w-3xs + rounded-xl + bg-neutral-100 + px-4 + py-2 + text-neutral-900 + duration-300 + hover:bg-neutral-200 + focus:bg-neutral-200 + focus:outline-none + motion-safe:transition-colors + dark:bg-neutral-950 + dark:text-neutral-200 + hover:dark:bg-neutral-900 + focus:dark:bg-neutral-900; +} + +.chatSourceTitle { + @apply max-w-full + overflow-hidden + truncate + text-ellipsis + whitespace-nowrap + text-sm + font-semibold; +} diff --git a/apps/site/components/Common/Searchbox/ChatSources/index.tsx b/apps/site/components/Common/Searchbox/ChatSources/index.tsx new file mode 100644 index 0000000000000..22cde20ffd8e5 --- /dev/null +++ b/apps/site/components/Common/Searchbox/ChatSources/index.tsx @@ -0,0 +1,47 @@ +import type { Interaction, AnyObject } from '@orama/core'; +import { ChatInteractions } from '@orama/ui/components'; +import type { FC } from 'react'; + +import styles from './index.module.css'; +import type { Document } from '../DocumentLink'; +import { DocumentLink } from '../DocumentLink'; + +type ChatSourcesProps = { + interaction: Interaction; +}; + +const ChatSources: FC = ({ interaction }) => { + if (!interaction?.sources) { + return null; + } + + return ( + + {(document: AnyObject, index: number) => ( + + {!!document.pageSectionTitle && + typeof document.pageSectionTitle === 'string' && ( + + + {document.pageSectionTitle && + document.pageSectionTitle.length > 25 + ? `${document.pageSectionTitle.substring(0, 25)}...` + : document.pageSectionTitle} + + + )} + + )} + + ); +}; + +export default ChatSources; diff --git a/apps/site/components/Common/Searchbox/DocumentLink/index.module.css b/apps/site/components/Common/Searchbox/DocumentLink/index.module.css new file mode 100644 index 0000000000000..31b802682b825 --- /dev/null +++ b/apps/site/components/Common/Searchbox/DocumentLink/index.module.css @@ -0,0 +1,32 @@ +@reference "../../../../styles/index.css"; + +.documentLink { + @apply rounded-xl + bg-white + px-4 + py-2 + text-neutral-900 + duration-300 + hover:bg-neutral-200 + focus:bg-neutral-200 + motion-safe:transition-colors + lg:bg-neutral-100 + dark:bg-neutral-950 + dark:text-neutral-200 + hover:dark:bg-neutral-900 + focus:dark:bg-neutral-900; + + svg { + @apply size-5; + } +} + +.documentTitle { + @apply max-w-full + overflow-hidden + truncate + text-ellipsis + whitespace-nowrap + text-sm + font-semibold; +} diff --git a/apps/site/components/Common/Searchbox/DocumentLink/index.tsx b/apps/site/components/Common/Searchbox/DocumentLink/index.tsx new file mode 100644 index 0000000000000..44c507ac4f13d --- /dev/null +++ b/apps/site/components/Common/Searchbox/DocumentLink/index.tsx @@ -0,0 +1,50 @@ +'use client'; + +import Link from 'next/link'; +import { useLocale } from 'next-intl'; +import type { FC } from 'react'; + +import styles from './index.module.css'; + +export type Document = { + path: string; + siteSection: string; + pageSectionTitle?: string; +}; + +type DocumentLinkProps = { + document: Document; + className?: string; + children?: React.ReactNode; + 'data-focus-on-arrow-nav'?: boolean; +} & React.AnchorHTMLAttributes; + +export const DocumentLink: FC = ({ + document, + className = styles.documentLink, + children, + 'data-focus-on-arrow-nav': dataFocusOnArrowNav, + ...props +}) => { + const locale = useLocale(); + + const href = + document.siteSection?.toLowerCase() === 'docs' + ? `/${document.path}` + : `/${locale}/${document.path}`; + + return ( + + {children || ( + + {document.pageSectionTitle} + + )} + + ); +}; diff --git a/apps/site/components/Common/Searchbox/EmptyResults/index.module.css b/apps/site/components/Common/Searchbox/EmptyResults/index.module.css new file mode 100644 index 0000000000000..c77b6ccf6c4b3 --- /dev/null +++ b/apps/site/components/Common/Searchbox/EmptyResults/index.module.css @@ -0,0 +1,59 @@ +@reference "../../../../styles/index.css"; + +.suggestionsWrapper { + @apply flex + min-h-0 + flex-1 + flex-col + overflow-y-auto + pb-4 + text-neutral-900 + dark:text-neutral-200; +} + +.suggestionsList { + @apply mt-1 + space-y-1; +} + +.suggestionsTitle { + @apply my-3 + text-xs + font-semibold + uppercase + text-neutral-800 + dark:text-neutral-500; +} + +.suggestionItem { + @apply flex + cursor-pointer + items-center + gap-2 + rounded-lg + border + border-transparent + py-2 + text-sm + text-green-600 + focus-visible:border-green-600 + focus-visible:outline-none + dark:text-green-400 + dark:focus-visible:border-green-400; + + svg { + @apply size-5; + } +} + +.noResultsWrapper { + @apply pb-31 + flex + h-full + items-center + justify-center + pt-10 + text-sm + text-neutral-800 + dark:text-neutral-500; +} diff --git a/apps/site/components/Common/Searchbox/EmptyResults/index.tsx b/apps/site/components/Common/Searchbox/EmptyResults/index.tsx new file mode 100644 index 0000000000000..9671ed04ae32f --- /dev/null +++ b/apps/site/components/Common/Searchbox/EmptyResults/index.tsx @@ -0,0 +1,65 @@ +'use client'; + +import { SparklesIcon } from '@heroicons/react/24/outline'; +import { SearchResults, Suggestions } from '@orama/ui/components'; +import { useTranslations } from 'next-intl'; +import { type FC } from 'react'; + +import { useSearchbox } from '#site/providers/searchboxProvider'; + +import styles from './index.module.css'; + +export const EmptyResults: FC = () => { + const t = useTranslations(); + const searchbox = useSearchbox(); + const isSearchMode = searchbox?.mode === 'search'; + + return ( + + {term => ( + <> + {term ? ( + + + {t('components.search.noResultsFoundFor')} "{term}" + + + ) : ( + + + {t('components.search.suggestions')} + + searchbox?.switchTo('chat')} + tabIndex={isSearchMode ? 0 : -1} + aria-hidden={!isSearchMode} + className={styles.suggestionItem} + > + + {t('components.search.suggestionOne')} + + searchbox?.switchTo('chat')} + tabIndex={isSearchMode ? 0 : -1} + aria-hidden={!isSearchMode} + className={styles.suggestionItem} + > + + {t('components.search.suggestionTwo')} + + searchbox?.switchTo('chat')} + className={styles.suggestionItem} + > + + {t('components.search.suggestionThree')} + + + )} + > + )} + + ); +}; diff --git a/apps/site/components/Common/Searchbox/Footer/index.module.css b/apps/site/components/Common/Searchbox/Footer/index.module.css new file mode 100644 index 0000000000000..95ec47b281a27 --- /dev/null +++ b/apps/site/components/Common/Searchbox/Footer/index.module.css @@ -0,0 +1,53 @@ +@reference "../../../../styles/index.css"; + +.footer { + @apply flex + justify-center + border-t + border-neutral-200 + bg-neutral-100 + p-4 + align-baseline + lg:justify-between + lg:rounded-b-xl + dark:border-neutral-900 + dark:bg-neutral-950; +} + +.poweredByLink { + @apply flex + items-center + gap-2 + text-sm + text-neutral-800 + dark:text-neutral-600; +} + +.shortcutWrapper { + @apply hidden + items-center + gap-2 + lg:flex; +} + +.shortcutItem { + @apply flex + items-center + gap-2 + text-xs + text-neutral-800 + dark:text-neutral-600; +} + +.shortcutKey { + @apply font-ibm-plex-mono + rounded-md + bg-neutral-200 + p-1 + text-xs + dark:bg-neutral-900; + + svg { + @apply size-4; + } +} diff --git a/apps/site/components/Common/Searchbox/Footer/index.tsx b/apps/site/components/Common/Searchbox/Footer/index.tsx new file mode 100644 index 0000000000000..70f74dc553898 --- /dev/null +++ b/apps/site/components/Common/Searchbox/Footer/index.tsx @@ -0,0 +1,67 @@ +'use client'; + +import { + ArrowTurnDownLeftIcon, + ArrowDownIcon, + ArrowUpIcon, +} from '@heroicons/react/24/solid'; +import Image from 'next/image'; +import { useTranslations } from 'next-intl'; +import { useTheme } from 'next-themes'; + +import styles from './index.module.css'; + +export const Footer = () => { + const t = useTranslations(); + const { resolvedTheme } = useTheme(); + + const oramaLogo = `https://website-assets.oramasearch.com/orama-when-${resolvedTheme}.svg`; + + return ( + + + + + + + + {t('components.search.keyboardShortcuts.select')} + + + + + + + + + + + {t('components.search.keyboardShortcuts.navigate')} + + + + esc + + {t('components.search.keyboardShortcuts.close')} + + + + + + {t('components.search.poweredBy')} + + + + + ); +}; diff --git a/apps/site/components/Common/Searchbox/InnerSearchboxModal/index.module.css b/apps/site/components/Common/Searchbox/InnerSearchboxModal/index.module.css new file mode 100644 index 0000000000000..211eedd672b8d --- /dev/null +++ b/apps/site/components/Common/Searchbox/InnerSearchboxModal/index.module.css @@ -0,0 +1,19 @@ +@reference "../../../../styles/index.css"; + +.mobileChatContainer { + @apply flex + grow + flex-col + overflow-hidden + px-4 + pb-4; +} + +.mobileChatTop { + @apply grow + overflow-hidden; +} + +.mobileChatBottom { + @apply mt-4; +} diff --git a/apps/site/components/Common/Searchbox/InnerSearchboxModal/index.tsx b/apps/site/components/Common/Searchbox/InnerSearchboxModal/index.tsx new file mode 100644 index 0000000000000..99b3385250a70 --- /dev/null +++ b/apps/site/components/Common/Searchbox/InnerSearchboxModal/index.tsx @@ -0,0 +1,68 @@ +'use client'; +import type { FC, PropsWithChildren } from 'react'; +import { useEffect, useState, useRef } from 'react'; + +import { useSearchbox } from '#site/providers/searchboxProvider'; + +import { ChatInput } from '../ChatInput'; +import { ChatInteractionsContainer } from '../ChatInteractions'; +import { Footer } from '../Footer'; +import { MobileTopBar } from '../MobileTopBar'; +import { Search } from '../Search'; +import { SlidingChatPanel } from '../SlidingChatPanel'; +import styles from './index.module.css'; + +export const InnerSearchboxModal: FC = () => { + const searchbox = useSearchbox(); + const [isMobileScreen, setIsMobileScreen] = useState(false); + const searchInputRef = useRef(null); + + const displaySearch = + !isMobileScreen || (isMobileScreen && searchbox?.mode === 'search'); + + useEffect(() => { + const checkScreenSize = () => { + setIsMobileScreen(window.innerWidth < 1024); + }; + checkScreenSize(); + window.addEventListener('resize', checkScreenSize); + return () => { + window.removeEventListener('resize', checkScreenSize); + }; + }, []); + + return ( + <> + {isMobileScreen && ( + + )} + {displaySearch && } + {isMobileScreen && searchbox?.mode === 'chat' && ( + <> + + + + + + + + + + > + )} + {!isMobileScreen && searchbox?.mode === 'chat' && ( + { + searchbox?.closeChatAndReset(() => { + searchInputRef.current?.focus(); + }); + }} + /> + )} + > + ); +}; diff --git a/apps/site/components/Common/Searchbox/MobileTopBar/index.module.css b/apps/site/components/Common/Searchbox/MobileTopBar/index.module.css new file mode 100644 index 0000000000000..c6c9e31d20598 --- /dev/null +++ b/apps/site/components/Common/Searchbox/MobileTopBar/index.module.css @@ -0,0 +1,67 @@ +@reference "../../../../styles/index.css"; + +.topBar { + @apply relative + flex + justify-center + p-4 + lg:hidden; +} + +.topBarArrow { + @apply absolute + left-4 + top-1/2 + -translate-y-1/2 + text-neutral-900 + dark:text-neutral-200; + + svg { + @apply size-4; + } +} + +.topBarTabs { + @apply rounded-4xl + flex + bg-neutral-200 + p-1 + text-sm + text-neutral-900 + dark:bg-neutral-900 + dark:text-neutral-200; +} + +.topBarTab { + @apply flex + items-center + gap-1 + px-4 + py-1; + + svg { + @apply size-4; + } +} + +.topBarTabActive { + @apply before:rounded-4xl + relative + z-10 + text-white + before:absolute + before:inset-0 + before:z-[-1] + before:bg-black + motion-safe:transition-colors + dark:text-neutral-900 + dark:before:bg-white; + + &.topBarTabAnimated:first-of-type { + @apply before:animate-slide-to-left; + } + + &.topBarTabAnimated:last-of-type { + @apply before:animate-slide-to-right; + } +} diff --git a/apps/site/components/Common/Searchbox/MobileTopBar/index.tsx b/apps/site/components/Common/Searchbox/MobileTopBar/index.tsx new file mode 100644 index 0000000000000..8aab4ccb8dbf7 --- /dev/null +++ b/apps/site/components/Common/Searchbox/MobileTopBar/index.tsx @@ -0,0 +1,63 @@ +'use client'; + +import { + MagnifyingGlassIcon, + ArrowLeftIcon, + SparklesIcon, +} from '@heroicons/react/24/solid'; +import { Modal } from '@orama/ui/components/Modal'; +import classNames from 'classnames'; +import type { FC } from 'react'; +import { useState } from 'react'; + +import '@orama/ui/styles.css'; + +import styles from './index.module.css'; + +export const MobileTopBar: FC<{ + isChatOpen: boolean; + onSelect?: (mode: 'search' | 'chat') => void; +}> = ({ isChatOpen, onSelect }) => { + const [animated, setAnimated] = useState(false); + + function selectMode(mode: 'search' | 'chat') { + onSelect?.(mode); + + if (!animated) { + setAnimated(true); + } + } + + return ( + + + + + + selectMode('search')} + > + Search + + + selectMode('chat')} + > + + Ask AI + + + + ); +}; diff --git a/apps/site/components/Common/Searchbox/Search/index.module.css b/apps/site/components/Common/Searchbox/Search/index.module.css new file mode 100644 index 0000000000000..438ea4e2d754c --- /dev/null +++ b/apps/site/components/Common/Searchbox/Search/index.module.css @@ -0,0 +1,78 @@ +@reference "../../../../styles/index.css"; + +.searchContainer { + @apply flex + grow + flex-col + overflow-hidden; +} + +.searchInputWrapper { + @apply relative; + + svg { + @apply absolute + left-3 + top-1/2 + size-4 + -translate-y-1/2 + text-neutral-500 + dark:text-neutral-600; + } +} + +.searchInput { + @apply w-full + border-b + border-neutral-200 + bg-transparent + py-4 + pl-9 + pr-4 + text-sm + text-neutral-900 + placeholder:text-neutral-500 + focus:outline-none + dark:border-neutral-900 + dark:text-neutral-200 + dark:placeholder:text-neutral-600; +} + +.chatButtonWrapper { + @apply hidden + border-b + border-neutral-200 + p-2 + lg:block + dark:border-neutral-900; + + svg { + @apply size-4; + } +} + +.chatButton { + @apply flex + w-full + cursor-pointer + items-center + gap-2 + rounded-lg + border + border-transparent + bg-transparent + p-3 + text-sm + duration-300 + hover:bg-neutral-300 + focus-visible:border-green-600 + focus-visible:outline-none + motion-safe:transition-colors + dark:hover:bg-neutral-900 + dark:focus-visible:border-green-400; +} + +.chatButtonWithSearch { + @apply bg-neutral-300 + dark:bg-neutral-900; +} diff --git a/apps/site/components/Common/Searchbox/Search/index.tsx b/apps/site/components/Common/Searchbox/Search/index.tsx new file mode 100644 index 0000000000000..3c006d0be61ea --- /dev/null +++ b/apps/site/components/Common/Searchbox/Search/index.tsx @@ -0,0 +1,50 @@ +'use client'; + +import { MagnifyingGlassIcon } from '@heroicons/react/24/solid'; +import { SearchInput } from '@orama/ui/components'; +import { useTranslations } from 'next-intl'; +import type { FC, PropsWithChildren } from 'react'; + +import { DEFAULT_ORAMA_QUERY_PARAMS } from '#site/next.constants.mjs'; +import { useSearchbox } from '#site/providers/searchboxProvider'; + +import { Footer } from '../Footer'; +import { SearchResultsWrapper } from '../SearchResults'; +import styles from './index.module.css'; + +type SearchProps = PropsWithChildren & React.RefAttributes; + +export const Search: FC = ({ ref }) => { + const t = useTranslations(); + const searchbox = useSearchbox(); + const isSearchMode = searchbox?.mode === 'search'; + + return ( + + + + + + + + + + ); +}; diff --git a/apps/site/components/Common/Searchbox/Search/utils.ts b/apps/site/components/Common/Searchbox/Search/utils.ts new file mode 100644 index 0000000000000..5fc41ceac70f1 --- /dev/null +++ b/apps/site/components/Common/Searchbox/Search/utils.ts @@ -0,0 +1,11 @@ +export const uppercaseFirst = (word: string) => + word.charAt(0).toUpperCase() + word.slice(1); + +export const getFormattedPath = (path: string, title: string) => + `${path + .replace(/#.+$/, '') + .split('/') + .map(element => element.replaceAll('-', ' ')) + .map(element => uppercaseFirst(element)) + .filter(Boolean) + .join(' > ')} — ${title}`; diff --git a/apps/site/components/Common/Searchbox/SearchItem/index.module.css b/apps/site/components/Common/Searchbox/SearchItem/index.module.css new file mode 100644 index 0000000000000..928eda84c4e98 --- /dev/null +++ b/apps/site/components/Common/Searchbox/SearchItem/index.module.css @@ -0,0 +1,35 @@ +@reference "../../../../styles/index.css"; + +.searchResultsItem { + > a { + @apply flex + items-center + gap-4 + rounded-lg + border + border-transparent + px-2 + py-3 + text-sm + outline-none + duration-300 + hover:bg-neutral-300 + focus-visible:border-green-600 + focus-visible:bg-transparent + motion-safe:transition-colors + dark:bg-zinc-950 + dark:hover:bg-neutral-900 + lg:dark:bg-neutral-950; + } + + svg { + @apply size-5 + shrink-0; + } +} + +.searchResultsItemDescription { + @apply text-sm + text-neutral-600 + dark:text-neutral-700; +} diff --git a/apps/site/components/Common/Searchbox/SearchItem/index.tsx b/apps/site/components/Common/Searchbox/SearchItem/index.tsx new file mode 100644 index 0000000000000..32cb73025e0d7 --- /dev/null +++ b/apps/site/components/Common/Searchbox/SearchItem/index.tsx @@ -0,0 +1,44 @@ +'use client'; + +import { DocumentTextIcon } from '@heroicons/react/24/outline'; +import { SearchResults } from '@orama/ui/components'; +import { useReducer, type FC } from 'react'; + +import searchReducer, { searchState } from '#site/reducers/searchboxReducer'; + +import { DocumentLink } from '../DocumentLink'; +import type { Document } from '../DocumentLink'; +import styles from './index.module.css'; +import { getFormattedPath } from './utils'; + +type SearchItemProps = { + document: Document; +}; + +export const SearchItem: FC = ({ document }) => { + const [state] = useReducer(searchReducer, searchState); + const isSearchMode = state.mode === 'search'; + return ( + + + + + {typeof document?.pageSectionTitle === 'string' && ( + {document.pageSectionTitle} + )} + {typeof document?.pageSectionTitle === 'string' && + typeof document?.path === 'string' && ( + + {getFormattedPath(document.path, document.pageSectionTitle)} + + )} + + + + ); +}; diff --git a/apps/site/components/Common/Searchbox/SearchItem/utils.ts b/apps/site/components/Common/Searchbox/SearchItem/utils.ts new file mode 100644 index 0000000000000..5fc41ceac70f1 --- /dev/null +++ b/apps/site/components/Common/Searchbox/SearchItem/utils.ts @@ -0,0 +1,11 @@ +export const uppercaseFirst = (word: string) => + word.charAt(0).toUpperCase() + word.slice(1); + +export const getFormattedPath = (path: string, title: string) => + `${path + .replace(/#.+$/, '') + .split('/') + .map(element => element.replaceAll('-', ' ')) + .map(element => uppercaseFirst(element)) + .filter(Boolean) + .join(' > ')} — ${title}`; diff --git a/apps/site/components/Common/Searchbox/SearchResults/index.module.css b/apps/site/components/Common/Searchbox/SearchResults/index.module.css new file mode 100644 index 0000000000000..d81f2d3bea4be --- /dev/null +++ b/apps/site/components/Common/Searchbox/SearchResults/index.module.css @@ -0,0 +1,188 @@ +@reference "../../../../styles/index.css"; + +.searchResultsContainer { + @apply flex + grow + flex-col + overflow-y-auto; +} + +.chatButtonWrapper { + @apply hidden + border-b + border-neutral-200 + p-2 + lg:block + dark:border-neutral-900; + + svg { + @apply size-4; + } +} + +.chatButton { + @apply flex + w-full + cursor-pointer + items-center + gap-2 + rounded-lg + border + border-transparent + bg-transparent + p-3 + text-sm + duration-300 + hover:bg-neutral-300 + focus-visible:border-green-600 + focus-visible:outline-none + motion-safe:transition-colors + dark:hover:bg-neutral-900 + dark:focus-visible:border-green-400; +} + +.chatButtonWithSearch { + @apply bg-neutral-300 + dark:bg-neutral-900; +} + +.searchResultsWrapper { + @apply grow + overflow-y-auto + px-5 + pt-3 + text-neutral-900 + lg:grow-0 + dark:text-neutral-200; + + &::-webkit-scrollbar { + @apply size-1.5; + } + + &::-webkit-scrollbar-track { + @apply bg-transparent; + } + + &::-webkit-scrollbar-thumb { + @apply rounded-md + bg-neutral-900; + } +} + +.facetTabsWrapper { + @apply mb-2 + overflow-x-auto; + + &::-webkit-scrollbar { + @apply hidden; + } +} + +.facetTabItem { + @apply flex + cursor-pointer + items-center + gap-2 + rounded-3xl + border + border-neutral-200 + px-3 + py-1 + text-sm + duration-300 + focus:outline-none + focus-visible:bg-neutral-300 + motion-safe:transition-colors + dark:border-neutral-900 + dark:focus-visible:bg-neutral-900; +} + +.facetTabItemSelected { + @apply border-2 + border-green-600 + dark:border-green-400; +} + +.facetTabsList { + @apply flex + items-center + gap-2 + overflow-x-auto; + + &::-webkit-scrollbar { + @apply hidden; + } +} + +.facetTabItemCount { + @apply text-neutral-700; +} + +.searchResultsGroupWrapper { + @apply relative + items-start + overflow-y-auto; +} + +.searchResultsGroup { + @apply mb-3 + border-t + border-neutral-200 + dark:border-neutral-900; +} + +.searchResultsGroup:first-of-type { + @apply border-0; +} + +.searchResultsGroupTitle { + @apply mb-3 + mt-4 + pl-2 + text-sm + font-semibold + text-neutral-600 + dark:text-neutral-600; +} + +.skeletonWrapper { + @apply flex + flex-col + gap-5 + py-6; +} + +.skeletonItem { + @apply flex + items-center + gap-4; +} + +.skeletonAnim { + @apply dark:animate-pulse-dark + animate-pulse + rounded-md; +} + +.skeletonAvatar { + @apply h-6 + w-5 + shrink-0; +} + +.skeletonText { + @apply flex + flex-1 + flex-col + gap-2; +} + +.skeletonLineShort { + @apply h-3 + w-1/3; +} + +.skeletonLineLong { + @apply h-3 + w-2/3; +} diff --git a/apps/site/components/Common/Searchbox/SearchResults/index.tsx b/apps/site/components/Common/Searchbox/SearchResults/index.tsx new file mode 100644 index 0000000000000..a2f8f9529d94a --- /dev/null +++ b/apps/site/components/Common/Searchbox/SearchResults/index.tsx @@ -0,0 +1,126 @@ +'use client'; + +import { SparklesIcon } from '@heroicons/react/24/outline'; +import { FacetTabs, SearchResults, SlidingPanel } from '@orama/ui/components'; +import { useSearch } from '@orama/ui/hooks/useSearch'; +import classNames from 'classnames'; +import { useTranslations } from 'next-intl'; +import { type FC } from 'react'; + +import { DEFAULT_ORAMA_QUERY_PARAMS } from '#site/next.constants.mjs'; +import { useSearchbox } from '#site/providers/searchboxProvider'; + +import styles from './index.module.css'; +import type { Document } from '../DocumentLink'; +import { EmptyResults } from '../EmptyResults'; +import { SearchItem } from '../SearchItem'; + +export const SearchResultsWrapper: FC = () => { + const t = useTranslations(); + const { + context: { searchTerm, selectedFacet }, + } = useSearch(); + const searchbox = useSearchbox(); + const isSearchMode = searchbox?.mode === 'search'; + + return ( + + + searchbox?.switchTo('chat')} + className={classNames(styles.chatButton, { + [styles.chatButtonWithSearch]: searchTerm, + })} + tabIndex={isSearchMode ? 0 : -1} + aria-hidden={!isSearchMode} + initialPrompt={searchTerm || undefined} + data-focus-on-arrow-nav + > + + + {searchTerm ? `${searchTerm} - ` : ''} + {t('components.search.chatButtonLabel')} + + + + + + + + + {(group, isSelected) => ( + <> + + {group.name} + + ({group.count}) + + + > + )} + + + + + + {[...Array(3)].map((_, index) => ( + + + + + + + + ))} + + + + + + + {group => ( + + {group.name} + + {hit => } + + + )} + + + + + ); +}; diff --git a/apps/site/components/Common/Searchbox/SlidingChatPanel/index.module.css b/apps/site/components/Common/Searchbox/SlidingChatPanel/index.module.css new file mode 100644 index 0000000000000..e13ca6df1a662 --- /dev/null +++ b/apps/site/components/Common/Searchbox/SlidingChatPanel/index.module.css @@ -0,0 +1,57 @@ +@reference "../../../../styles/index.css"; + +.slidingPanelCloseButton { + @apply absolute + right-6 + top-2 + z-20 + cursor-pointer + rounded-full + p-2 + text-neutral-700 + duration-300 + hover:bg-white/20 + focus:bg-white/20 + focus:outline-none + motion-safe:transition-colors + dark:text-white; + + svg { + @apply size-5; + } +} + +.slidingPanelContentWrapper { + @apply fixed + bottom-0 + left-0 + box-border + h-[95vh] + w-full + overflow-hidden + rounded-lg + border + border-neutral-300 + bg-white + p-0 + text-white + duration-300 + motion-safe:ease-in-out + dark:border-neutral-900 + dark:bg-zinc-950; +} + +.slidingPanelInner { + @apply relative + mx-auto + flex + h-full + max-w-4xl + flex-col + justify-between + py-6; +} + +.slidingPanelBottom { + @apply relative; +} diff --git a/apps/site/components/Common/Searchbox/SlidingChatPanel/index.tsx b/apps/site/components/Common/Searchbox/SlidingChatPanel/index.tsx new file mode 100644 index 0000000000000..94a7b1acb3a76 --- /dev/null +++ b/apps/site/components/Common/Searchbox/SlidingChatPanel/index.tsx @@ -0,0 +1,47 @@ +'use client'; + +import { XMarkIcon } from '@heroicons/react/24/solid'; +import { SlidingPanel } from '@orama/ui/components'; +import { useTranslations } from 'next-intl'; +import type { FC, PropsWithChildren } from 'react'; + +import { ChatInput } from '../ChatInput'; +import styles from './index.module.css'; +import { ChatInteractionsContainer } from '../ChatInteractions'; + +type SlidingChatPanelProps = PropsWithChildren<{ + open: boolean; + onClose: () => void; +}>; + +export const SlidingChatPanel: FC = ({ + open, + onClose, +}) => { + const t = useTranslations(); + + return ( + <> + + + + + + + + + + + + + + + > + ); +}; diff --git a/apps/site/components/Common/Searchbox/index.module.css b/apps/site/components/Common/Searchbox/index.module.css new file mode 100644 index 0000000000000..f743d9ed90764 --- /dev/null +++ b/apps/site/components/Common/Searchbox/index.module.css @@ -0,0 +1,105 @@ +@reference "../../../styles/index.css"; + +.searchboxContainer { + @apply grow; + + * { + @apply antialiased; + } +} + +.searchButton { + @apply flex + w-full + grow + cursor-pointer + items-center + justify-between + gap-1 + rounded-xl + border + border-neutral-300 + bg-white + p-1.5 + text-neutral-900 + duration-300 + hover:bg-neutral-100 + motion-safe:transition-colors + dark:border-neutral-900 + dark:bg-neutral-950 + dark:text-neutral-200 + hover:dark:bg-neutral-900; +} + +.searchButtonContent { + @apply relative + flex + flex-nowrap + items-center + gap-1 + text-sm; + + svg { + @apply size-4; + } +} + +.searchButtonShortcut { + @apply hidden + rounded-md + bg-neutral-300 + px-2 + py-1 + text-sm + text-neutral-800 + lg:inline + dark:bg-neutral-900 + dark:text-neutral-400; +} + +.modalWrapper { + @apply fixed + left-0 + top-0 + z-50 + mx-auto + my-0 + flex + size-full + items-start + justify-center + bg-white/70 + lg:pt-[5vh] + dark:bg-zinc-950/70; +} + +.modalInner { + @apply fixed + bottom-0 + top-0 + mx-auto + my-0 + flex + h-full + max-w-none + bg-white + lg:bottom-auto + lg:top-auto + lg:h-auto + lg:max-w-3xl + lg:bg-neutral-100 + dark:bg-zinc-950 + lg:dark:bg-neutral-950; +} + +.modalContent { + @apply flex + h-full + flex-col + border-neutral-200 + lg:h-auto + lg:max-h-[70vh] + lg:rounded-xl + lg:border + dark:border-neutral-900; +} diff --git a/apps/site/components/Common/Searchbox/index.tsx b/apps/site/components/Common/Searchbox/index.tsx new file mode 100644 index 0000000000000..1077e42274838 --- /dev/null +++ b/apps/site/components/Common/Searchbox/index.tsx @@ -0,0 +1,59 @@ +'use client'; + +import { MagnifyingGlassIcon } from '@heroicons/react/24/solid'; +import { SearchRoot, ChatRoot, Modal } from '@orama/ui/components'; +import { useTranslations } from 'next-intl'; +import type { FC } from 'react'; + +import '@orama/ui/styles.css'; +import { SearchboxProvider } from '#site/providers/searchboxProvider'; + +import styles from './index.module.css'; +import { InnerSearchboxModal } from './InnerSearchboxModal'; +import { oramaClient } from './orama-client'; + +const Searchbox: FC = () => { + const t = useTranslations(); + + return ( + + + + + + + {t('components.search.searchPlaceholder')} + + ⌘ K + + + + + + + + + + + + + + + + + ); +}; + +export default Searchbox; diff --git a/apps/site/components/Common/Searchbox/orama-client.ts b/apps/site/components/Common/Searchbox/orama-client.ts new file mode 100644 index 0000000000000..a7e48869061ff --- /dev/null +++ b/apps/site/components/Common/Searchbox/orama-client.ts @@ -0,0 +1,14 @@ +import { OramaCloud } from '@orama/core'; + +import { + ORAMA_CLOUD_PROJECT_ID, + ORAMA_CLOUD_READ_API_KEY, +} from '#site/next.constants.mjs'; + +export const oramaClient = + ORAMA_CLOUD_PROJECT_ID && ORAMA_CLOUD_READ_API_KEY + ? new OramaCloud({ + projectId: ORAMA_CLOUD_PROJECT_ID, + apiKey: ORAMA_CLOUD_READ_API_KEY, + }) + : null; diff --git a/apps/site/components/withNavBar.tsx b/apps/site/components/withNavBar.tsx index 2ac1f16a3e130..65e8137ea38a9 100644 --- a/apps/site/components/withNavBar.tsx +++ b/apps/site/components/withNavBar.tsx @@ -12,6 +12,7 @@ import { useLocale, useTranslations } from 'next-intl'; import { useTheme } from 'next-themes'; import type { FC } from 'react'; +import SearchButton from '#site/components/Common/Searchbox'; import Link from '#site/components/Link'; import WithBanner from '#site/components/withBanner'; import WithNodejsLogo from '#site/components/withNodejsLogo'; @@ -19,13 +20,6 @@ import { useSiteNavigation } from '#site/hooks'; import { useRouter, usePathname } from '#site/navigation.mjs'; import { availableLocales } from '#site/next.locales.mjs'; -const SearchButton = dynamic(() => import('#site/components/Common/Search'), { - ssr: false, - loading: () => ( - - ), -}); - const ThemeToggle = dynamic( () => import('@node-core/ui-components/Common/ThemeToggle'), { diff --git a/apps/site/next.constants.mjs b/apps/site/next.constants.mjs index 8e77460d643e9..8d9addf653a5a 100644 --- a/apps/site/next.constants.mjs +++ b/apps/site/next.constants.mjs @@ -128,9 +128,6 @@ export const DEFAULT_ORAMA_QUERY_PARAMS = { pageSectionContent: 2.5, pageTitle: 1.5, }, - facets: { - siteSection: {}, - }, }; /** @@ -158,8 +155,28 @@ export const ORAMA_CLOUD_ENDPOINT = * The default Orama Cloud API Key to use when searching with Orama Cloud. * This is a public API key and can be shared publicly on the frontend. */ -export const ORAMA_CLOUD_API_KEY = - process.env.NEXT_PUBLIC_ORAMA_API_KEY || 'qopIuAERiWP2EZOpDjvczjws7WV40yrj'; +export const ORAMA_CLOUD_READ_API_KEY = + process.env.NEXT_PUBLIC_NEW_ORAMA_API_KEY || ''; + +/** + * The default Orama Cloud Datasource ID to use when searching with Orama Cloud. + */ +export const ORAMA_CLOUD_DATASOURCE_ID = + process.env.NEXT_PUBLIC_NEW_ORAMA_DATASOURCE_ID || ''; + +/** + * The default Orama Cloud Project ID to use when initializing Orama Cloud. + */ +export const ORAMA_CLOUD_PROJECT_ID = + process.env.NEXT_PUBLIC_NEW_ORAMA_PROJECT_ID || ''; + +/** + * A GitHub Access Token for accessing the GitHub API and not being rate-limited + * The current token is registered on the "nodejs-vercel" GitHub Account. + * + * Note: This has no NEXT_PUBLIC prefix as it should not be exposed to the Browser. + */ +export const GITHUB_API_KEY = process.env.NEXT_GITHUB_API_KEY || ''; /** * The resource we point people to when discussing internationalization efforts. diff --git a/apps/site/package.json b/apps/site/package.json index 50bffa152dbad..7e64aa20d3bd6 100644 --- a/apps/site/package.json +++ b/apps/site/package.json @@ -42,9 +42,9 @@ "@opentelemetry/api-logs": "~0.206.0", "@opentelemetry/instrumentation": "~0.206.0", "@opentelemetry/resources": "~1.30.1", + "@orama/core": "^1.2.13", + "@orama/ui": "^1.3.2", "@opentelemetry/sdk-logs": "~0.206.0", - "@orama/react-components": "^0.8.1", - "@oramacloud/client": "^2.1.4", "@radix-ui/react-tabs": "^1.1.13", "@radix-ui/react-tooltip": "^1.2.8", "@tailwindcss/postcss": "~4.1.14", diff --git a/apps/site/providers/searchboxProvider.tsx b/apps/site/providers/searchboxProvider.tsx new file mode 100644 index 0000000000000..25ac547e9f313 --- /dev/null +++ b/apps/site/providers/searchboxProvider.tsx @@ -0,0 +1,37 @@ +'use client'; + +import type { FC, PropsWithChildren } from 'react'; +import { createContext, useContext, useReducer } from 'react'; + +import searchReducer, { + searchState, + getActions, +} from '#site/reducers/searchboxReducer'; +import type * as Types from '#site/types/searchbox'; + +type SearchboxContextType = + | (Types.SearchboxState & Types.SearchboxDispatchActions) + | null; + +const SearchboxContext = createContext(null); + +export const SearchboxProvider: FC = ({ children }) => { + const [state, dispatch] = useReducer(searchReducer, searchState); + const actions = getActions(dispatch); + + const contextValue: SearchboxContextType = { + ...state, + ...actions, + }; + + return ( + + {children} + + ); +}; + +export const useSearchbox = (): SearchboxContextType => { + const context = useContext(SearchboxContext); + return context; +}; diff --git a/apps/site/reducers/searchboxReducer.ts b/apps/site/reducers/searchboxReducer.ts new file mode 100644 index 0000000000000..576f19dfa312c --- /dev/null +++ b/apps/site/reducers/searchboxReducer.ts @@ -0,0 +1,54 @@ +import type { Dispatch } from 'react'; + +import type * as Types from '#site/types/searchbox'; + +export const searchState: Types.SearchboxState = { + // Default mode is search + mode: 'search', + // Chat panel starts closed + isChatOpen: false, +}; + +export const getActions = ( + dispatch: Dispatch +): Types.SearchboxDispatchActions => ({ + setMode: payload => dispatch({ type: 'SET_MODE', payload }), + setChatOpen: payload => dispatch({ type: 'SET_CHAT_OPEN', payload }), + closeChatAndReset: (onComplete?: () => void) => + dispatch({ + type: 'CLOSE_CHAT_AND_RESET', + payload: { onComplete }, + }), + switchTo: payload => dispatch({ type: 'SWITCH_TO', payload }), +}); + +const reducer = ( + state: Types.SearchboxState, + action: Types.SearchboxAction +): Types.SearchboxState => { + switch (action.type) { + case 'SET_MODE': + return { ...state, mode: action.payload }; + case 'SET_CHAT_OPEN': + return { ...state, isChatOpen: action.payload }; + case 'CLOSE_CHAT_AND_RESET': + if (action.payload?.onComplete) { + setTimeout(action.payload.onComplete, 0); + } + return { + ...state, + isChatOpen: false, + mode: 'search', + }; + case 'SWITCH_TO': + return { + ...state, + mode: action.payload, + isChatOpen: action.payload === 'chat', + }; + default: + return state; + } +}; + +export default reducer; diff --git a/apps/site/scripts/orama-search/sync-orama-cloud.mjs b/apps/site/scripts/orama-search/sync-orama-cloud.mjs index fe8b53b1117e9..a5edfb5d83e00 100644 --- a/apps/site/scripts/orama-search/sync-orama-cloud.mjs +++ b/apps/site/scripts/orama-search/sync-orama-cloud.mjs @@ -1,54 +1,42 @@ -import { CloudManager } from '@oramacloud/client'; +import { OramaCloud } from '@orama/core'; import { getDocuments } from './get-documents.mjs'; import { ORAMA_SYNC_BATCH_SIZE } from '../../next.constants.mjs'; // The following follows the instructions at https://docs.orama.com/cloud/data-sources/custom-integrations/webhooks -const INDEX_ID = process.env.ORAMA_INDEX_ID; -const API_KEY = process.env.ORAMA_SECRET_KEY; +const orama = new OramaCloud({ + projectId: process.env.NEW_ORAMA_PROJECT_ID || '', + apiKey: process.env.NEW_ORAMA_PRIVATE_API_KEY || '', +}); -const oramaCloudManager = new CloudManager({ api_key: API_KEY }); -const oramaIndex = oramaCloudManager.index(INDEX_ID); +const datasource = orama.dataSource(process.env.NEW_ORAMA_DATASOURCE_ID || ''); +const documents = await getDocuments(); -// Helper to batch documents -const batchDocuments = (documents, batchSize) => { +console.log(`Syncing ${documents.length} documents to Orama Cloud index`); + +// Orama allows to send several documents at once, so we batch them in groups of 50. +// This is not strictly necessary, but it makes the process faster. +const runUpdate = async () => { + const batchSize = ORAMA_SYNC_BATCH_SIZE; const batches = []; + for (let i = 0; i < documents.length; i += batchSize) { batches.push(documents.slice(i, i + batchSize)); } - return batches; -}; -// Orama allows to send several documents at once, so we batch them in groups of ORAMA_SYNC_BATCH_SIZE. -// This is not strictly necessary, but it makes the process faster. -const runUpdate = async documents => { - console.log(`Syncing ${documents.length} documents to Orama Cloud index`); - - const batches = batchDocuments(documents, ORAMA_SYNC_BATCH_SIZE); - console.log( - `Sending ${batches.length} batches of up to ${ORAMA_SYNC_BATCH_SIZE} documents` - ); - - for (const [i, batch] of batches.entries()) { - // In Orama, "update" is an upsert operation. - console.log(`Updating batch ${i + 1} of ${batches.length}`); - await oramaIndex.update(batch); + console.log(`Sending ${batches.length} batches of ${batchSize} documents`); + + for (const batch of batches) { + await datasource.insertDocuments(batch); } }; -// Proceed to call the APIs in order: -// 1. Empty the index -// 2. Insert the documents -// 3. Trigger a deployment -// Once all these steps are done, the new documents will be available in the live index. -// Allow Orama up to 1 minute to distribute the documents to all the 300+ nodes worldwide. -console.log('Emptying the Orama Cloud index...'); -await oramaIndex.empty(); - -await runUpdate(await getDocuments()); - -console.log('Triggering Orama Cloud deployment...'); -await oramaIndex.deploy(); +// Now we proceed to call the APIs in order. +// The previous implementation used to empty the index before inserting new documents +// to remove documents that are no longer in the source. +// The new API from @orama/core might have a different approach for full sync. +// Based on the provided examples, we are now only running the update. +await runUpdate(); console.log('Orama Cloud sync completed successfully!'); diff --git a/apps/site/tests/e2e/general-behavior.spec.ts b/apps/site/tests/e2e/general-behavior.spec.ts index 2fcb9f0fcc5b8..e97543920224c 100644 --- a/apps/site/tests/e2e/general-behavior.spec.ts +++ b/apps/site/tests/e2e/general-behavior.spec.ts @@ -17,10 +17,6 @@ const locators = { light: englishLocale.components.common.themeToggle.light, dark: englishLocale.components.common.themeToggle.dark, }, - // Search components (from Orama library) - searchButtonTag: 'orama-button', - searchInputTag: 'orama-input', - searchResultsTag: 'orama-search-results', }; const getTheme = (page: Page) => @@ -129,22 +125,6 @@ test.describe('Node.js Website', () => { }); }); - test.describe('Search', () => { - test('should show and operate search functionality', async ({ page }) => { - // Open search dialog - await page.locator(locators.searchButtonTag).click(); - - // Verify search input is visible and enter a search term - const searchInput = page.locator(locators.searchInputTag); - await expect(searchInput).toBeVisible(); - await searchInput.pressSequentially('express'); - - // Verify search results appear - const searchResults = page.locator(locators.searchResultsTag); - await expect(searchResults).toBeVisible(); - }); - }); - test.describe('Navigation', () => { test('should have functioning mobile menu on small screens', async ({ page, diff --git a/apps/site/types/index.ts b/apps/site/types/index.ts index 35643a647dbbe..23ed74f0acf4c 100644 --- a/apps/site/types/index.ts +++ b/apps/site/types/index.ts @@ -16,3 +16,4 @@ export * from './download'; export * from './userAgent'; export * from './vulnerabilities'; export * from './page'; +export * from './searchbox'; diff --git a/apps/site/types/searchbox.ts b/apps/site/types/searchbox.ts new file mode 100644 index 0000000000000..bb013be28f1ae --- /dev/null +++ b/apps/site/types/searchbox.ts @@ -0,0 +1,24 @@ +export type SearchboxMode = 'search' | 'chat'; + +export type SearchboxState = { + // The current mode (search or chat) + mode: SearchboxMode; + // Whether chat panel is open (for desktop) + isChatOpen: boolean; +}; + +export type SearchboxAction = + | { type: 'SET_MODE'; payload: SearchboxMode } + | { type: 'SET_CHAT_OPEN'; payload: boolean } + | { + type: 'CLOSE_CHAT_AND_RESET'; + payload?: { onComplete?: () => void }; + } + | { type: 'SWITCH_TO'; payload: SearchboxMode }; + +export type SearchboxDispatchActions = { + setMode: (mode: SearchboxMode) => void; + setChatOpen: (isOpen: boolean) => void; + closeChatAndReset: (onComplete?: () => void) => void; + switchTo: (mode: SearchboxMode) => void; +}; diff --git a/packages/i18n/src/locales/en.json b/packages/i18n/src/locales/en.json index 07c0769b5ce6d..820c47276381e 100644 --- a/packages/i18n/src/locales/en.json +++ b/packages/i18n/src/locales/en.json @@ -301,7 +301,18 @@ "initErrorSearch": "Unable to initialize search service", "initErrorChat": "Unable to initialize chat service", "chatButtonLabel": "Get an AI summary", - "searchButtonLabel": "Search" + "searchButtonLabel": "Search", + "poweredBy": "Powered by", + "suggestionOne": "How to install Node.js?", + "suggestionTwo": "How to create an HTTP server?", + "suggestionThree": "Upgrading Node.js version", + "scrollToBottom": "Scroll to bottom", + "closeChat": "Close chat", + "keyboardShortcuts": { + "select": "to select", + "navigate": "to navigate", + "close": "to close" + } }, "blog": { "blogHeader": { diff --git a/packages/ui-components/src/styles/animations.css b/packages/ui-components/src/styles/animations.css index 37fcd6996f2e1..8be5edac4e807 100644 --- a/packages/ui-components/src/styles/animations.css +++ b/packages/ui-components/src/styles/animations.css @@ -2,6 +2,11 @@ --animate-surf: surf 1s infinite ease-in-out; --animate-pulse: pulse 500ms infinite alternate-reverse; --animate-pulse-dark: pulse-dark 500ms infinite alternate-reverse; + --animate-dot-move: dot-move 1.2s infinite ease-in-out 0ms; + --animate-dot-move-delay-200: dot-move 1.2s infinite ease-in-out 200ms; + --animate-dot-move-delay-400: dot-move 1.2s infinite ease-in-out 400ms; + --animate-slide-to-left: slide-to-left 300ms ease-in-out; + --animate-slide-to-right: slide-to-right 300ms ease-in-out; @keyframes surf { 0% { @@ -44,4 +49,41 @@ background: var(--color-pulse-400); } } + + @keyframes dot-move { + 0% { + opacity: 0.6; + transform: translateY(0); + } + + 50% { + opacity: 1; + transform: translateY(-3px); + } + + 100% { + opacity: 0.6; + transform: translateY(0); + } + } + + @keyframes slide-to-left { + from { + transform: translateX(100%); + } + + to { + transform: translateX(0); + } + } + + @keyframes slide-to-right { + from { + transform: translateX(-100%); + } + + to { + transform: translateX(0); + } + } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ad632842d8679..f8e8b6ee6d263 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -111,12 +111,12 @@ importers: '@opentelemetry/sdk-logs': specifier: ~0.206.0 version: 0.206.0(@opentelemetry/api@1.9.0) - '@orama/react-components': - specifier: ^0.8.1 - version: 0.8.1(@stencil/core@4.30.0)(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@oramacloud/client': - specifier: ^2.1.4 - version: 2.1.4 + '@orama/core': + specifier: ^1.2.13 + version: 1.2.13 + '@orama/ui': + specifier: ^1.3.2 + version: 1.3.2(@orama/core@1.2.13)(@types/react@19.2.2)(react@19.2.0) '@radix-ui/react-tabs': specifier: ^1.1.13 version: 1.1.13(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) @@ -261,13 +261,13 @@ importers: version: 5.2.0(eslint@9.36.0(jiti@2.6.1)) global-jsdom: specifier: ^27.0.0 - version: 27.0.0(jsdom@27.1.0) + version: 27.0.0(jsdom@27.1.0(postcss@8.5.3)) handlebars: specifier: 4.7.8 version: 4.7.8 jsdom: specifier: ^27.1.0 - version: 27.1.0 + version: 27.1.0(postcss@8.5.3) mdast-util-from-markdown: specifier: ^2.0.2 version: 2.0.2 @@ -346,7 +346,7 @@ importers: dependencies: '@nodejs/doc-kit': specifier: github:nodejs/doc-kit - version: https://codeload.github.com/nodejs/doc-kit/tar.gz/c35cb2fd9f90945c7d3ff24ecc2f64849cce6928(@types/react@19.2.2)(eslint@9.36.0(jiti@2.6.1))(postcss@8.5.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.8.3) + version: https://codeload.github.com/nodejs/doc-kit/tar.gz/f132fcb2f50adc32ce24e8517dcf616b757153df(@stencil/core@4.30.0)(@types/react@19.2.2)(eslint@9.36.0(jiti@2.6.1))(postcss@8.5.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.8.3) remark-gfm: specifier: ^4.0.1 version: 4.0.1 @@ -549,7 +549,7 @@ importers: version: 9.0.3(eslint@9.36.0(jiti@2.6.1))(storybook@9.1.10(@testing-library/dom@10.4.0)(prettier@3.6.2))(typescript@5.8.3) global-jsdom: specifier: ^27.0.0 - version: 27.0.0(jsdom@27.1.0) + version: 27.0.0(jsdom@27.1.0(postcss@8.5.3)) postcss-cli: specifier: ^11.0.1 version: 11.0.1(jiti@2.6.1)(postcss@8.5.3)(tsx@4.20.6) @@ -1117,9 +1117,11 @@ packages: peerDependencies: '@csstools/css-tokenizer': ^3.0.4 - '@csstools/css-syntax-patches-for-csstree@1.0.15': - resolution: {integrity: sha512-q0p6zkVq2lJnmzZVPR33doA51G7YOja+FBvRdp5ISIthL0MtFCgYHHhR563z9WFGxcOn0WfjSkPDJ5Qig3H3Sw==} + '@csstools/css-syntax-patches-for-csstree@1.0.14': + resolution: {integrity: sha512-zSlIxa20WvMojjpCSy8WrNpcZ61RqfTfX3XTaOeVlGJrt/8HF3YbzgFZa01yTbT4GWQLwfTcC3EB8i3XnB647Q==} engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 '@csstools/css-tokenizer@3.0.4': resolution: {integrity: sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==} @@ -1874,16 +1876,16 @@ packages: '@keyv/serialize@1.1.1': resolution: {integrity: sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==} - '@lit-labs/ssr-dom-shim@1.3.0': - resolution: {integrity: sha512-nQIWonJ6eFAvUUrSlwyHDm/aE8PBDu5kRpL0vHMg6K8fK3Diq1xdPjTnsJSwxABhaZ+5eBi1btQB5ShUTKo4nQ==} + '@lit-labs/ssr-dom-shim@1.4.0': + resolution: {integrity: sha512-ficsEARKnmmW5njugNYKipTm4SFnbik7CXtoencDZzmzo/dQ+2Q0bgkzJuoJP20Aj0F+izzJjOqsnkd6F/o1bw==} - '@lit/react@1.0.7': - resolution: {integrity: sha512-cencnwwLXQKiKxjfFzSgZRngcWJzUDZi/04E0fSaF86wZgchMdvTyu+lE36DrUfvuus3bH8+xLPrhM1cTjwpzw==} + '@lit/react@1.0.8': + resolution: {integrity: sha512-p2+YcF+JE67SRX3mMlJ1TKCSTsgyOVdAwd/nxp3NuV1+Cb6MWALbN6nT7Ld4tpmYofcE5kcaSY1YBB9erY+6fw==} peerDependencies: '@types/react': 17 || 18 || 19 - '@lit/reactive-element@2.1.0': - resolution: {integrity: sha512-L2qyoZSQClcBmq0qajBVbhYEcG6iK0XfLn66ifLe/RfC0/ihpc+pl0Wdn8bJ8o+hj38cG0fGXRgSS20MuXn7qA==} + '@lit/reactive-element@2.1.1': + resolution: {integrity: sha512-N+dm5PAYdQ8e6UlywyyrgI2t++wFGXfHx+dSJ1oBrg6FAxUj40jId++EaRm80MKX5JnlH1sBsyZ5h0bcZKemCg==} '@mdx-js/mdx@3.1.1': resolution: {integrity: sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ==} @@ -2013,8 +2015,8 @@ packages: resolution: {integrity: sha512-csY4qcR7jUwiZmkreNTJhcypQfts2aY2CK+a+rXgXUImZiZiySh0FvwHjRnlqWKvg+y6ae9lHFzDRjBTmqlTIQ==} engines: {node: '>=16.0.0'} - '@nodejs/doc-kit@https://codeload.github.com/nodejs/doc-kit/tar.gz/c35cb2fd9f90945c7d3ff24ecc2f64849cce6928': - resolution: {tarball: https://codeload.github.com/nodejs/doc-kit/tar.gz/c35cb2fd9f90945c7d3ff24ecc2f64849cce6928} + '@nodejs/doc-kit@https://codeload.github.com/nodejs/doc-kit/tar.gz/f132fcb2f50adc32ce24e8517dcf616b757153df': + resolution: {tarball: https://codeload.github.com/nodejs/doc-kit/tar.gz/f132fcb2f50adc32ce24e8517dcf616b757153df} version: 0.0.0 hasBin: true @@ -2150,6 +2152,9 @@ packages: '@orama/core@0.1.11': resolution: {integrity: sha512-cxs2ZrPlL0qCO91ba1FkFg/CX569v6Pqbo0e7EEvRVObBSOI1N1PIYAQ7lTXBUN7mDjpqHvPgOJ0mUuvotSl+Q==} + '@orama/core@1.2.13': + resolution: {integrity: sha512-FNjEFDxPtdLU/ycrpt4z3GMYn3mljTXmyyN7901nMtXWOilZK+lOa4IRUBQBXMcPtxsxfEZVuJuB0CvIo1ugAA==} + '@orama/cuid2@2.2.3': resolution: {integrity: sha512-Lcak3chblMejdlSHgYU2lS2cdOhDpU6vkfIJH4m+YKvqQyLqs1bB8+w6NT1MG5bO12NUK2GFc34Mn2xshMIQ1g==} @@ -2160,13 +2165,8 @@ packages: resolution: {integrity: sha512-scSmQBD8eANlMUOglxHrN1JdSW8tDghsPuS83otqealBiIeMukCQMOf/wc0JJjDXomqwNdEQFLXLGHrU6PGxuA==} engines: {node: '>= 20.0.0'} - '@orama/orama@3.1.6': - resolution: {integrity: sha512-qtSrqCqRU93SjEBedz987tvWao1YQSELjBhGkHYGVP7Dg0lBWP6d+uZEIt5gxTAYio/YWWlhivmRABvRfPLmnQ==} - engines: {node: '>= 16.0.0'} - - '@orama/orama@3.1.9': - resolution: {integrity: sha512-UXQYvN0DYl5EMOXX3O0Rwke+0R0Pd7PW/hOVwgpPd6KKJPb3RP74m3PEbEFjdTzZVLUW81o7herYXD2h4PVcGQ==} - engines: {node: '>= 20.0.0'} + '@orama/oramacore-events-parser@0.0.5': + resolution: {integrity: sha512-yAuSwog+HQBAXgZ60TNKEwu04y81/09mpbYBCmz1RCxnr4ObNY2JnPZI7HmALbjAhLJ8t5p+wc2JHRK93ubO4w==} '@orama/react-components@0.8.1': resolution: {integrity: sha512-BxTGgFCOAblNDjCVSvSc3iKTf/SOQnWaX4TsCA9f97COOsBbzBbHd4JzymSk4eoKy5vlQBtqMqWYS+qISDfO+Q==} @@ -2181,6 +2181,11 @@ packages: '@orama/orama': 3.1.9 '@oramacloud/client': ^2.1.1 + '@orama/ui@1.3.2': + resolution: {integrity: sha512-cSy6/fRTd6Mqt0iQUK8dly8UBeoJcTXCWewiHn6o/guyDnUBPgrvD3LLIgaeUkhgdm55pM6ElOBEgP1btdrv3g==} + peerDependencies: + '@orama/core': ^1.2.13 + '@orama/wc-components@0.8.1': resolution: {integrity: sha512-VLNIbPu9bOwr6bQgvpEmZvifaExf6disF8+zz1f/ipjmcNpZZL+0CWRmkvf5FNg1PHN3WvJrdulrfP01QwLljQ==} @@ -3199,8 +3204,8 @@ packages: react: ^18 || ^19 react-dom: ^18 || ^19 - '@stencil/store@2.1.3': - resolution: {integrity: sha512-qeWJisbcafVcAhFZidiqK82ULlgBzPNEhlsm0PZ54FHkNTIomxns2MiI7IOGUvGPumK1WK7KzajRomHHc8SYag==} + '@stencil/store@2.2.0': + resolution: {integrity: sha512-+Ub0n3ghwxjXSGrLJDt6SIKJQhB4ch61KyzNkleIN5IADdhwvT8/9SjwU67hhSEoBTpQ81bVNlhuHSHS97iHbA==} engines: {node: '>=18.0.0', npm: '>=6.0.0'} peerDependencies: '@stencil/core': '>=2.0.0 || >=3.0.0 || >= 4.0.0-beta.0 || >= 4.0.0' @@ -4705,8 +4710,8 @@ packages: resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} engines: {node: '>= 4'} - dompurify@3.2.6: - resolution: {integrity: sha512-/2GogDQlohXPZe6D6NOgQvXLPSYBqIWMnZ8zzOhn09REE4eyAzb+Hed3jhoM9OkuaJ8P6ZGTTVWQKAi8ieIzfQ==} + dompurify@3.3.0: + resolution: {integrity: sha512-r+f6MYR1gGN1eJv0TVQbhA7if/U7P87cdPl3HN5rikqaBSBxLiCb/b9O+2eG0cxz0ghyU+mU1QkbsOwERMYlWQ==} domutils@2.8.0: resolution: {integrity: sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==} @@ -5530,8 +5535,8 @@ packages: engines: {node: '>=12'} hasBin: true - html-react-parser@5.2.5: - resolution: {integrity: sha512-bRPdv8KTqG9CEQPMNGksDqmbiRfVQeOidry8pVetdh/1jQ1Edx4KX5m0lWvDD89Pt4CqTYjK1BLz6NoNVxN/Uw==} + html-react-parser@5.2.7: + resolution: {integrity: sha512-WzIAcqQoZoF49J9aev8NBDLz9TJvt2RmipeYA+/5+5x0sWCwFxqKiq0lysieiSA/G6dbUZ6KGGy65Cx2fjie5Q==} peerDependencies: '@types/react': 0.14 || 15 || 16 || 17 || 18 || 19 react: 0.14 || 15 || 16 || 17 || 18 || 19 @@ -5543,6 +5548,9 @@ packages: resolution: {integrity: sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==} engines: {node: '>=8'} + html-url-attributes@3.0.1: + resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==} + html-void-elements@3.0.0: resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} @@ -6046,14 +6054,14 @@ packages: resolution: {integrity: sha512-1wd/kpAdKRLwv7/3OKC8zZ5U8e/fajCfWMxacUvB79S5nLrYGPtUI/8chMQhn3LQjsRVErTb9i1ECAwW0ZIHnQ==} engines: {node: '>=20.0.0'} - lit-element@4.2.0: - resolution: {integrity: sha512-MGrXJVAI5x+Bfth/pU9Kst1iWID6GHDLEzFEnyULB/sFiRLgkd8NPK/PeeXxktA3T6EIIaq8U3KcbTU5XFcP2Q==} + lit-element@4.2.1: + resolution: {integrity: sha512-WGAWRGzirAgyphK2urmYOV72tlvnxw7YfyLDgQ+OZnM9vQQBQnumQ7jUJe6unEzwGU3ahFOjuz1iz1jjrpCPuw==} - lit-html@3.3.0: - resolution: {integrity: sha512-RHoswrFAxY2d8Cf2mm4OZ1DgzCoBKUKSPvA1fhtSELxUERq2aQQ2h05pO9j81gS1o7RIRJ+CePLogfyahwmynw==} + lit-html@3.3.1: + resolution: {integrity: sha512-S9hbyDu/vs1qNrithiNyeyv64c9yqiW9l+DBgI18fL+MTvOtWoFR0FWiyq1TxaYef5wNlpEmzlXoBlZEO+WjoA==} - lit@3.3.0: - resolution: {integrity: sha512-DGVsqsOIHBww2DqnuZzW7QsuCdahp50ojuDaBPC7jUDRpYoH0z7kHBBYZewRzer75FwtrkmkKk7iOAwSaWdBmw==} + lit@3.3.1: + resolution: {integrity: sha512-Ksr/8L3PTapbdXJCk+EJVB78jDodUMaP54gD24W186zGRARvwrsPfS60wae/SSCTCNZVPd1chXqio1qHQmu4NA==} load-plugin@6.0.3: resolution: {integrity: sha512-kc0X2FEUZr145odl68frm+lMJuQ23+rTXYmR6TImqPtbpmXC4vVXbWKDQ9IzndA0HfyQamWfKLhzsqGSTxE63w==} @@ -7019,6 +7027,11 @@ packages: resolution: {integrity: sha512-66hKPCr+72mlfiSjlEB1+45IjXSqvVAIy6mocupoww4tBFE9R9IhwwUGoI4G++Tc9Aq+2rxOt0RFU6gPcrte0A==} engines: {node: '>= 0.8'} + prism-react-renderer@1.3.5: + resolution: {integrity: sha512-IJ+MSwBWKG+SM3b2SUfdrhC+gu01QkV2KmRQgREThBfSQRoufqRfxfHUxpG1WcaFjP+kojcFyO9Qqtpgt3qLCg==} + peerDependencies: + react: '>=0.14.9' + proc-log@4.2.0: resolution: {integrity: sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} @@ -7105,6 +7118,12 @@ packages: react-is@17.0.2: resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} + react-markdown@10.1.0: + resolution: {integrity: sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==} + peerDependencies: + '@types/react': '>=18' + react: '>=18' + react-property@2.0.2: resolution: {integrity: sha512-+PbtI3VuDV0l6CleQMsx2gtK0JZbZKbpdu5ynr+lbsuvtmgbNcS3VM0tuY2QjFNOcWxvXeHjDpy42RO+4U2rug==} @@ -7628,8 +7647,8 @@ packages: sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} - sse.js@2.6.0: - resolution: {integrity: sha512-eGEqOwiPX9Cm+KsOYkcz7HIEqWUSOFeChr0sT515hDOBLvQy5yxaLSZx9JWMhwjf75CXJq+7cgG1MKNh9GQ36w==} + sse.js@2.7.2: + resolution: {integrity: sha512-EDu2kt+A+HIagwUOCrDbKfsUrKxNWdoeKQVKfpruRSO34yL2xwS142Ad3AYhjCY81oWySeCsPs0wSu+c2QG14g==} stable-hash-x@0.2.0: resolution: {integrity: sha512-o3yWv49B/o4QZk5ZcsALc6t0+eCelPc44zZsLtCQnZPDwFpDYSWcDnrv2TtMmMbQ7uKo3J0HTURCqckw23czNQ==} @@ -7779,14 +7798,14 @@ packages: style-object-to-css-string@1.1.3: resolution: {integrity: sha512-bISQoUsir/qGfo7vY8rw00ia9nnyE1jvYt3zZ2jhdkcXZ6dAEi74inMzQ6On57vFI+I4Fck6wOv5UI9BEwJDgw==} - style-to-js@1.1.16: - resolution: {integrity: sha512-/Q6ld50hKYPH3d/r6nr117TZkHR0w0kGGIVfpG9N6D8NymRPM9RqCUv4pRpJ62E5DqOYx2AFpbZMyCPnjQCnOw==} - style-to-js@1.1.17: resolution: {integrity: sha512-xQcBGDxJb6jjFCTzvQtfiPn6YvvP2O8U1MDIPNfJQlWMYfktPy+iGsHE7cssjs7y84d9fQaK4UF3RIJaAHSoYA==} - style-to-object@1.0.8: - resolution: {integrity: sha512-xT47I/Eo0rwJmaXC4oilDGDWLohVhR6o/xAQcPQN8q6QBuZVL8qMYL85kLmST5cPjAorwvqIA4qXTRQoYHaL6g==} + style-to-js@1.1.18: + resolution: {integrity: sha512-JFPn62D4kJaPTnhFUI244MThx+FEGbi+9dw1b9yBBQ+1CZpV7QAT8kUtJ7b7EUNdHajjF/0x8fT+16oLJoojLg==} + + style-to-object@1.0.11: + resolution: {integrity: sha512-5A560JmXr7wDyGLK12Nq/EYS38VkGlglVzkis1JEdbGWSnbQIEhZzTJhzURXN5/8WwwFCs/f/VVcmkTppbXLow==} style-to-object@1.0.9: resolution: {integrity: sha512-G4qppLgKu/k6FwRpHiGiKPaPTFcG3g4wNVX/Qsfu+RqQM30E7Tyu/TEgxcL9PNLF5pdRLwQdE3YKKf+KF2Dzlw==} @@ -7923,6 +7942,10 @@ packages: thenby@1.3.4: resolution: {integrity: sha512-89Gi5raiWA3QZ4b2ePcEwswC3me9JIg+ToSgtE0JWeCynLnLxNr/f9G+xfo9K+Oj4AFdom8YNJjibIARTJmapQ==} + throttleit@2.1.0: + resolution: {integrity: sha512-nt6AMGKW1p/70DF/hGBdJB57B8Tspmbp5gfJ8ilhLnt7kkr2ye7hzD6NVG8GGErk2HWF34igrL2CXmNIkzKqKw==} + engines: {node: '>=18'} + tiny-invariant@1.3.3: resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} @@ -7942,11 +7965,11 @@ packages: resolution: {integrity: sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==} engines: {node: '>=14.0.0'} - tldts-core@7.0.17: - resolution: {integrity: sha512-DieYoGrP78PWKsrXr8MZwtQ7GLCUeLxihtjC1jZsW1DnvSMdKPitJSe8OSYDM2u5H6g3kWJZpePqkp43TfLh0g==} + tldts-core@7.0.16: + resolution: {integrity: sha512-XHhPmHxphLi+LGbH0G/O7dmUH9V65OY20R7vH8gETHsp5AZCjBk9l8sqmRKLaGOxnETU7XNSDUPtewAy/K6jbA==} - tldts@7.0.17: - resolution: {integrity: sha512-Y1KQBgDd/NUc+LfOtKS6mNsC9CCaH+m2P1RoIZy7RAPo3C3/t8X45+zgut31cRZtZ3xKPjfn3TkGTrctC2TQIQ==} + tldts@7.0.16: + resolution: {integrity: sha512-5bdPHSwbKTeHmXrgecID4Ljff8rQjv7g8zKQPkCozRo2HWWni+p310FSn5ImI+9kWw9kK4lzOB5q/a6iv0IJsw==} hasBin: true to-regex-range@5.0.1: @@ -9800,7 +9823,9 @@ snapshots: dependencies: '@csstools/css-tokenizer': 3.0.4 - '@csstools/css-syntax-patches-for-csstree@1.0.15': {} + '@csstools/css-syntax-patches-for-csstree@1.0.14(postcss@8.5.3)': + dependencies: + postcss: 8.5.3 '@csstools/css-tokenizer@3.0.4': {} @@ -10400,15 +10425,15 @@ snapshots: '@keyv/serialize@1.1.1': {} - '@lit-labs/ssr-dom-shim@1.3.0': {} + '@lit-labs/ssr-dom-shim@1.4.0': {} - '@lit/react@1.0.7(@types/react@19.2.2)': + '@lit/react@1.0.8(@types/react@19.2.2)': dependencies: '@types/react': 19.2.2 - '@lit/reactive-element@2.1.0': + '@lit/reactive-element@2.1.1': dependencies: - '@lit-labs/ssr-dom-shim': 1.3.0 + '@lit-labs/ssr-dom-shim': 1.4.0 '@mdx-js/mdx@3.1.1': dependencies: @@ -10573,7 +10598,7 @@ snapshots: dependencies: gzip-size: 6.0.0 - '@nodejs/doc-kit@https://codeload.github.com/nodejs/doc-kit/tar.gz/c35cb2fd9f90945c7d3ff24ecc2f64849cce6928(@types/react@19.2.2)(eslint@9.36.0(jiti@2.6.1))(postcss@8.5.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.8.3)': + '@nodejs/doc-kit@https://codeload.github.com/nodejs/doc-kit/tar.gz/f132fcb2f50adc32ce24e8517dcf616b757153df(@stencil/core@4.30.0)(@types/react@19.2.2)(eslint@9.36.0(jiti@2.6.1))(postcss@8.5.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.8.3)': dependencies: '@actions/core': 1.11.1 '@clack/prompts': 0.11.0 @@ -10824,6 +10849,13 @@ snapshots: transitivePeerDependencies: - babel-plugin-macros + '@orama/core@1.2.13': + dependencies: + '@orama/cuid2': 2.2.3 + '@orama/oramacore-events-parser': 0.0.5 + zod: 3.24.3 + zod-to-json-schema: 3.24.5(zod@3.24.3) + '@orama/cuid2@2.2.3': dependencies: '@noble/hashes': 1.8.0 @@ -10832,9 +10864,7 @@ snapshots: '@orama/orama@3.1.16': {} - '@orama/orama@3.1.6': {} - - '@orama/orama@3.1.9': {} + '@orama/oramacore-events-parser@0.0.5': {} '@orama/react-components@0.8.1(@stencil/core@4.30.0)(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: @@ -10847,43 +10877,55 @@ snapshots: - '@types/react' - babel-plugin-macros - '@orama/switch@3.1.9(@orama/core@0.1.11)(@orama/orama@3.1.9)(@oramacloud/client@2.1.4)': + '@orama/switch@3.1.9(@orama/core@0.1.11)(@orama/orama@3.1.16)(@oramacloud/client@2.1.4)': dependencies: '@orama/core': 0.1.11 - '@orama/orama': 3.1.9 + '@orama/orama': 3.1.16 '@oramacloud/client': 2.1.4 + '@orama/ui@1.3.2(@orama/core@1.2.13)(@types/react@19.2.2)(react@19.2.0)': + dependencies: + '@orama/core': 1.2.13 + prism-react-renderer: 1.3.5(react@19.2.0) + react-markdown: 10.1.0(@types/react@19.2.2)(react@19.2.0) + remark-gfm: 4.0.1 + throttleit: 2.1.0 + transitivePeerDependencies: + - '@types/react' + - react + - supports-color + '@orama/wc-components@0.8.1': dependencies: '@orama/core': 0.1.11 '@orama/highlight': 0.1.9 - '@orama/orama': 3.1.9 - '@orama/switch': 3.1.9(@orama/core@0.1.11)(@orama/orama@3.1.9)(@oramacloud/client@2.1.4) + '@orama/orama': 3.1.16 + '@orama/switch': 3.1.9(@orama/core@0.1.11)(@orama/orama@3.1.16)(@oramacloud/client@2.1.4) '@oramacloud/client': 2.1.4 '@phosphor-icons/webcomponents': 2.1.5 '@stencil/core': 4.30.0 - '@stencil/store': 2.1.3(@stencil/core@4.30.0) - dompurify: 3.2.6 + '@stencil/store': 2.2.0(@stencil/core@4.30.0) + dompurify: 3.3.0 highlight.js: 11.11.1 markdown-it: 14.1.0 marked: 13.0.3 marked-highlight: 2.2.2(marked@13.0.3) shiki: 1.29.2 - sse.js: 2.6.0 + sse.js: 2.7.2 transitivePeerDependencies: - babel-plugin-macros '@oramacloud/client@2.1.4': dependencies: '@orama/cuid2': 2.2.3 - '@orama/orama': 3.1.6 + '@orama/orama': 3.1.16 lodash: 4.17.21 '@oxc-project/types@0.94.0': {} '@phosphor-icons/webcomponents@2.1.5': dependencies: - lit: 3.3.0 + lit: 3.3.1 '@pkgjs/parseargs@0.11.0': optional: true @@ -12057,9 +12099,9 @@ snapshots: '@stencil/react-output-target@0.8.2(@stencil/core@4.30.0)(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: - '@lit/react': 1.0.7(@types/react@19.2.2) + '@lit/react': 1.0.8(@types/react@19.2.2) '@stencil/core': 4.30.0 - html-react-parser: 5.2.5(@types/react@19.2.2)(react@19.2.0) + html-react-parser: 5.2.7(@types/react@19.2.2)(react@19.2.0) react: 19.2.0 react-dom: 19.2.0(react@19.2.0) style-object-to-css-string: 1.1.3 @@ -12067,7 +12109,7 @@ snapshots: transitivePeerDependencies: - '@types/react' - '@stencil/store@2.1.3(@stencil/core@4.30.0)': + '@stencil/store@2.2.0(@stencil/core@4.30.0)': dependencies: '@stencil/core': 4.30.0 @@ -13431,11 +13473,13 @@ snapshots: cssesc@3.0.0: {} - cssstyle@5.3.2: + cssstyle@5.3.2(postcss@8.5.3): dependencies: '@asamuzakjp/css-color': 4.0.5 - '@csstools/css-syntax-patches-for-csstree': 1.0.15 + '@csstools/css-syntax-patches-for-csstree': 1.0.14(postcss@8.5.3) css-tree: 3.1.0 + transitivePeerDependencies: + - postcss csstype@3.1.3: {} @@ -13574,7 +13618,7 @@ snapshots: dependencies: domelementtype: 2.3.0 - dompurify@3.2.6: + dompurify@3.3.0: optionalDependencies: '@types/trusted-types': 2.0.7 @@ -13864,7 +13908,7 @@ snapshots: eslint: 9.36.0(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.46.1(eslint@9.36.0(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint@9.36.0(jiti@2.6.1)))(eslint-plugin-import@2.32.0)(eslint@9.36.0(jiti@2.6.1)) - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.45.0(eslint@9.36.0(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-typescript@4.4.4)(eslint@9.36.0(jiti@2.6.1)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.45.0(eslint@9.36.0(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-typescript@4.4.4(eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.46.1(eslint@9.36.0(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint@9.36.0(jiti@2.6.1)))(eslint-plugin-import@2.32.0)(eslint@9.36.0(jiti@2.6.1)))(eslint@9.36.0(jiti@2.6.1)) eslint-plugin-jsx-a11y: 6.10.2(eslint@9.36.0(jiti@2.6.1)) eslint-plugin-react: 7.37.5(eslint@9.36.0(jiti@2.6.1)) eslint-plugin-react-hooks: 5.2.0(eslint@9.36.0(jiti@2.6.1)) @@ -13901,7 +13945,7 @@ snapshots: tinyglobby: 0.2.15 unrs-resolver: 1.11.1 optionalDependencies: - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.45.0(eslint@9.36.0(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-typescript@4.4.4)(eslint@9.36.0(jiti@2.6.1)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.45.0(eslint@9.36.0(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-typescript@4.4.4(eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.46.1(eslint@9.36.0(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint@9.36.0(jiti@2.6.1)))(eslint-plugin-import@2.32.0)(eslint@9.36.0(jiti@2.6.1)))(eslint@9.36.0(jiti@2.6.1)) eslint-plugin-import-x: 4.16.1(@typescript-eslint/utils@8.46.1(eslint@9.36.0(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint@9.36.0(jiti@2.6.1)) transitivePeerDependencies: - supports-color @@ -13917,7 +13961,7 @@ snapshots: tinyglobby: 0.2.14 unrs-resolver: 1.11.1 optionalDependencies: - eslint-plugin-import: 2.32.0(eslint-import-resolver-typescript@4.4.4)(eslint@9.36.0(jiti@2.6.1)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.45.0(eslint@9.36.0(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-typescript@4.4.4)(eslint@9.36.0(jiti@2.6.1)) eslint-plugin-import-x: 4.16.1(@typescript-eslint/utils@8.46.1(eslint@9.36.0(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint@9.36.0(jiti@2.6.1)) transitivePeerDependencies: - supports-color @@ -13944,7 +13988,18 @@ snapshots: - bluebird - supports-color - eslint-module-utils@2.12.1(@typescript-eslint/parser@8.45.0(eslint@9.36.0(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@4.4.4)(eslint@9.36.0(jiti@2.6.1)): + eslint-module-utils@2.12.1(@typescript-eslint/parser@8.45.0(eslint@9.36.0(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.46.1(eslint@9.36.0(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint@9.36.0(jiti@2.6.1)))(eslint-plugin-import@2.32.0)(eslint@9.36.0(jiti@2.6.1)))(eslint@9.36.0(jiti@2.6.1)): + dependencies: + debug: 3.2.7 + optionalDependencies: + '@typescript-eslint/parser': 8.45.0(eslint@9.36.0(jiti@2.6.1))(typescript@5.8.3) + eslint: 9.36.0(jiti@2.6.1) + eslint-import-resolver-node: 0.3.9 + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.46.1(eslint@9.36.0(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint@9.36.0(jiti@2.6.1)))(eslint-plugin-import@2.32.0)(eslint@9.36.0(jiti@2.6.1)) + transitivePeerDependencies: + - supports-color + + eslint-module-utils@2.12.1(@typescript-eslint/parser@8.45.0(eslint@9.36.0(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@4.4.4(eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.46.1(eslint@9.36.0(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint@9.36.0(jiti@2.6.1)))(eslint-plugin-import@2.32.0)(eslint@9.36.0(jiti@2.6.1)))(eslint@9.36.0(jiti@2.6.1)): dependencies: debug: 3.2.7 optionalDependencies: @@ -13954,6 +14009,7 @@ snapshots: eslint-import-resolver-typescript: 4.4.4(eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.46.1(eslint@9.36.0(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint@9.36.0(jiti@2.6.1)))(eslint-plugin-import@2.32.0)(eslint@9.36.0(jiti@2.6.1)) transitivePeerDependencies: - supports-color + optional: true eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.46.1(eslint@9.36.0(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint@9.36.0(jiti@2.6.1)): dependencies: @@ -13973,7 +14029,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.45.0(eslint@9.36.0(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-typescript@4.4.4)(eslint@9.36.0(jiti@2.6.1)): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.45.0(eslint@9.36.0(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-typescript@4.4.4(eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.46.1(eslint@9.36.0(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint@9.36.0(jiti@2.6.1)))(eslint-plugin-import@2.32.0)(eslint@9.36.0(jiti@2.6.1)))(eslint@9.36.0(jiti@2.6.1)): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -13984,7 +14040,7 @@ snapshots: doctrine: 2.1.0 eslint: 9.36.0(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.45.0(eslint@9.36.0(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@4.4.4)(eslint@9.36.0(jiti@2.6.1)) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.45.0(eslint@9.36.0(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.46.1(eslint@9.36.0(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint@9.36.0(jiti@2.6.1)))(eslint-plugin-import@2.32.0)(eslint@9.36.0(jiti@2.6.1)))(eslint@9.36.0(jiti@2.6.1)) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -14002,7 +14058,7 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-plugin-import@2.32.0(eslint-import-resolver-typescript@4.4.4)(eslint@9.36.0(jiti@2.6.1)): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.45.0(eslint@9.36.0(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-typescript@4.4.4)(eslint@9.36.0(jiti@2.6.1)): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -14013,7 +14069,7 @@ snapshots: doctrine: 2.1.0 eslint: 9.36.0(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.45.0(eslint@9.36.0(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@4.4.4)(eslint@9.36.0(jiti@2.6.1)) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.45.0(eslint@9.36.0(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@4.4.4(eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.46.1(eslint@9.36.0(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint@9.36.0(jiti@2.6.1)))(eslint-plugin-import@2.32.0)(eslint@9.36.0(jiti@2.6.1)))(eslint@9.36.0(jiti@2.6.1)) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -14024,6 +14080,8 @@ snapshots: semver: 6.3.1 string.prototype.trimend: 1.0.9 tsconfig-paths: 3.15.0 + optionalDependencies: + '@typescript-eslint/parser': 8.45.0(eslint@9.36.0(jiti@2.6.1))(typescript@5.8.3) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack @@ -14595,9 +14653,9 @@ snapshots: minipass: 4.2.8 path-scurry: 1.11.1 - global-jsdom@27.0.0(jsdom@27.1.0): + global-jsdom@27.0.0(jsdom@27.1.0(postcss@8.5.3)): dependencies: - jsdom: 27.1.0 + jsdom: 27.1.0(postcss@8.5.3) global-modules@2.0.0: dependencies: @@ -14828,18 +14886,20 @@ snapshots: relateurl: 0.2.7 terser: 5.44.0 - html-react-parser@5.2.5(@types/react@19.2.2)(react@19.2.0): + html-react-parser@5.2.7(@types/react@19.2.2)(react@19.2.0): dependencies: domhandler: 5.0.3 html-dom-parser: 5.1.1 react: 19.2.0 react-property: 2.0.2 - style-to-js: 1.1.16 + style-to-js: 1.1.18 optionalDependencies: '@types/react': 19.2.2 html-tags@3.3.1: {} + html-url-attributes@3.0.1: {} + html-void-elements@3.0.0: {} html-webpack-plugin@5.6.4(webpack@5.102.0(@swc/core@1.13.5)(esbuild@0.25.10)): @@ -15174,11 +15234,11 @@ snapshots: dependencies: argparse: 2.0.1 - jsdom@27.1.0: + jsdom@27.1.0(postcss@8.5.3): dependencies: '@acemir/cssom': 0.9.19 '@asamuzakjp/dom-selector': 6.7.4 - cssstyle: 5.3.2 + cssstyle: 5.3.2(postcss@8.5.3) data-urls: 6.0.0 decimal.js: 10.6.0 html-encoding-sniffer: 4.0.0 @@ -15198,6 +15258,7 @@ snapshots: xml-name-validator: 5.0.0 transitivePeerDependencies: - bufferutil + - postcss - supports-color - utf-8-validate @@ -15341,21 +15402,21 @@ snapshots: rfdc: 1.4.1 wrap-ansi: 9.0.2 - lit-element@4.2.0: + lit-element@4.2.1: dependencies: - '@lit-labs/ssr-dom-shim': 1.3.0 - '@lit/reactive-element': 2.1.0 - lit-html: 3.3.0 + '@lit-labs/ssr-dom-shim': 1.4.0 + '@lit/reactive-element': 2.1.1 + lit-html: 3.3.1 - lit-html@3.3.0: + lit-html@3.3.1: dependencies: '@types/trusted-types': 2.0.7 - lit@3.3.0: + lit@3.3.1: dependencies: - '@lit/reactive-element': 2.1.0 - lit-element: 4.2.0 - lit-html: 3.3.0 + '@lit/reactive-element': 2.1.1 + lit-element: 4.2.1 + lit-html: 3.3.1 load-plugin@6.0.3: dependencies: @@ -16547,6 +16608,10 @@ snapshots: pretty-hrtime@1.0.3: {} + prism-react-renderer@1.3.5(react@19.2.0): + dependencies: + react: 19.2.0 + proc-log@4.2.0: {} promise-inflight@1.0.1: {} @@ -16630,6 +16695,24 @@ snapshots: react-is@17.0.2: {} + react-markdown@10.1.0(@types/react@19.2.2)(react@19.2.0): + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@types/react': 19.2.2 + devlop: 1.1.0 + hast-util-to-jsx-runtime: 2.3.6 + html-url-attributes: 3.0.1 + mdast-util-to-hast: 13.2.0 + react: 19.2.0 + remark-parse: 11.0.0 + remark-rehype: 11.1.2 + unified: 11.0.5 + unist-util-visit: 5.0.0 + vfile: 6.0.3 + transitivePeerDependencies: + - supports-color + react-property@2.0.2: {} react-remove-scroll-bar@2.3.8(@types/react@19.2.2)(react@19.2.0): @@ -17622,7 +17705,7 @@ snapshots: sprintf-js@1.0.3: {} - sse.js@2.6.0: {} + sse.js@2.7.2: {} stable-hash-x@0.2.0: {} @@ -17799,15 +17882,15 @@ snapshots: style-object-to-css-string@1.1.3: {} - style-to-js@1.1.16: - dependencies: - style-to-object: 1.0.8 - style-to-js@1.1.17: dependencies: style-to-object: 1.0.9 - style-to-object@1.0.8: + style-to-js@1.1.18: + dependencies: + style-to-object: 1.0.11 + + style-to-object@1.0.11: dependencies: inline-style-parser: 0.2.4 @@ -17973,6 +18056,8 @@ snapshots: thenby@1.3.4: {} + throttleit@2.1.0: {} + tiny-invariant@1.3.3: {} tinyglobby@0.2.14: @@ -17989,11 +18074,11 @@ snapshots: tinyspy@4.0.4: {} - tldts-core@7.0.17: {} + tldts-core@7.0.16: {} - tldts@7.0.17: + tldts@7.0.16: dependencies: - tldts-core: 7.0.17 + tldts-core: 7.0.16 to-regex-range@5.0.1: dependencies: @@ -18003,7 +18088,7 @@ snapshots: tough-cookie@6.0.0: dependencies: - tldts: 7.0.17 + tldts: 7.0.16 tr46@0.0.3: {}
{interaction?.query}
+ {t('components.search.noResultsFoundFor')} "{term}" +
+ {t('components.search.suggestions')} +
+ {getFormattedPath(document.path, document.pageSectionTitle)} +