diff --git a/website/src/components/FeedbackComponent.js b/website/src/components/FeedbackComponent.js index 6077e2fad48..11437c5fe1d 100644 --- a/website/src/components/FeedbackComponent.js +++ b/website/src/components/FeedbackComponent.js @@ -6,7 +6,8 @@ export function FeedbackComponent() { const [comment, setComment] = useState(''); const [showComment, setShowComment] = useState(false); - const handleFeedback = (type) => { + const handleFeedback = (event, type) => { + event.preventDefault(); setFeedback(type); setShowComment(true); }; @@ -34,38 +35,40 @@ export function FeedbackComponent() { }; if (isSubmitted) { - return ( + return <> +
Thanks for your feedback!
- ); + } - return ( + return <> +
{!showComment ? (
Was this page helpful?
- - + 👎 No +
) : ( @@ -93,5 +96,5 @@ export function FeedbackComponent() {
)} - ); + ; } diff --git a/website/src/theme/DocItem/Layout/index.js b/website/src/theme/DocItem/Layout/index.js index 873fecb1f36..489c6838da0 100644 --- a/website/src/theme/DocItem/Layout/index.js +++ b/website/src/theme/DocItem/Layout/index.js @@ -15,6 +15,7 @@ import styles from './styles.module.css'; import { FeedbackComponent } from '../../../components/FeedbackComponent'; +import ChatbotTrigger from '../../SearchBar/components/ChatbotTrigger'; /** * Decide if the toc should be rendered, on mobile or desktop viewports @@ -57,6 +58,7 @@ export default function DocItemLayout({ children }) { {children} {!isMain && <> + diff --git a/website/src/theme/SearchBar/components/AIChatInSearch.tsx b/website/src/theme/SearchBar/components/AIChatInSearch.tsx index 40769cf6eb4..95478c6ba6d 100644 --- a/website/src/theme/SearchBar/components/AIChatInSearch.tsx +++ b/website/src/theme/SearchBar/components/AIChatInSearch.tsx @@ -15,6 +15,11 @@ interface AIChatInSearchProps { onSaveConversation?: (data: SavedConversation) => void; onClearConversation?: () => void; savedConversation?: SavedConversation | null; + initialMessage?: string | null; +} + +function getDisplayText(text: string): string { + return text.replace(/\n\n\[Page:[^\]]*\]$/, '').trim(); } const SUGGESTIONS = [ @@ -29,6 +34,7 @@ export default function AIChatInSearch({ onSaveConversation, onClearConversation, savedConversation, + initialMessage, }: AIChatInSearchProps) { const { colorMode } = useColorMode(); @@ -42,6 +48,14 @@ export default function AIChatInSearch({ const messagesEndRef = useRef(null); const inputRef = useRef(null); + const initialMessageSentRef = useRef(false); + + useEffect(() => { + if (initialMessage && !initialMessageSentRef.current) { + initialMessageSentRef.current = true; + sendMessage(initialMessage); + } + }, [initialMessage, sendMessage]); const prevIsLoadingRef = useRef(false); useEffect(() => { @@ -113,14 +127,14 @@ export default function AIChatInSearch({ {index > 0 &&
}
-

{turn.userText}

+

{getDisplayText(turn.userText)}


- {turn.assistantText?.trim() ? ( + {turn.assistantText? (
diff --git a/website/src/theme/SearchBar/components/ChatbotTrigger.tsx b/website/src/theme/SearchBar/components/ChatbotTrigger.tsx new file mode 100644 index 00000000000..420e230de41 --- /dev/null +++ b/website/src/theme/SearchBar/components/ChatbotTrigger.tsx @@ -0,0 +1,99 @@ +import React, { useEffect, useState, useRef } from 'react'; +import { ArrowUp } from 'lucide-react'; +import { useLocation } from '@docusaurus/router'; +import { useWindowSize } from '@docusaurus/theme-common'; +import styles from '../styles.module.css'; + +export default function ChatbotTrigger() { + const [value, setValue] = useState(''); + const [isStuck, setIsStuck] = useState(false); + const containerRef = useRef(null); + const textareaRef = useRef(null); + const windowSize = useWindowSize(); + const { pathname } = useLocation(); + + useEffect(() => { + if (windowSize === 'mobile') { + setIsStuck(false); + return; + } + + const updateStuckState = () => { + const element = containerRef.current; + if (!element) return; + + const rect = element.getBoundingClientRect(); + const stickyBottom = Number.parseFloat(getComputedStyle(element).bottom) || 0; + const viewportBottom = window.innerHeight; + const targetBottom = viewportBottom - stickyBottom; + + setIsStuck(Math.abs(rect.bottom - targetBottom) < 1.5); + }; + + updateStuckState(); + window.addEventListener('scroll', updateStuckState, { passive: true }); + window.addEventListener('resize', updateStuckState); + + return () => { + window.removeEventListener('scroll', updateStuckState); + window.removeEventListener('resize', updateStuckState); + }; + }, [windowSize]); + + const getPageMdUrl = () => { + const clean = pathname.replace(/\/$/, ''); + return `${clean}.md`; + }; + + const submit = () => { + const text = value.trim(); + if (!text) return; + setValue(''); + const message = `${text}\n\n[Page: ${getPageMdUrl()}]`; + console.log(message); + + window.dispatchEvent(new CustomEvent('open-chatbot', { detail: { message } })); + }; + + const handleKeyDown = (e: React.KeyboardEvent) => { + if (e.key === 'Enter' && !e.shiftKey) { + e.preventDefault(); + submit(); + } + }; + + if (windowSize === 'mobile') { + return null; + } + + return <> +
+
+