diff --git a/public/chat_flyout.tsx b/public/chat_flyout.tsx index df5f3405..f0c538c7 100644 --- a/public/chat_flyout.tsx +++ b/public/chat_flyout.tsx @@ -18,7 +18,7 @@ interface ChatFlyoutProps { overrideComponent: React.ReactNode | null; flyoutProps: Partial>; flyoutFullScreen: boolean; - toggleFlyoutFullScreen: () => void; + toggleFlyoutFullScreen: (direction: string) => void; } export const ChatFlyout = (props: ChatFlyoutProps) => { @@ -81,17 +81,10 @@ export const ChatFlyout = (props: ChatFlyoutProps) => { const rightPanelSize = getRightPanelSize(); return ( - chatContext.setFlyoutVisible(false)} {...props.flyoutProps} > <> @@ -146,6 +139,6 @@ export const ChatFlyout = (props: ChatFlyoutProps) => { )} - + ); }; diff --git a/public/chat_header_button.tsx b/public/chat_header_button.tsx index 98ec5f10..50bd53ba 100644 --- a/public/chat_header_button.tsx +++ b/public/chat_header_button.tsx @@ -16,6 +16,11 @@ import './index.scss'; import chatIcon from './assets/chat.svg'; import { ActionExecutor, AssistantActions, MessageRenderer, UserAccount, TabId } from './types'; import { TAB_ID } from './utils/constants'; +import { useCore } from './contexts/core_context'; +import { toMountPoint } from '../../../src/plugins/opensearch_dashboards_react/public'; +import { OpenSearchDashboardsReactContext } from '../../../src/plugins/opensearch_dashboards_react/public'; +import { AssistantServices } from './contexts/core_context'; +import { ISidecarConfig } from '../../../src/core/public'; interface HeaderChatButtonProps { application: ApplicationStart; @@ -24,6 +29,7 @@ interface HeaderChatButtonProps { actionExecutors: Record; assistantActions: AssistantActions; currentAccount: UserAccount; + coreContext: OpenSearchDashboardsReactContext; } let flyoutLoaded = false; @@ -37,22 +43,45 @@ export const HeaderChatButton = (props: HeaderChatButtonProps) => { const [selectedTabId, setSelectedTabId] = useState(TAB_ID.CHAT); const [preSelectedTabId, setPreSelectedTabId] = useState(undefined); const [interactionId, setInteractionId] = useState(undefined); - const [chatSize, setChatSize] = useState('dock-right'); + const [dockedDirection, setDockedDirection] = useState( + 'right' + ); const [query, setQuery] = useState(''); const [inputFocus, setInputFocus] = useState(false); - const flyoutFullScreen = chatSize === 'fullscreen'; + const flyoutFullScreen = dockedDirection === 'bottom'; const inputRef = useRef(null); - - if (!flyoutLoaded && flyoutVisible) flyoutLoaded = true; + // const [flyoutLoaded, setFlyoutLoaded] = useState(false); + const core = useCore(); + // if (!flyoutLoaded && flyoutVisible) flyoutLoaded = true; useEffectOnce(() => { const subscription = props.application.currentAppId$.subscribe((id) => setAppId(id)); return () => subscription.unsubscribe(); }); - const toggleFlyoutFullScreen = useCallback(() => { - setChatSize(flyoutFullScreen ? 'dock-right' : 'fullscreen'); - }, [flyoutFullScreen, setChatSize]); + const toggleFlyoutFullScreen = useCallback( + (direction: ISidecarConfig['dockedDirection']) => { + setDockedDirection((prevDirection) => { + if (prevDirection === direction) { + return prevDirection; + } else { + if (direction === 'bottom') { + core.overlays.sidecar().setSidecarConfig({ + dockedDirection: 'bottom', + paddingSize: window.innerHeight - 136, + }); + } else { + core.overlays.sidecar().setSidecarConfig({ + dockedDirection: direction, + paddingSize: 460, + }); + } + return direction; + } + }); + }, + [flyoutFullScreen] + ); const chatContextValue: IChatContext = useMemo( () => ({ @@ -117,6 +146,46 @@ export const HeaderChatButton = (props: HeaderChatButtonProps) => { } }; + const Chat = ( + + + + + + + + + ); + + useEffect(() => { + console.log('visa', flyoutVisible, flyoutLoaded); + if (!flyoutLoaded && flyoutVisible) { + console.log('mounut'); + core.overlays.sidecar().open(toMountPoint(Chat), { + className: 'chatbot-sidecar', + config: { + dockedDirection: 'right', + paddingSize: 460, + }, + }); + flyoutLoaded = true; + } else if (flyoutLoaded && flyoutVisible) { + console.log('show'); + + core.overlays.sidecar().show(); + } else if (flyoutLoaded && !flyoutVisible) { + console.log('hide'); + + core.overlays.sidecar().hide(); + } + }, [flyoutVisible, flyoutLoaded]); + const onKeyUp = (e: React.KeyboardEvent) => { if (e.key === 'Escape') { inputRef.current?.blur(); @@ -185,20 +254,6 @@ export const HeaderChatButton = (props: HeaderChatButtonProps) => { disabled={!props.userHasAccess} /> - - - - {flyoutLoaded ? ( - - ) : null} - - ); }; diff --git a/public/components/sidecar_icon_menu.tsx b/public/components/sidecar_icon_menu.tsx new file mode 100644 index 00000000..4445afb9 --- /dev/null +++ b/public/components/sidecar_icon_menu.tsx @@ -0,0 +1,104 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { + EuiContextMenuItem, + EuiContextMenuPanel, + EuiFlexGroup, + EuiFlexItem, + EuiPopover, + EuiButtonIcon, +} from '@elastic/eui'; +import React, { useCallback, useState } from 'react'; + +const dockBottom = () => ( + + + + + + +); + +const dockRight = () => ( + + + + + + +); + +interface Props { + setDockedDirection: (direction: string) => void; +} + +export const SidecarIconMenu = ({ setDockedDirection }) => { + const [isPopoverOpen, setPopoverOpen] = useState(false); + + const onButtonClick = useCallback(() => { + setPopoverOpen((flag) => !flag); + }, []); + + const closePopover = useCallback(() => { + setPopoverOpen(false); + }, []); + + const button = ( + + ); + + const items = [ + { + closePopover(); + setDockedDirection('right'); + }} + > + Dock Right + , + { + closePopover(); + setDockedDirection('left'); + }} + > + Dock Left + , + { + closePopover(); + setDockedDirection('bottom'); + }} + > + Dock Full Width + , + ]; + + return ( + <> + + + + + ); +}; diff --git a/public/index.scss b/public/index.scss index 1136487e..6648e942 100644 --- a/public/index.scss +++ b/public/index.scss @@ -64,6 +64,7 @@ } .llm-chat-flyout { + height: 100%; .euiFlyoutFooter { background: transparent; } @@ -88,6 +89,10 @@ border-radius: 8px; } +.llm-chat-flyout-footer { + padding-bottom: 24px; +} + .llm-chat-bubble-wrapper { .llm-chat-action-buttons-hidden { opacity: 0; diff --git a/public/plugin.tsx b/public/plugin.tsx index 3a2400c2..c73239dd 100644 --- a/public/plugin.tsx +++ b/public/plugin.tsx @@ -75,36 +75,38 @@ export class AssistantPlugin const checkAccess = (account: Awaited>) => account.data.roles.some((role) => ['all_access', 'assistant_user'].includes(role)); - if (this.config.chat.enabled) { - core.getStartServices().then(async ([coreStart, startDeps]) => { - const CoreContext = createOpenSearchDashboardsReactContext({ - ...coreStart, - setupDeps, - startDeps, - conversationLoad: new ConversationLoadService(coreStart.http), - conversations: new ConversationsService(coreStart.http), - }); - const account = await getAccount(); - const username = account.data.user_name; - const tenant = account.data.user_requested_tenant ?? ''; + // if (this.config.chat.enabled) { + core.getStartServices().then(async ([coreStart, startDeps]) => { + const CoreContext = createOpenSearchDashboardsReactContext({ + ...coreStart, + setupDeps, + startDeps, + conversationLoad: new ConversationLoadService(coreStart.http), + conversations: new ConversationsService(coreStart.http), + }); + const account = await getAccount(); + const username = account.data.user_name; + const tenant = account.data.user_requested_tenant ?? ''; - coreStart.chrome.navControls.registerRight({ - order: 10000, - mount: toMountPoint( - - - - ), - }); + coreStart.chrome.navControls.registerRight({ + order: 10000, + mount: toMountPoint( + + + + ), }); - } + }); + // }); + // } return { registerMessageRenderer: (contentType, render) => { diff --git a/public/tabs/chat/chat_page.tsx b/public/tabs/chat/chat_page.tsx index 0cb3b803..1347fd9e 100644 --- a/public/tabs/chat/chat_page.tsx +++ b/public/tabs/chat/chat_page.tsx @@ -56,7 +56,7 @@ export const ChatPage: React.FC = (props) => { - + void; + toggleFlyoutFullScreen: (direction: string) => void; } export const ChatWindowHeader = React.memo((props: ChatWindowHeaderProps) => { @@ -70,7 +71,8 @@ export const ChatWindowHeader = React.memo((props: ChatWindowHeaderProps) => { - + + {/* { iconType={props.flyoutFullScreen ? dockRight : dockBottom} onClick={props.toggleFlyoutFullScreen} /> - + */}