From d26e18579a0044e9e5fc95c454aefae08f704346 Mon Sep 17 00:00:00 2001 From: Natalia Venditto Date: Thu, 26 Oct 2023 21:08:36 +0200 Subject: [PATCH 01/21] chore: update followup questions preliminary --- .../chat-component/src/core/parser/index.ts | 51 ++++++++++++++----- 1 file changed, 37 insertions(+), 14 deletions(-) diff --git a/packages/chat-component/src/core/parser/index.ts b/packages/chat-component/src/core/parser/index.ts index e76951f8..c83e4555 100644 --- a/packages/chat-component/src/core/parser/index.ts +++ b/packages/chat-component/src/core/parser/index.ts @@ -41,7 +41,9 @@ export async function parseStreamedMessages({ // we use numeric values to identify the beginning of a step // if we match a number, store it in the buffer and move on to the next iteration - const LIST_ITEM_NUMBER = /(\d+)/; + const LIST_ITEM_NUMBER: RegExp = /(\d+)/; + const FOLLOW_UP_QUESTION_END: RegExp = /<<|>|>\?|>>/g; + // const FOLLOW_UP_QUESTION_START: RegExp = /<<|Next/; let matchedStepIndex = chunkValue.match(LIST_ITEM_NUMBER)?.[0]; if (matchedStepIndex) { stepsBuffer.push(matchedStepIndex); @@ -50,18 +52,39 @@ export async function parseStreamedMessages({ // followup questions are marked either with the word 'Next Questions:' or '<>' or both at the same time // these markers may be split across multiple chunks, so we need to buffer them! - // TODO: support followup questions wrapped in <> markers - const matchedFollowupQuestionMarker = !isFollowupQuestion && chunkValue.includes('Next'); - if (matchedFollowupQuestionMarker) { - followupQuestionsBuffer.push(chunkValue); - continue; - } else if (followupQuestionsBuffer.length > 0 && chunkValue.includes('Question')) { - isFollowupQuestion = true; - followupQuestionsBuffer.push(chunkValue); - continue; - } else if (isFollowupQuestion) { - isFollowupQuestion = true; - chunkValue = chunkValue.replace(/:?\n/, ''); + // TODO: remove all this logic from the frontend! + const matchFollowUpCase1 = !isFollowupQuestion && chunkValue.includes('Next'); + const matchFollowUpCase2 = !isFollowupQuestion && chunkValue.includes('<<'); + if (followupQuestionsBuffer.length === 0) { + if (matchFollowUpCase1) { + chunkValue = chunkValue.replace('Next Questions:', ''); + followupQuestionsBuffer.push(chunkValue); + continue; + } + if (matchFollowUpCase2) { + followupQuestionsBuffer.push(chunkValue); + isFollowupQuestion = true; + chunkValue = chunkValue.replace('<<', ''); + continue; + } + } else { + if (chunkValue.includes('Question')) { + isFollowupQuestion = true; + followupQuestionsBuffer.push(chunkValue); + continue; + } + if (isFollowupQuestion) { + if ( + chunkValue.includes('<<') || + chunkValue.includes('>') || + chunkValue.includes('?>') || + chunkValue.includes('>>') + ) { + chunkValue = chunkValue.replaceAll(FOLLOW_UP_QUESTION_END, ''); + } + isFollowupQuestion = true; + chunkValue = chunkValue.replace(/:?\n/, ''); + } } // if we are here, it means we have previously matched a number, followed by a dot (in current chunk) @@ -136,7 +159,7 @@ export function updateCitationsEntry({ const updateCitationReference = (match, capture) => { const citation = citations.find((citation) => citation.text === capture); if (citation) { - return `[${citation.ref}]`; + return `${citation.ref}`; } return match; }; From e9075c6b4217ec92af282888c4aad309075550b1 Mon Sep 17 00:00:00 2001 From: Natalia Venditto Date: Thu, 26 Oct 2023 21:09:05 +0200 Subject: [PATCH 02/21] chore: update styles --- packages/chat-component/src/style.ts | 39 ++++++++++++++++++---------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/packages/chat-component/src/style.ts b/packages/chat-component/src/style.ts index 482ed475..c3663c98 100644 --- a/packages/chat-component/src/style.ts +++ b/packages/chat-component/src/style.ts @@ -5,13 +5,13 @@ export const mainStyle = css` width: 100vw; display: block; padding: 16px; - --secondary-color: #f8fffd; + --secondary-color: #f5f5f5; --text-color: #123f58; --primary-color: rgba(241, 255, 165, 0.6); --white: #fff; --light-gray: #e3e3e3; --dark-gray: #4e5288; - --accent-high: #8cdef2; + --accent-high: #0040ff; --accent-dark: #002b23; --accent-light: #e6fbf7; --accent-lighter: rgba(140, 222, 242, 0.4); @@ -102,7 +102,7 @@ export const mainStyle = css` color: var(--text-color); font-size: 1.5rem; padding: 0; - margin: 0; + margin: 10px 0; } .subheadline { color: var(--text-color); @@ -164,7 +164,7 @@ export const mainStyle = css` position: sticky; bottom: 0; z-index: 1; - background: var(--accent-light); + background: var(--secondary-color); } .form__label { display: block; @@ -254,7 +254,7 @@ export const mainStyle = css` height: 50px; } .chatbox__button { - background: var(--accent-high); + background: var(--white); border: none; color: var(--text-color); font-weight: bold; @@ -262,6 +262,11 @@ export const mainStyle = css` border-radius: 4px; margin-left: 8px; width: 80px; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); + } + .chatbox__button:hover, + .chatbox__button:focus { + background: var(--secondary-color); } .chatbox__button--reset { position: absolute; @@ -273,13 +278,12 @@ export const mainStyle = css` background: var(--accent-dark); border-radius: 50%; color: var(--white); - font-weight: bold; + font-wdafaeight: bold; height: 20px; width: 20px; cursor: pointer; } .chatbox__input { - border: 1px solid var(--accent-high); background: var(--white); color: var(--text-color); border-radius: 4px; @@ -311,13 +315,16 @@ export const mainStyle = css` word-wrap: break-word; margin-block-end: 0; position: relative; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); } .chat__txt.error { - background-color: var(--error-color); - color: var(--white); + border: 3px solid var(--error-color); + color: var(--error-color); } .chat__txt.user-message { background-color: var(--accent-high); + color: var(--white); + font-weight: bold; } .chat__listItem.user-message { align-self: flex-end; @@ -379,6 +386,7 @@ export const mainStyle = css` text-align: center; display: flex; flex-direction: column; + justify-content: center; } .defaults__list.always-row { text-align: left; @@ -391,11 +399,11 @@ export const mainStyle = css` .defaults__listItem { padding: 10px; border-radius: 10px; - border: 1px solid var(--accent-high); - background: var(--secondary-color); + background: var(--white); margin: 4px; color: var(--text-color); justify-content: space-evenly; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); @media (min-width: 768px) { min-height: 100px; @@ -404,7 +412,7 @@ export const mainStyle = css` .defaults__listItem:hover, .defaults__listItem:focus { color: var(--accent-dark); - background: var(--accent-light); + background: var(--secondary-color); transition: all 0.3s ease-in-out; } .defaults__span { @@ -413,6 +421,11 @@ export const mainStyle = css` margin-top: 20px; text-decoration: underline; } + .citation { + background-color: var(--accent-lighter); + border-radius: 3px; + padding: 3px; + } .loading-skeleton { display: flex; margin-bottom: 50px; @@ -421,7 +434,7 @@ export const mainStyle = css` width: 10px; height: 10px; margin: 0 5px; - background-color: var(--accent-high); + background-color: var(--accent-lighter); border-radius: 50%; animation: chatloadinganimation 1.5s infinite; } From 4868a486c9edb10fb2c68f6f027b2058fb50b674 Mon Sep 17 00:00:00 2001 From: Natalia Venditto Date: Thu, 26 Oct 2023 22:31:57 +0200 Subject: [PATCH 03/21] chore: restyle followup questions --- packages/chat-component/src/main.ts | 5 +++-- packages/chat-component/src/style.ts | 12 +++++++++--- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/packages/chat-component/src/main.ts b/packages/chat-component/src/main.ts index 7e9b01d4..3619222e 100644 --- a/packages/chat-component/src/main.ts +++ b/packages/chat-component/src/main.ts @@ -383,10 +383,11 @@ export class ChatComponent extends LitElement { } renderFollowupQuestions(followupQuestions: string[] | undefined) { + // render followup questions if (followupQuestions && followupQuestions.length > 0) { return html`

You may also want to ask...

-
    +
      ${followupQuestions.map( (followupQuestion) => html`
    • @@ -399,7 +400,7 @@ export class ChatComponent extends LitElement {
    • `, )} -
+ `; } diff --git a/packages/chat-component/src/style.ts b/packages/chat-component/src/style.ts index c3663c98..7aa1edf8 100644 --- a/packages/chat-component/src/style.ts +++ b/packages/chat-component/src/style.ts @@ -340,15 +340,20 @@ export const mainStyle = css` } .items__list.followup { display: flex; - flex-direction: column; + flex-direction: row; padding: 20px; + list-style-type: none; + flex-wrap: wrap; } .items__list.steps { display: block; } .items__listItem--followup { cursor: pointer; - color: var(--dark-gray); + padding: 0 5px; + border-radius: 5px; + border: 1px solid var(--accent-high); + margin: 5px; } .items__listItem--citation { display: inline-block; @@ -370,10 +375,11 @@ export const mainStyle = css` border-bottom: 1px solid var(--light-gray); } .followup .items__link { - color: var(--dark-gray); + color: var(--accent-high); display: block; padding: 5px 0; border-bottom: 1px solid var(--light-gray); + font-size: small; } .defaults__button { text-decoration: none; From fb45e89e62611c4bb505ef6c8952ed817623d079 Mon Sep 17 00:00:00 2001 From: Natalia Venditto Date: Fri, 27 Oct 2023 11:32:32 +0200 Subject: [PATCH 04/21] chore: fix parser for follow up questions (before TC) --- .../chat-component/src/core/parser/index.ts | 65 +++++++++---------- 1 file changed, 29 insertions(+), 36 deletions(-) diff --git a/packages/chat-component/src/core/parser/index.ts b/packages/chat-component/src/core/parser/index.ts index c83e4555..0dbea89a 100644 --- a/packages/chat-component/src/core/parser/index.ts +++ b/packages/chat-component/src/core/parser/index.ts @@ -17,6 +17,7 @@ export async function parseStreamedMessages({ let isProcessingStep = false; let isLastStep = false; let isFollowupQuestion = false; + let followUpQuestionIndex = 0; let stepIndex = 0; let textBlockIndex = 0; const result = { @@ -42,7 +43,7 @@ export async function parseStreamedMessages({ // we use numeric values to identify the beginning of a step // if we match a number, store it in the buffer and move on to the next iteration const LIST_ITEM_NUMBER: RegExp = /(\d+)/; - const FOLLOW_UP_QUESTION_END: RegExp = /<<|>|>\?|>>/g; + const FOLLOW_UP_QUESTION: RegExp = /<<|>|>\?|>>/g; // const FOLLOW_UP_QUESTION_START: RegExp = /<<|Next/; let matchedStepIndex = chunkValue.match(LIST_ITEM_NUMBER)?.[0]; if (matchedStepIndex) { @@ -53,38 +54,28 @@ export async function parseStreamedMessages({ // followup questions are marked either with the word 'Next Questions:' or '<>' or both at the same time // these markers may be split across multiple chunks, so we need to buffer them! // TODO: remove all this logic from the frontend! - const matchFollowUpCase1 = !isFollowupQuestion && chunkValue.includes('Next'); - const matchFollowUpCase2 = !isFollowupQuestion && chunkValue.includes('<<'); - if (followupQuestionsBuffer.length === 0) { - if (matchFollowUpCase1) { - chunkValue = chunkValue.replace('Next Questions:', ''); - followupQuestionsBuffer.push(chunkValue); - continue; - } - if (matchFollowUpCase2) { - followupQuestionsBuffer.push(chunkValue); - isFollowupQuestion = true; - chunkValue = chunkValue.replace('<<', ''); - continue; - } - } else { - if (chunkValue.includes('Question')) { - isFollowupQuestion = true; - followupQuestionsBuffer.push(chunkValue); - continue; - } - if (isFollowupQuestion) { - if ( - chunkValue.includes('<<') || - chunkValue.includes('>') || - chunkValue.includes('?>') || - chunkValue.includes('>>') - ) { - chunkValue = chunkValue.replaceAll(FOLLOW_UP_QUESTION_END, ''); - } - isFollowupQuestion = true; - chunkValue = chunkValue.replace(/:?\n/, ''); - } + + const matchedFollowupQuestionMarker = + (!isFollowupQuestion && chunkValue.includes('Next')) || chunkValue.includes('<<'); + if (matchedFollowupQuestionMarker) { + followupQuestionsBuffer.push(chunkValue); + continue; + } else if (followupQuestionsBuffer.length > 0 && chunkValue.includes('Question')) { + isFollowupQuestion = true; + followupQuestionsBuffer.push(chunkValue); + continue; + } else if (chunkValue.includes('<<') && isFollowupQuestion) { + isFollowupQuestion = true; + chunkValue = chunkValue.replaceAll(FOLLOW_UP_QUESTION, ''); + continue; + } else if (chunkValue.includes('?>') || chunkValue.includes('>')) { + followUpQuestionIndex = followUpQuestionIndex + 1; + isFollowupQuestion = true; + chunkValue = chunkValue.replaceAll(FOLLOW_UP_QUESTION, ''); + continue; + } else if (isFollowupQuestion) { + isFollowupQuestion = true; + chunkValue = chunkValue.replace(/:?\n/, ''); } // if we are here, it means we have previously matched a number, followed by a dot (in current chunk) @@ -110,11 +101,10 @@ export async function parseStreamedMessages({ // if we are at the beginning of a step, we need to remove the step number and dot from the chunk value // we simply clear the current chunk value - if (matchedStepIndex || isProcessingStep) { + if (matchedStepIndex || isProcessingStep || isFollowupQuestion) { if (matchedStepIndex) { chunkValue = ''; } - // set the step index that is needed to update the correct step entry stepIndex = matchedStepIndex ? Number(matchedStepIndex) - 1 : stepIndex; updateFollowingStepOrFollowupQuestionEntry({ @@ -122,6 +112,7 @@ export async function parseStreamedMessages({ textBlockIndex, stepIndex, isFollowupQuestion, + followUpQuestionIndex, chatThread, }); @@ -225,18 +216,20 @@ export function updateFollowingStepOrFollowupQuestionEntry({ textBlockIndex, stepIndex, isFollowupQuestion, + followUpQuestionIndex, chatThread, }: { chunkValue: string; textBlockIndex: number; stepIndex: number; isFollowupQuestion: boolean; + followUpQuestionIndex: number; chatThread: ChatThreadEntry[]; }) { // following steps and followup questions are treated the same way. They are just stored in different arrays const { followupQuestions, text: lastChatMessageTextEntry } = chatThread.at(-1) as ChatThreadEntry; if (isFollowupQuestion && followupQuestions) { - followupQuestions[stepIndex] = (followupQuestions[stepIndex] || '') + chunkValue; + followupQuestions[followUpQuestionIndex] = (followupQuestions[followUpQuestionIndex] || '') + chunkValue; return; } From 8b04c0be7c9781adcd662ec757a97edad3d4abe1 Mon Sep 17 00:00:00 2001 From: Natalia Venditto Date: Fri, 27 Oct 2023 12:03:45 +0200 Subject: [PATCH 05/21] chore: fix bug and adapt styles --- packages/chat-component/src/core/parser/index.ts | 3 ++- packages/chat-component/src/main.ts | 6 ++---- packages/chat-component/src/style.ts | 4 ++++ 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/chat-component/src/core/parser/index.ts b/packages/chat-component/src/core/parser/index.ts index 0dbea89a..e31654ba 100644 --- a/packages/chat-component/src/core/parser/index.ts +++ b/packages/chat-component/src/core/parser/index.ts @@ -53,11 +53,12 @@ export async function parseStreamedMessages({ // followup questions are marked either with the word 'Next Questions:' or '<>' or both at the same time // these markers may be split across multiple chunks, so we need to buffer them! - // TODO: remove all this logic from the frontend! + // TODO: remove all this logic from the frontend and implement a solution on the backend or with TypeChat const matchedFollowupQuestionMarker = (!isFollowupQuestion && chunkValue.includes('Next')) || chunkValue.includes('<<'); if (matchedFollowupQuestionMarker) { + isFollowupQuestion = true; followupQuestionsBuffer.push(chunkValue); continue; } else if (followupQuestionsBuffer.length > 0 && chunkValue.includes('Question')) { diff --git a/packages/chat-component/src/main.ts b/packages/chat-component/src/main.ts index 3619222e..7ddc7ad3 100644 --- a/packages/chat-component/src/main.ts +++ b/packages/chat-component/src/main.ts @@ -360,8 +360,7 @@ export class ChatComponent extends LitElement { // render citations if (citations && citations.length > 0) { return html` -

Citations

-
    +
      ${citations.map( (citation) => html`
    1. @@ -386,7 +385,6 @@ export class ChatComponent extends LitElement { // render followup questions if (followupQuestions && followupQuestions.length > 0) { return html` -

      You may also want to ask...

        ${followupQuestions.map( (followupQuestion) => html` @@ -469,8 +467,8 @@ export class ChatComponent extends LitElement { `} ${message.text.map((textEntry) => this.renderTextEntry(textEntry))} - ${this.renderFollowupQuestions(message.followupQuestions)} ${this.renderCitation(message.citations)} + ${this.renderFollowupQuestions(message.followupQuestions)}

        ${message.timestamp}, diff --git a/packages/chat-component/src/style.ts b/packages/chat-component/src/style.ts index 7aa1edf8..4452302a 100644 --- a/packages/chat-component/src/style.ts +++ b/packages/chat-component/src/style.ts @@ -345,6 +345,10 @@ export const mainStyle = css` list-style-type: none; flex-wrap: wrap; } + .items__list.followup { + margin-top: 10px; + border-top: 1px dotted var(--light-gray); + } .items__list.steps { display: block; } From 5a9e5aae5dca0aced070c1ac193706d37214e224 Mon Sep 17 00:00:00 2001 From: Natalia Venditto Date: Fri, 27 Oct 2023 12:12:12 +0200 Subject: [PATCH 06/21] chore: add margin to citations --- packages/chat-component/src/style.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/chat-component/src/style.ts b/packages/chat-component/src/style.ts index 4452302a..b9112e2b 100644 --- a/packages/chat-component/src/style.ts +++ b/packages/chat-component/src/style.ts @@ -344,12 +344,10 @@ export const mainStyle = css` padding: 20px; list-style-type: none; flex-wrap: wrap; - } - .items__list.followup { - margin-top: 10px; border-top: 1px dotted var(--light-gray); } - .items__list.steps { + .items__list { + margin-top: 10px; display: block; } .items__listItem--followup { From 80436e3dec9968b3a0b5a37741737cba80823b25 Mon Sep 17 00:00:00 2001 From: Natalia Venditto Date: Fri, 27 Oct 2023 12:16:42 +0200 Subject: [PATCH 07/21] chore: remove commented regex --- packages/chat-component/src/core/parser/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/chat-component/src/core/parser/index.ts b/packages/chat-component/src/core/parser/index.ts index e31654ba..e3fd9844 100644 --- a/packages/chat-component/src/core/parser/index.ts +++ b/packages/chat-component/src/core/parser/index.ts @@ -44,7 +44,6 @@ export async function parseStreamedMessages({ // if we match a number, store it in the buffer and move on to the next iteration const LIST_ITEM_NUMBER: RegExp = /(\d+)/; const FOLLOW_UP_QUESTION: RegExp = /<<|>|>\?|>>/g; - // const FOLLOW_UP_QUESTION_START: RegExp = /<<|Next/; let matchedStepIndex = chunkValue.match(LIST_ITEM_NUMBER)?.[0]; if (matchedStepIndex) { stepsBuffer.push(matchedStepIndex); From 6449f69afe7bf07f97ca9b480d0d373cd18a3040 Mon Sep 17 00:00:00 2001 From: Natalia Venditto Date: Fri, 27 Oct 2023 14:52:59 +0200 Subject: [PATCH 08/21] fix: remove unnecessary regex and replace --- packages/chat-component/src/core/parser/index.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/chat-component/src/core/parser/index.ts b/packages/chat-component/src/core/parser/index.ts index e3fd9844..ee44e6fe 100644 --- a/packages/chat-component/src/core/parser/index.ts +++ b/packages/chat-component/src/core/parser/index.ts @@ -43,7 +43,6 @@ export async function parseStreamedMessages({ // we use numeric values to identify the beginning of a step // if we match a number, store it in the buffer and move on to the next iteration const LIST_ITEM_NUMBER: RegExp = /(\d+)/; - const FOLLOW_UP_QUESTION: RegExp = /<<|>|>\?|>>/g; let matchedStepIndex = chunkValue.match(LIST_ITEM_NUMBER)?.[0]; if (matchedStepIndex) { stepsBuffer.push(matchedStepIndex); @@ -66,12 +65,10 @@ export async function parseStreamedMessages({ continue; } else if (chunkValue.includes('<<') && isFollowupQuestion) { isFollowupQuestion = true; - chunkValue = chunkValue.replaceAll(FOLLOW_UP_QUESTION, ''); continue; } else if (chunkValue.includes('?>') || chunkValue.includes('>')) { followUpQuestionIndex = followUpQuestionIndex + 1; isFollowupQuestion = true; - chunkValue = chunkValue.replaceAll(FOLLOW_UP_QUESTION, ''); continue; } else if (isFollowupQuestion) { isFollowupQuestion = true; From e0de7af99ffdb907a2f0c06b3c327a30a1e9e6a4 Mon Sep 17 00:00:00 2001 From: Natalia Venditto Date: Fri, 27 Oct 2023 20:32:44 +0200 Subject: [PATCH 09/21] chore: update global config --- packages/chat-component/src/config/global-config.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/chat-component/src/config/global-config.js b/packages/chat-component/src/config/global-config.js index 72345cb3..ead83291 100644 --- a/packages/chat-component/src/config/global-config.js +++ b/packages/chat-component/src/config/global-config.js @@ -17,13 +17,15 @@ const globalConfig = { // This are the labels for the chat button and input CHAT_BUTTON_LABEL_TEXT: 'Ask Support', CHAT_INPUT_PLACEHOLDER: 'Type your question, eg. "How to search and book rentals?"', - USER_IS_BOT: 'Support Bot', + USER_IS_BOT: 'Support Assistant', RESET_BUTTON_LABEL_TEXT: 'X', RESET_BUTTON_TITLE_TEXT: 'Reset current question', RESET_CHAT_BUTTON_TITLE: 'Reset chat', // Copy response to clipboard COPY_RESPONSE_BUTTON_LABEL_TEXT: 'Copy Response', COPIED_SUCCESSFULLY_MESSAGE: 'Response copied!', + // Follow up questions text + FOLLOW_UP_QUESTIONS_LABEL_TEXT: 'You can also ask...', SHOW_THOUGH_PROCESS_BUTTON_LABEL_TEXT: 'Show thought process', HIDE_THOUGH_PROCESS_BUTTON_LABEL_TEXT: 'Hide thought process', LOADING_INDICATOR_TEXT: 'Please wait. We are searching for an answer...', From 1362575178e6dabc2bc120f602288e916beb478e Mon Sep 17 00:00:00 2001 From: Natalia Venditto Date: Fri, 27 Oct 2023 20:33:21 +0200 Subject: [PATCH 10/21] chore: add question icon --- packages/chat-component/public/svg/question-icon.svg | 1 + packages/chat-component/public/svg/readme.md | 1 + 2 files changed, 2 insertions(+) create mode 100644 packages/chat-component/public/svg/question-icon.svg diff --git a/packages/chat-component/public/svg/question-icon.svg b/packages/chat-component/public/svg/question-icon.svg new file mode 100644 index 00000000..588be4cf --- /dev/null +++ b/packages/chat-component/public/svg/question-icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/chat-component/public/svg/readme.md b/packages/chat-component/public/svg/readme.md index 6850ec70..41285c42 100644 --- a/packages/chat-component/public/svg/readme.md +++ b/packages/chat-component/public/svg/readme.md @@ -11,6 +11,7 @@ All icons in this sample are free and open source - [Double Check Icon](https://fontawesome.com/icons/check-double?f=classic&s=solid) - [Email Icon](https://fontawesome.com/icons/envelope-open-text?f=classic&s=solid) - [Lightbulb Icon](https://fontawesome.com/icons/lightbulb?f=classic&s=solid) +- [Question Icon](https://fontawesome.com/icons/circle-question?f=classic&s=solid) ## FontAwesome License From 8fe2d3172209152ea89fd6a430505f69992de368 Mon Sep 17 00:00:00 2001 From: Natalia Venditto Date: Fri, 27 Oct 2023 20:33:39 +0200 Subject: [PATCH 11/21] chore: update styles --- packages/chat-component/src/main.ts | 38 ++++++++++++++-------- packages/chat-component/src/style.ts | 35 +++++++++++++------- packages/chat-component/src/utils/index.ts | 33 +++++++------------ 3 files changed, 60 insertions(+), 46 deletions(-) diff --git a/packages/chat-component/src/main.ts b/packages/chat-component/src/main.ts index 7ddc7ad3..f9312e97 100644 --- a/packages/chat-component/src/main.ts +++ b/packages/chat-component/src/main.ts @@ -16,6 +16,7 @@ import iconDoubleCheck from '../public/svg/doublecheck-icon.svg?inline'; import iconCopyToClipboard from '../public/svg/copy-icon.svg?inline'; import iconSend from '../public/svg/send-icon.svg?inline'; import iconClose from '../public/svg/close-icon.svg?inline'; +import iconQuestion from '../public/svg/question-icon.svg?inline'; /** * A chat component that allows the user to ask questions and get answers from an API. @@ -385,20 +386,29 @@ export class ChatComponent extends LitElement { // render followup questions if (followupQuestions && followupQuestions.length > 0) { return html` -

          - ${followupQuestions.map( - (followupQuestion) => html` -
        • - ${followupQuestion} -
        • - `, - )} -
        +
        + ${globalConfig.FOLLOW_UP_QUESTIONS_LABEL_TEXT} +
          + ${followupQuestions.map( + (followupQuestion) => html` +
        • + ${followupQuestion} +
        • + `, + )} +
        +
        `; } diff --git a/packages/chat-component/src/style.ts b/packages/chat-component/src/style.ts index b9112e2b..bc78656f 100644 --- a/packages/chat-component/src/style.ts +++ b/packages/chat-component/src/style.ts @@ -9,12 +9,13 @@ export const mainStyle = css` --text-color: #123f58; --primary-color: rgba(241, 255, 165, 0.6); --white: #fff; + --black: #111111; --light-gray: #e3e3e3; --dark-gray: #4e5288; - --accent-high: #0040ff; + --accent-high: #b200ff; --accent-dark: #002b23; --accent-light: #e6fbf7; - --accent-lighter: rgba(140, 222, 242, 0.4); + --accent-lighter: rgba(242, 140, 241, 0.4); --error-color: #8a0000; } :host(.dark) { @@ -73,8 +74,8 @@ export const mainStyle = css` display: flex; width: 100%; height: 0; - background: var(--accent-dark); - z-index: 1; + background: var(--black); + z-index: 2; opacity: 0.8; transition: all 0.3s ease-in-out; } @@ -147,7 +148,7 @@ export const mainStyle = css` .aside { top: 30px; left: auto; - z-index: 2; + z-index: 3; background: var(--white); display: block; padding: 20px; @@ -164,7 +165,10 @@ export const mainStyle = css` position: sticky; bottom: 0; z-index: 1; + border-radius: 10px; background: var(--secondary-color); + box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); + padding: 15px 10px 50px; } .form__label { display: block; @@ -185,7 +189,7 @@ export const mainStyle = css` border-bottom: 4px solid var(--accent-high); } .aside__link:not(.active):hover { - border-bottom: 4px solid var(--accent-light); + border-bottom: 4px solid var(--accent-lighter); cursor: pointer; } .aside__link { @@ -338,23 +342,30 @@ export const mainStyle = css` .user-message .chat__txt--info { text-align: right; } + .items__listWrapper { + border-top: 1px dotted var(--light-gray); + display: flex; + } + .items__listWrapper .icon { + padding-top: 35px; + opacity: 0.3; + } .items__list.followup { display: flex; flex-direction: row; padding: 20px; list-style-type: none; flex-wrap: wrap; - border-top: 1px dotted var(--light-gray); } .items__list { - margin-top: 10px; + margin: 10px 0; display: block; } .items__listItem--followup { cursor: pointer; - padding: 0 5px; - border-radius: 5px; - border: 1px solid var(--accent-high); + padding: 0 15px; + border-radius: 15px; + border: 2px solid var(--accent-high); margin: 5px; } .items__listItem--citation { @@ -387,6 +398,7 @@ export const mainStyle = css` text-decoration: none; color: var(--text-color); display: block; + font-size: 1.2rem; } .defaults__list { list-style-type: none; @@ -424,6 +436,7 @@ export const mainStyle = css` transition: all 0.3s ease-in-out; } .defaults__span { + color: var(--accent-high); font-weight: bold; display: block; margin-top: 20px; diff --git a/packages/chat-component/src/utils/index.ts b/packages/chat-component/src/utils/index.ts index 7b05476a..a5653f73 100644 --- a/packages/chat-component/src/utils/index.ts +++ b/packages/chat-component/src/utils/index.ts @@ -1,19 +1,17 @@ // Util functions to process text from response, clean it up and style it // We keep it in this util file because we may not need it once we introduce // a new response format with TypeChat or a similar component -import { NEXT_QUESTION_INDICATOR } from '../config/global-config.js'; // Let's give the response a type so we can use it in the component export function processText(inputText: string, arrays: Array | Array>): ProcessTextReturn { // Keeping all the regex at this level so they can be easily changed or removed - const nextQuestionIndicator = NEXT_QUESTION_INDICATOR; + const nextQuestionMatch = `Next questions:|<<([^>]+)>>`; const findCitations = /\[(.*?)]/g; - const findFollowingSteps = /steps:(.*?)(?:Next Questions:|<<|$)/s; + const findFollowingSteps = /:(.*?)(?:Follow-up questions:|Next questions:|<<|$)/s; const findNextQuestions = /Next Questions:(.*?)$/s; const findQuestionsbyDoubleArrow = /<<([^<>]+)>>/g; - const findNumberedItems = /\d+\.\s+/; - + const findNumberedItems = /^\d+\.\s/; // Find and process citations const citation: NonNullable = {}; let citations: Citation[] = []; @@ -24,7 +22,7 @@ export function processText(inputText: string, arrays: Array | Arr if (!citation[citationText]) { citation[citationText] = referenceCounter++; } - return `[${citation[citationText]}]`; + return `${citation[citationText]}`; }); citations = Object.keys(citation).map((text, index) => ({ ref: index + 1, @@ -34,36 +32,29 @@ export function processText(inputText: string, arrays: Array | Arr // Because the format for followup questions is inconsistent // and sometimes it includes a Next Questions prefix, we need do some extra work - const nextQuestionsIndex = replacedText.indexOf(nextQuestionIndicator); - const hasNextQuestions = nextQuestionsIndex !== -1; + const hasNextQuestions = replacedText.includes(nextQuestionMatch); // Find and store 'follow this steps' portion of the response // considering the fact that sometimes the 'next questions' indicator is present // and sometimes it's not const followingStepsMatch = replacedText.match(findFollowingSteps); const followingStepsText = followingStepsMatch ? followingStepsMatch[1].trim() : ''; const followingSteps = followingStepsText.split('\n').filter(Boolean); - arrays[1] = followingSteps; + const cleanFollowingSteps = followingSteps.map((item) => { + return item.replace(findNumberedItems, ''); + }); + arrays[1] = cleanFollowingSteps; // Determine which regex to use, depending if the indicator is present const nextRegex = hasNextQuestions ? findNextQuestions : findQuestionsbyDoubleArrow; const nextQuestionsMatch = replacedText.match(nextRegex); - const nextQuestionsText = nextQuestionsMatch ? nextQuestionsMatch[1].trim() : ''; let nextQuestions: string[] = []; - // Find and store 'follow up questions' portion of the response - if (hasNextQuestions) { - // Remove the 'Next Questions' prefix from the response - replacedText = replacedText.replace(nextQuestionIndicator, ''); - nextQuestions = nextQuestionsText.split(findNumberedItems).filter(Boolean); - } else { - nextQuestions = nextQuestionsText.split('\n').filter(Boolean); - nextQuestions = cleanUpFollowUp(nextQuestions); - } + nextQuestions = cleanUpFollowUp([...(nextQuestionsMatch as string[])]); // Remove the 'steps', 'citation' and 'next questions' portions of the response // from the response answer - const stepsIndex = replacedText.indexOf('steps:'); + const stepsIndex = replacedText.indexOf('s:'); // eslint-disable-next-line unicorn/no-negated-condition, unicorn/prefer-string-slice - replacedText = stepsIndex !== -1 ? inputText.substring(0, stepsIndex + 6) : inputText; + replacedText = stepsIndex !== -1 ? inputText.substring(0, stepsIndex + 4) : inputText; arrays[2] = nextQuestions; return { replacedText, arrays }; From f79735c7f5831b61b8e9ac1e4ba67059669129c5 Mon Sep 17 00:00:00 2001 From: Natalia Venditto Date: Fri, 27 Oct 2023 20:46:19 +0200 Subject: [PATCH 12/21] chore: update comments to reflect decisions --- packages/chat-component/src/core/parser/index.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/chat-component/src/core/parser/index.ts b/packages/chat-component/src/core/parser/index.ts index ee44e6fe..fa5f5c8b 100644 --- a/packages/chat-component/src/core/parser/index.ts +++ b/packages/chat-component/src/core/parser/index.ts @@ -52,9 +52,10 @@ export async function parseStreamedMessages({ // followup questions are marked either with the word 'Next Questions:' or '<>' or both at the same time // these markers may be split across multiple chunks, so we need to buffer them! // TODO: remove all this logic from the frontend and implement a solution on the backend or with TypeChat - + // we start by creating a buffer when we match the first marker const matchedFollowupQuestionMarker = (!isFollowupQuestion && chunkValue.includes('Next')) || chunkValue.includes('<<'); + // once we do, we can assume that we are processing a followup question and set the flag to true if (matchedFollowupQuestionMarker) { isFollowupQuestion = true; followupQuestionsBuffer.push(chunkValue); @@ -63,13 +64,18 @@ export async function parseStreamedMessages({ isFollowupQuestion = true; followupQuestionsBuffer.push(chunkValue); continue; + // if we're already processing questions, we don't need to check for the marker again + // but we need to check if we reached the end of the followup questions } else if (chunkValue.includes('<<') && isFollowupQuestion) { isFollowupQuestion = true; continue; + // this updates the index, so we add each question to a different array entry + // to simplify styling } else if (chunkValue.includes('?>') || chunkValue.includes('>')) { followUpQuestionIndex = followUpQuestionIndex + 1; isFollowupQuestion = true; continue; + // additional returns need to be removed, but only after we have processed the whole set of chunks } else if (isFollowupQuestion) { isFollowupQuestion = true; chunkValue = chunkValue.replace(/:?\n/, ''); From 65d3533bb20caf44f6cef18515b34e2c2c0a1272 Mon Sep 17 00:00:00 2001 From: Natalia Venditto Date: Mon, 30 Oct 2023 10:28:44 +0100 Subject: [PATCH 13/21] chore: refine cosmetic styles --- packages/chat-component/src/style.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/chat-component/src/style.ts b/packages/chat-component/src/style.ts index bc78656f..fc3145e8 100644 --- a/packages/chat-component/src/style.ts +++ b/packages/chat-component/src/style.ts @@ -166,7 +166,12 @@ export const mainStyle = css` bottom: 0; z-index: 1; border-radius: 10px; - background: var(--secondary-color); + background: linear-gradient( + 0deg, + rgba(245, 245, 245, 1) 0%, + rgba(245, 245, 245, 0.6) 35%, + rgba(245, 245, 245, 0.2) 100% + ); box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); padding: 15px 10px 50px; } @@ -374,6 +379,7 @@ export const mainStyle = css` border-radius: 5px; text-decoration: none; padding: 5px; + margin-top: 5px; font-size: small; } .items__listItem--citation:not(first-child) { @@ -446,6 +452,7 @@ export const mainStyle = css` background-color: var(--accent-lighter); border-radius: 3px; padding: 3px; + margin-left: 3px; } .loading-skeleton { display: flex; From bb0baf30f186bdb9bce603e0e2e30cc0ec4f13cf Mon Sep 17 00:00:00 2001 From: Natalia Venditto Date: Mon, 30 Oct 2023 11:24:00 +0100 Subject: [PATCH 14/21] chore: replace icons with iconcloud set --- .../chat-component/public/svg/close-icon.svg | 4 +- .../chat-component/public/svg/copy-icon.svg | 4 +- .../chat-component/public/svg/delete-icon.svg | 4 +- .../public/svg/doublecheck-icon.svg | 1 - .../public/svg/lightbulb-icon.svg | 4 +- .../public/svg/question-icon.svg | 4 +- packages/chat-component/public/svg/readme.md | 18 +------ .../chat-component/public/svg/send-icon.svg | 4 +- .../public/svg/success-icon.svg | 3 ++ packages/chat-component/src/main.ts | 50 ++++++------------- packages/chat-component/src/style.ts | 26 ++++++++-- 11 files changed, 62 insertions(+), 60 deletions(-) delete mode 100644 packages/chat-component/public/svg/doublecheck-icon.svg create mode 100644 packages/chat-component/public/svg/success-icon.svg diff --git a/packages/chat-component/public/svg/close-icon.svg b/packages/chat-component/public/svg/close-icon.svg index 59e0d29e..871991a5 100644 --- a/packages/chat-component/public/svg/close-icon.svg +++ b/packages/chat-component/public/svg/close-icon.svg @@ -1 +1,3 @@ - \ No newline at end of file + + + \ No newline at end of file diff --git a/packages/chat-component/public/svg/copy-icon.svg b/packages/chat-component/public/svg/copy-icon.svg index b10d481e..a775ad69 100644 --- a/packages/chat-component/public/svg/copy-icon.svg +++ b/packages/chat-component/public/svg/copy-icon.svg @@ -1 +1,3 @@ - \ No newline at end of file + + + \ No newline at end of file diff --git a/packages/chat-component/public/svg/delete-icon.svg b/packages/chat-component/public/svg/delete-icon.svg index 30f63f43..7123a1fd 100644 --- a/packages/chat-component/public/svg/delete-icon.svg +++ b/packages/chat-component/public/svg/delete-icon.svg @@ -1 +1,3 @@ - \ No newline at end of file + + + \ No newline at end of file diff --git a/packages/chat-component/public/svg/doublecheck-icon.svg b/packages/chat-component/public/svg/doublecheck-icon.svg deleted file mode 100644 index ed022101..00000000 --- a/packages/chat-component/public/svg/doublecheck-icon.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/chat-component/public/svg/lightbulb-icon.svg b/packages/chat-component/public/svg/lightbulb-icon.svg index c75b9c1a..088057aa 100644 --- a/packages/chat-component/public/svg/lightbulb-icon.svg +++ b/packages/chat-component/public/svg/lightbulb-icon.svg @@ -1 +1,3 @@ - \ No newline at end of file + + + \ No newline at end of file diff --git a/packages/chat-component/public/svg/question-icon.svg b/packages/chat-component/public/svg/question-icon.svg index 588be4cf..9645a0c5 100644 --- a/packages/chat-component/public/svg/question-icon.svg +++ b/packages/chat-component/public/svg/question-icon.svg @@ -1 +1,3 @@ - \ No newline at end of file + + + \ No newline at end of file diff --git a/packages/chat-component/public/svg/readme.md b/packages/chat-component/public/svg/readme.md index 41285c42..e9c0f3a7 100644 --- a/packages/chat-component/public/svg/readme.md +++ b/packages/chat-component/public/svg/readme.md @@ -1,18 +1,4 @@ ## Icon sources -All icons in this sample are free and open source - -- [Trash Can Icon](https://fontawesome.com/icons/trash?f=classic&s=solid) -- [Send Icon](https://fontawesome.com/icons/paper-plane?f=classic&s=solid) -- [Settings Icon](https://fontawesome.com/icons/cog?f=classic&s=solid) -- [Close Icon](https://fontawesome.com/icons/times-circle?f=classic&s=solid) -- [Info Icon](https://fontawesome.com/icons/info-circle?f=classic&s=solid) -- [Copy Icon](https://fontawesome.com/icons/copy?f=classic&s=solid) -- [Double Check Icon](https://fontawesome.com/icons/check-double?f=classic&s=solid) -- [Email Icon](https://fontawesome.com/icons/envelope-open-text?f=classic&s=solid) -- [Lightbulb Icon](https://fontawesome.com/icons/lightbulb?f=classic&s=solid) -- [Question Icon](https://fontawesome.com/icons/circle-question?f=classic&s=solid) - -## FontAwesome License - -For the full information and license, please visit [Font Awesome License](https://github.com/FortAwesome/Font-Awesome/blob/6.x/LICENSE.txt) +All icons in this sample are part of the +https://iconcloud.design/ icon set. diff --git a/packages/chat-component/public/svg/send-icon.svg b/packages/chat-component/public/svg/send-icon.svg index c260761c..1dcfb5b4 100644 --- a/packages/chat-component/public/svg/send-icon.svg +++ b/packages/chat-component/public/svg/send-icon.svg @@ -1 +1,3 @@ - \ No newline at end of file + + + \ No newline at end of file diff --git a/packages/chat-component/public/svg/success-icon.svg b/packages/chat-component/public/svg/success-icon.svg new file mode 100644 index 00000000..172bce2e --- /dev/null +++ b/packages/chat-component/public/svg/success-icon.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/chat-component/src/main.ts b/packages/chat-component/src/main.ts index f9312e97..c5868071 100644 --- a/packages/chat-component/src/main.ts +++ b/packages/chat-component/src/main.ts @@ -8,15 +8,17 @@ import { getAPIResponse } from './core/http/index.js'; import { parseStreamedMessages } from './core/parser/index.js'; import { mainStyle } from './style.js'; import { getTimestamp, processText } from './utils/index.js'; +import { unsafeSVG } from 'lit/directives/unsafe-svg.js'; // TODO: allow host applications to customize these icons -import iconLightBulb from '../public/svg/lightbulb-icon.svg?inline'; -import iconDelete from '../public/svg/delete-icon.svg?inline'; -import iconDoubleCheck from '../public/svg/doublecheck-icon.svg?inline'; -import iconCopyToClipboard from '../public/svg/copy-icon.svg?inline'; -import iconSend from '../public/svg/send-icon.svg?inline'; -import iconClose from '../public/svg/close-icon.svg?inline'; -import iconQuestion from '../public/svg/question-icon.svg?inline'; + +import iconLightBulb from '../public/svg/lightbulb-icon.svg?raw'; +import iconDelete from '../public/svg/delete-icon.svg?raw'; +import iconSuccess from '../public/svg/success-icon.svg?raw'; +import iconCopyToClipboard from '../public/svg/copy-icon.svg?raw'; +import iconSend from '../public/svg/send-icon.svg?raw'; +import iconClose from '../public/svg/close-icon.svg?raw'; +import iconQuestion from '../public/svg/question-icon.svg?raw'; /** * A chat component that allows the user to ask questions and get answers from an API. @@ -387,13 +389,7 @@ export class ChatComponent extends LitElement { if (followupQuestions && followupQuestions.length > 0) { return html`
        - ${globalConfig.FOLLOW_UP_QUESTIONS_LABEL_TEXT} + ${unsafeSVG(iconQuestion)}
          ${followupQuestions.map( (followupQuestion) => html` @@ -430,7 +426,7 @@ export class ChatComponent extends LitElement { @click="${this.resetCurrentChat}" > ${globalConfig.RESET_CHAT_BUTTON_TITLE} - ${globalConfig.RESET_CHAT_BUTTON_TITLE} + ${unsafeSVG(iconDelete)}
          @@ -450,12 +446,8 @@ export class ChatComponent extends LitElement { ${globalConfig.SHOW_THOUGH_PROCESS_BUTTON_LABEL_TEXT} - ${globalConfig.SHOW_THOUGH_PROCESS_BUTTON_LABEL_TEXT} + + ${unsafeSVG(iconLightBulb)} `} ${message.text.map((textEntry) => this.renderTextEntry(textEntry))} @@ -566,7 +553,7 @@ export class ChatComponent extends LitElement { title="${globalConfig.CHAT_BUTTON_LABEL_TEXT}" ?disabled="${this.isDisabled}" > - ${globalConfig.CHAT_BUTTON_LABEL_TEXT} + ${unsafeSVG(iconSend)}