docs: improve AI Assistant (#11208)

* initial implementation

* integrate new ai assistant in other projects + ux improvements

* fix chat window on mobile devices

* fixes to mobile

* allow pre

* change shortcut to i

* improved responsiveness

* align version in navbar
This commit is contained in:
Shahed Nasser
2025-01-29 19:13:51 +02:00
committed by GitHub
parent 51d2960a57
commit 5634a4762b
57 changed files with 1571 additions and 743 deletions
+1
View File
@@ -1,4 +1,5 @@
export * from "./use-active-on-scroll"
export * from "./use-ai-assistant-chat-navigation"
export * from "./use-child-docs"
export * from "./use-click-outside"
export * from "./use-collapsible"
@@ -0,0 +1,141 @@
"use client"
import { useCallback, useEffect, useMemo } from "react"
import { useAiAssistant } from "@/providers"
import { findNextSibling, findPrevSibling } from "@/utils"
import {
useKeyboardShortcut,
type useKeyboardShortcutOptions,
} from "../use-keyboard-shortcut"
export type UseAiAssistantChatNavigationProps = {
getChatWindowElm: () => HTMLElement | null
getInputElm: () => HTMLTextAreaElement | null
focusInput: () => void
keyboardProps?: Partial<useKeyboardShortcutOptions>
}
export const useAiAssistantChatNavigation = ({
getInputElm,
focusInput,
keyboardProps,
getChatWindowElm,
}: UseAiAssistantChatNavigationProps) => {
const shortcutKeys = useMemo(() => ["ArrowUp", "ArrowDown", "Enter"], [])
const { chatOpened } = useAiAssistant()
const handleKeyAction = (e: KeyboardEvent) => {
const chatElm = getChatWindowElm()
if (
!chatOpened ||
e.metaKey ||
e.ctrlKey ||
!chatElm?.contains(document.activeElement)
) {
return
}
e.preventDefault()
const focusedItem = chatElm?.querySelector(":focus") as HTMLElement
if (!focusedItem) {
// focus the first data-hit
const nextItem = chatElm?.querySelector("[data-hit]") as HTMLElement
nextItem?.focus()
return
}
const isHit = focusedItem.hasAttribute("data-hit")
const isInput = focusedItem.tagName.toLowerCase() === "textarea"
if (!isHit && !isInput) {
// ignore if focused items aren't input/data-hit
return
}
const lowerPressedKey = e.key.toLowerCase()
if (lowerPressedKey === "enter") {
if (isHit) {
// trigger click event of the focused element
focusedItem.click()
}
return
}
if (lowerPressedKey === "arrowdown") {
// only hit items has action on arrow down
if (isHit) {
// find if there's a data-hit item before this one
const beforeItem = findNextSibling(focusedItem, "[data-hit]")
if (!beforeItem) {
// focus the input
focusInput()
} else {
// focus the previous item
beforeItem.focus()
}
}
} else if (lowerPressedKey === "arrowup") {
// check if item is input or hit
if (isInput) {
// go to the first data-hit item
const nextItem = chatElm?.querySelector(
"[data-hit]:last-child"
) as HTMLElement
nextItem?.focus()
} else {
// handle go down for hit items
// find if there's a data-hit item after this one
const afterItem = findPrevSibling(focusedItem, "[data-hit]")
if (afterItem) {
// focus the next item
afterItem.focus()
}
}
}
}
/** Handles starting to type which focuses the input */
const handleKeyDown = useCallback(
(e: KeyboardEvent) => {
if (!chatOpened) {
return
}
// check if shortcut keys were pressed
const lowerPressedKey = e.key.toLowerCase()
const pressedShortcut = [...shortcutKeys, "Escape"].some(
(s) => s.toLowerCase() === lowerPressedKey
)
if (pressedShortcut) {
return
}
const chatElm = getChatWindowElm()
if (!chatElm?.contains(document.activeElement)) {
return
}
const focusedItem = chatElm?.querySelector(":focus") as HTMLElement
const inputElm = getInputElm()
if (inputElm && focusedItem !== inputElm) {
focusInput()
}
},
[shortcutKeys, chatOpened, shortcutKeys, getInputElm, focusInput]
)
useEffect(() => {
window.addEventListener("keydown", handleKeyDown)
return () => {
window.removeEventListener("keydown", handleKeyDown)
}
}, [handleKeyDown])
useKeyboardShortcut({
metakey: false,
shortcutKeys: shortcutKeys,
checkEditing: false,
isLoading: false,
action: handleKeyAction,
...keyboardProps,
})
}
@@ -379,10 +379,10 @@ export const useChildDocs = ({
{!searchResult.length && (
<div className="flex flex-col justify-center items-center gap-docs_0.75">
<ExclamationCircle className="text-medusa-fg-subtle" />
<span className="text-compact-small-plus text-medusa-fg-subtle text-center">
<span className="text-compact-small-plus text-medusa-fg-base text-center">
No results found matching your query.
</span>
<span className="text-compact-small text-medusa-fg-muted text-center">
<span className="text-compact-small text-medusa-fg-subtle text-center">
Try searching with another term or clearing the search.
</span>
</div>
@@ -40,7 +40,7 @@ export const useKeyboardShortcut = ({
[shortcutKeys]
)
const sidebarShortcut = useCallback(
const onKeyDown = useCallback(
(e: KeyboardEvent) => {
// the event is triggered when an input
// autocompletes, and in that case
@@ -63,10 +63,10 @@ export const useKeyboardShortcut = ({
)
useEffect(() => {
window.addEventListener("keydown", sidebarShortcut)
window.addEventListener("keydown", onKeyDown)
return () => {
window.removeEventListener("keydown", sidebarShortcut)
window.removeEventListener("keydown", onKeyDown)
}
}, [sidebarShortcut])
}, [onKeyDown])
}