diff --git a/package-lock.json b/package-lock.json index a66ea3dc..1834ebba 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,9 +11,10 @@ "dependencies": { "@chatscope/chat-ui-kit-styles": "^1.4.0", "@fortawesome/fontawesome-free": "^5.12.1", - "@fortawesome/fontawesome-svg-core": "^1.2.26", - "@fortawesome/free-solid-svg-icons": "^5.12.0", - "@fortawesome/react-fontawesome": "^0.1.8", + "@fortawesome/fontawesome-svg-core": "^6.5.1", + "@fortawesome/free-regular-svg-icons": "^6.5.1", + "@fortawesome/free-solid-svg-icons": "^6.5.1", + "@fortawesome/react-fontawesome": "^0.2.0", "@monaco-editor/react": "^4.5.2", "@popperjs/core": "^2.11.8", "@types/decompress": "^4.2.4", @@ -2309,9 +2310,9 @@ } }, "node_modules/@fortawesome/fontawesome-common-types": { - "version": "0.2.36", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.36.tgz", - "integrity": "sha512-a/7BiSgobHAgBWeN7N0w+lAhInrGxksn13uK7231n2m8EDPE3BMCl9NZLTGrj9ZXfCmC6LM0QLqXidIizVQ6yg==", + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.5.1.tgz", + "integrity": "sha512-GkWzv+L6d2bI5f/Vk6ikJ9xtl7dfXtoRu3YGE6nq0p/FFqA1ebMOAWg3XgRyb0I6LYyYkiAo+3/KrwuBp8xG7A==", "hasInstallScript": true, "engines": { "node": ">=6" @@ -2327,39 +2328,51 @@ } }, "node_modules/@fortawesome/fontawesome-svg-core": { - "version": "1.2.36", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.36.tgz", - "integrity": "sha512-YUcsLQKYb6DmaJjIHdDWpBIGCcyE/W+p/LMGvjQem55Mm2XWVAP5kWTMKWLv9lwpCVjpLxPyOMOyUocP1GxrtA==", + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.5.1.tgz", + "integrity": "sha512-MfRCYlQPXoLlpem+egxjfkEuP9UQswTrlCOsknus/NcMoblTH2g0jPrapbcIb04KGA7E2GZxbAccGZfWoYgsrQ==", "hasInstallScript": true, "dependencies": { - "@fortawesome/fontawesome-common-types": "^0.2.36" + "@fortawesome/fontawesome-common-types": "6.5.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/free-regular-svg-icons": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-6.5.1.tgz", + "integrity": "sha512-m6ShXn+wvqEU69wSP84coxLbNl7sGVZb+Ca+XZq6k30SzuP3X4TfPqtycgUh9ASwlNh5OfQCd8pDIWxl+O+LlQ==", + "hasInstallScript": true, + "dependencies": { + "@fortawesome/fontawesome-common-types": "6.5.1" }, "engines": { "node": ">=6" } }, "node_modules/@fortawesome/free-solid-svg-icons": { - "version": "5.15.4", - "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.15.4.tgz", - "integrity": "sha512-JLmQfz6tdtwxoihXLg6lT78BorrFyCf59SAwBM6qV/0zXyVeDygJVb3fk+j5Qat+Yvcxp1buLTY5iDh1ZSAQ8w==", + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.5.1.tgz", + "integrity": "sha512-S1PPfU3mIJa59biTtXJz1oI0+KAXW6bkAb31XKhxdxtuXDiUIFsih4JR1v5BbxY7hVHsD1RKq+jRkVRaf773NQ==", "hasInstallScript": true, "dependencies": { - "@fortawesome/fontawesome-common-types": "^0.2.36" + "@fortawesome/fontawesome-common-types": "6.5.1" }, "engines": { "node": ">=6" } }, "node_modules/@fortawesome/react-fontawesome": { - "version": "0.1.19", - "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.1.19.tgz", - "integrity": "sha512-Hyb+lB8T18cvLNX0S3llz7PcSOAJMLwiVKBuuzwM/nI5uoBw+gQjnf9il0fR1C3DKOI5Kc79pkJ4/xB0Uw9aFQ==", + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.2.0.tgz", + "integrity": "sha512-uHg75Rb/XORTtVt7OS9WoK8uM276Ufi7gCzshVWkUJbHhh3svsUUeqXerrM96Wm7fRiDzfKRwSoahhMIkGAYHw==", "dependencies": { "prop-types": "^15.8.1" }, "peerDependencies": { "@fortawesome/fontawesome-svg-core": "~1 || ~6", - "react": ">=16.x" + "react": ">=16.3" } }, "node_modules/@gar/promisify": { diff --git a/package.json b/package.json index 14d0033c..98b88661 100644 --- a/package.json +++ b/package.json @@ -85,9 +85,10 @@ "dependencies": { "@chatscope/chat-ui-kit-styles": "^1.4.0", "@fortawesome/fontawesome-free": "^5.12.1", - "@fortawesome/fontawesome-svg-core": "^1.2.26", - "@fortawesome/free-solid-svg-icons": "^5.12.0", - "@fortawesome/react-fontawesome": "^0.1.8", + "@fortawesome/fontawesome-svg-core": "^6.5.1", + "@fortawesome/free-regular-svg-icons": "^6.5.1", + "@fortawesome/free-solid-svg-icons": "^6.5.1", + "@fortawesome/react-fontawesome": "^0.2.0", "@monaco-editor/react": "^4.5.2", "@popperjs/core": "^2.11.8", "@types/decompress": "^4.2.4", diff --git a/src/commands/iq/chat/iqChatHandler.ts b/src/commands/iq/chat/iqChatHandler.ts index a19643c3..e7db7753 100644 --- a/src/commands/iq/chat/iqChatHandler.ts +++ b/src/commands/iq/chat/iqChatHandler.ts @@ -121,7 +121,6 @@ const getFinalResponse = async (message: string, additionalContext: IAdditionalC export const iqChatHandler = async (iqPayload: any, cacheService: CacheService, allMessages: IStoredMessages[], webview: WebviewView) => { const newMessage: string = iqPayload.newMessage, orgId: string = iqPayload.orgId, chatId: string = iqPayload.chatId, qaId: string = iqPayload.qaId; const userChats = iqPayload.userChats || []; - const jwtToken = Memory.state.get("vscode-couchbase.iq.jwtToken"); if (jwtToken === undefined) { return { diff --git a/src/reactViews/iq/chatscope/src/components/Buttons/AttachmentButton.jsx b/src/reactViews/iq/chatscope/src/components/Buttons/AttachmentButton.jsx index 0c5610e8..6d84eb3b 100644 --- a/src/reactViews/iq/chatscope/src/components/Buttons/AttachmentButton.jsx +++ b/src/reactViews/iq/chatscope/src/components/Buttons/AttachmentButton.jsx @@ -6,14 +6,15 @@ import Button from "./Button"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faPaperclip } from "@fortawesome/free-solid-svg-icons/faPaperclip"; -export const AttachmentButton = ({ className, children, ...rest }) => { +export const AttachmentButton = ({ className, children, icon={faPaperclip}, ...rest }) => { const cName = `${prefix}-button--attachment`; return ( diff --git a/src/reactViews/iq/chatscope/src/components/MessageInput/MessageInput.d.ts b/src/reactViews/iq/chatscope/src/components/MessageInput/MessageInput.d.ts index 6d4a7d24..2d774532 100644 --- a/src/reactViews/iq/chatscope/src/components/MessageInput/MessageInput.d.ts +++ b/src/reactViews/iq/chatscope/src/components/MessageInput/MessageInput.d.ts @@ -1,5 +1,6 @@ import type {MouseEvent, ReactElement} from "react"; import type {ChatComponentPropsRef} from "../../types"; +import { IconDefinition } from "@fortawesome/free-regular-svg-icons"; export interface MessageInputProps { value?: string; @@ -16,6 +17,7 @@ export interface MessageInputProps { attachButton?: boolean; attachDisabled?: boolean; onAttachClick?: (evt: MouseEvent) => void; + attachIcon?: IconDefinition; } export declare const MessageInput: (props: ChatComponentPropsRef) => ReactElement; diff --git a/src/reactViews/iq/chatscope/src/components/MessageInput/MessageInput.jsx b/src/reactViews/iq/chatscope/src/components/MessageInput/MessageInput.jsx index c0dc3534..69243965 100644 --- a/src/reactViews/iq/chatscope/src/components/MessageInput/MessageInput.jsx +++ b/src/reactViews/iq/chatscope/src/components/MessageInput/MessageInput.jsx @@ -82,6 +82,7 @@ function MessageInputInner( sendButton, attachButton, onAttachClick, + attachIcon, ...rest }, ref @@ -160,7 +161,7 @@ function MessageInputInner( }; const handleChange = (innerHTML, textContent, innerText) => { - setStateValue(innerHTML); + setStateValue(innerText); if (typeof sendDisabled === "undefined") { setStateSendDisabled(textContent.length === 0); } @@ -174,6 +175,7 @@ function MessageInputInner( onChange(innerHTML, textContent, innerText, content[2]); }; + const cName = `${prefix}-message-input`, ph = typeof placeholder === "string" ? placeholder : ""; @@ -191,6 +193,7 @@ function MessageInputInner( )} @@ -208,6 +211,7 @@ function MessageInputInner( placeholder={ph} onKeyPress={handleKeyPress} onChange={handleChange} + activateAfterChange={activateAfterChange} value={stateValue} /> diff --git a/src/reactViews/iq/pages/chatscreen/IqChat.scss b/src/reactViews/iq/pages/chatscreen/IqChat.scss index 8dd5df01..f407f6f6 100644 --- a/src/reactViews/iq/pages/chatscreen/IqChat.scss +++ b/src/reactViews/iq/pages/chatscreen/IqChat.scss @@ -64,6 +64,7 @@ cursor: pointer; margin: 0 5px; font-size: 1.5rem; + background: transparent !important; } .likeButton:hover, .dislikeButton:hover { diff --git a/src/reactViews/iq/pages/chatscreen/IqChat.tsx b/src/reactViews/iq/pages/chatscreen/IqChat.tsx index 1cbbd6e9..e15e9419 100644 --- a/src/reactViews/iq/pages/chatscreen/IqChat.tsx +++ b/src/reactViews/iq/pages/chatscreen/IqChat.tsx @@ -7,7 +7,7 @@ import MessageList from "../../chatscope/src/components/MessageList/MessageList" import { Message } from "../../chatscope/src/components/Message/Message"; import { MessageInput } from "../../chatscope/src/components/MessageInput/MessageInput"; import { TypingIndicator } from "../../chatscope/src/components/TypingIndicator/TypingIndicator"; -import {v4 as uuid} from 'uuid'; +import { v4 as uuid } from "uuid"; import ReactMarkdown from "react-markdown"; import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"; @@ -22,6 +22,9 @@ import { } from "chatscope/src/components/ActionBar/ActionBar"; import { ChatAction, availableActions } from "utils/ChatAction"; import { SendFeedback } from "components/chatActions/SendFeedback"; +import { faThumbsUp, faThumbsDown } from "@fortawesome/free-regular-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { faSquarePlus } from "@fortawesome/free-solid-svg-icons"; export type userMessage = { message: string; @@ -32,53 +35,52 @@ export type userMessage = { }; export type iqMessages = { - userChats: userMessage[], - chatId: string + userChats: userMessage[]; + chatId: string; }; const IqChat = ({ org }) => { - const [messages, setMessages] = useState - ({ - userChats: [ - { - message: "Hello, I'm Couchbase IQ! Ask me anything!", - sender: "assistant", - msgDate: (Date.now()/1000).toFixed(0), - qaId: "firstMessage", - feedbackSent: false, - }, - ], - chatId: uuid() - }); + const [messages, setMessages] = useState({ + userChats: [ + { + message: "Hello, I'm Couchbase IQ! Ask me anything!", + sender: "assistant", + msgDate: (Date.now() / 1000).toFixed(0), + qaId: "firstMessage", + feedbackSent: false, + }, + ], + chatId: uuid(), + }); const [isTyping, setIsTyping] = useState(false); const [codeTheme, setCodeTheme] = useState(oneLight); const [showFeedbackModal, setShowFeedbackModal] = useState(false); const [feedbackModalData, setFeedbackModalData] = useState({ msgIndex: -1, - qaId: "" + qaId: "", }); - + const [actions, setActions] = useState([]); // fetch settings - useEffect(()=>{ + useEffect(() => { tsvscode.postMessage({ command: "vscode-couchbase.iq.fetchChatSettings", - value: undefined + value: undefined, }); }, []); - const [actions, setActions] = useState([]); const handleMessageLike = (index: number, qaId: string) => { const originalReply = messages.userChats[index].message; - const originalQuestion = index - 1 > 0 ? messages.userChats[index - 1].message : ""; - const newMessage:userMessage = { + const originalQuestion = + index - 1 > 0 ? messages.userChats[index - 1].message : ""; + const newMessage: userMessage = { message: "Glad you liked the result. Would you like to give more feedback", sender: "feedback", - msgDate: (Date.now()/1000).toFixed(0), + msgDate: (Date.now() / 1000).toFixed(0), qaId: qaId, - feedbackSent: false + feedbackSent: false, }; const messagesCopy = [...messages.userChats]; @@ -87,11 +89,17 @@ const IqChat = ({ org }) => { const updatedMessages = [...messagesCopy, newMessage]; setMessages({ chatId: messages.chatId, - userChats: updatedMessages + userChats: updatedMessages, }); - + setActions([ - ChatAction("Send Feedback", setShowFeedbackModal, setFeedbackModalData, index, qaId) + ChatAction( + "Send Feedback", + setShowFeedbackModal, + setFeedbackModalData, + index, + qaId + ), ]); // send info to lambda tsvscode.postMessage({ @@ -112,14 +120,15 @@ const IqChat = ({ org }) => { const handleMessageDislike = (index: number, qaId: string) => { const originalReply = messages.userChats[index].message; - const originalQuestion = index - 1 > 0 ? messages.userChats[index - 1].message : ""; - const newMessage:userMessage = { + const originalQuestion = + index - 1 > 0 ? messages.userChats[index - 1].message : ""; + const newMessage: userMessage = { message: "Oh! We are very sorry. Can you please give us additional info via feedback", sender: "feedback", - msgDate: (Date.now()/1000).toFixed(0), + msgDate: (Date.now() / 1000).toFixed(0), qaId: qaId, - feedbackSent: false + feedbackSent: false, }; const messagesCopy = [...messages.userChats]; @@ -128,12 +137,18 @@ const IqChat = ({ org }) => { const updatedMessages = [...messagesCopy, newMessage]; setMessages({ chatId: messages.chatId, - userChats: updatedMessages + userChats: updatedMessages, }); // set actions to feedback setActions([ - ChatAction("Send Feedback", setShowFeedbackModal, setFeedbackModalData, index, qaId) + ChatAction( + "Send Feedback", + setShowFeedbackModal, + setFeedbackModalData, + index, + qaId + ), ]); tsvscode.postMessage({ @@ -156,8 +171,8 @@ const IqChat = ({ org }) => { const index = feedbackModalData.msgIndex; const qaId = feedbackModalData.qaId; const originalReply = messages.userChats[index].message; - const originalQuestion = index - 1 > 0 ? messages.userChats[index - 1].message : ""; - + const originalQuestion = + index - 1 > 0 ? messages.userChats[index - 1].message : ""; tsvscode.postMessage({ command: "vscode-couchbase.iq.sendFeedbackPerMessageEmote", @@ -165,10 +180,10 @@ const IqChat = ({ org }) => { type: null, question: originalQuestion, reply: originalReply, - msgDate: ((Date.now())/1000).toFixed(0), + msgDate: (Date.now() / 1000).toFixed(0), additionalFeedback: feedbackText, qaId: qaId, - chatId: messages.chatId + chatId: messages.chatId, }, }); }; @@ -186,7 +201,7 @@ const IqChat = ({ org }) => { const handlePaste = (event) => { // Prevent the original paste action event.preventDefault(); - const text = event.clipboardData.getData("text"); + const text = event.clipboardData.getData("text/plain"); const selection = window.getSelection(); if (selection.rangeCount) { @@ -198,6 +213,24 @@ const IqChat = ({ org }) => { event.target.dispatchEvent(inputEvent); }; + const onNewChatClick = () => { + setMessages({ + userChats: [ + { + message: "Hello, I'm Couchbase IQ! Ask me anything!", + sender: "assistant", + msgDate: (Date.now() / 1000).toFixed(0), + qaId: "firstMessage", + feedbackSent: false, + }, + ], + chatId: uuid(), + }); + setIsTyping(false); + setShowFeedbackModal(false); + setActions([]); + }; + window.addEventListener("message", (event) => { const message = event.data; switch (message.command) { @@ -213,13 +246,13 @@ const IqChat = ({ org }) => { sender: "assistant", feedbackSent: false, msgDate: message.msgDate, - qaId: message.qaId // TODO: get qaId + qaId: message.qaId, // TODO: get qaId }; const updatedMessages = [...messages.userChats, newMessage]; setMessages({ chatId: messages.chatId, - userChats: updatedMessages + userChats: updatedMessages, }); setIsTyping(false); @@ -235,10 +268,18 @@ const IqChat = ({ org }) => { } case "vscode-webview.iq.updateActions": { const actionsForBar = []; - for(const action of message.actions) { - if(availableActions.includes(action)){ - if(action === "Send Feedback") { - actionsForBar.push(ChatAction(action, setShowFeedbackModal, setFeedbackModalData, 0, "firstMessage")); // TODO: Right now Gives feedback to just the first message + for (const action of message.actions) { + if (availableActions.includes(action)) { + if (action === "Send Feedback") { + actionsForBar.push( + ChatAction( + action, + setShowFeedbackModal, + setFeedbackModalData, + 0, + "firstMessage" + ) + ); // TODO: Right now Gives feedback to just the first message } else { actionsForBar.push(ChatAction(action)); } @@ -254,18 +295,18 @@ const IqChat = ({ org }) => { }); const handleSendRequest = async (message: string) => { - const newMessage:userMessage = { + const newMessage: userMessage = { message, sender: "user", - msgDate: (Date.now()/1000).toFixed(0), + msgDate: (Date.now() / 1000).toFixed(0), qaId: uuid(), - feedbackSent: false + feedbackSent: false, }; const updatedMessages = [...messages.userChats, newMessage]; setMessages({ chatId: messages.chatId, - userChats: updatedMessages + userChats: updatedMessages, }); setIsTyping(true); try { @@ -298,12 +339,15 @@ const IqChat = ({ org }) => { > 0) ? "hasActionbar" : "" + isTyping || actions.length > 0 ? "hasActionbar" : "" }`} scrollBehavior="smooth" actionbar={ isTyping ? ( - + ) : actions.length > 0 ? ( ) : undefined @@ -361,18 +405,22 @@ const IqChat = ({ org }) => { {message.feedbackSent !== true ? ( <> -
handleMessageLike(index, message.qaId)} + onClick={() => + handleMessageLike(index, message.qaId) + } > - 👍 -
-
+ +
+ + ) : (
@@ -400,16 +448,24 @@ const IqChat = ({ org }) => { handlePaste(event); }} sendButton={true} - attachButton={false} + attachButton={true} placeholder="Type a message..." onSend={(msg) => handleSendRequest(msg)} className="chatscope-message-input" + sendDisabled={!isTyping} + attachIcon={faSquarePlus} + onAttachClick={onNewChatClick} /> - - - {setShowFeedbackModal(false)} onSubmit={(text)=>handleFeedbackSubmit(text)}/>} + { + setShowFeedbackModal(false)} + onSubmit={(text) => handleFeedbackSubmit(text)} + /> + } +
); };