From 628065df3f25972b6a66f8d56aa750c4b0818179 Mon Sep 17 00:00:00 2001 From: Shibani Basava Date: Tue, 13 Feb 2024 09:55:47 +0000 Subject: [PATCH] feat: update follow up questions for DI --- .../src/components/chat-component.ts | 9 ++- .../src/components/chat-thread-component.ts | 65 +++++++------------ .../src/components/composable.ts | 10 ++- .../src/components/default-questions.ts | 4 +- .../chat-component/src/components/features.ts | 1 + .../src/components/follow-up-questions.ts | 48 ++++++++++++++ .../src/components/voice-input.ts | 4 +- 7 files changed, 93 insertions(+), 48 deletions(-) create mode 100644 packages/chat-component/src/components/follow-up-questions.ts diff --git a/packages/chat-component/src/components/chat-component.ts b/packages/chat-component/src/components/chat-component.ts index 3c5b3351..0e86d997 100644 --- a/packages/chat-component/src/components/chat-component.ts +++ b/packages/chat-component/src/components/chat-component.ts @@ -120,6 +120,11 @@ export class ChatComponent extends LitElement { @lazyMultiInject(ControllerType.ChatSection) chatSectionControllers: ChatSectionController[] | undefined; + public constructor() { + super(); + this.setQuestionInputValue = this.setQuestionInputValue.bind(this); + } + // Lifecycle method that runs when the component is first connected to the DOM override connectedCallback(): void { super.connectedCallback(); @@ -328,7 +333,7 @@ export class ChatComponent extends LitElement { .svgIcon="${iconLogo}" .context="${this.chatContext}" @on-citation-click="${this.handleCitationClick}" - @on-followup-click="${this.handleInput}" + @on-input="${this.handleInput}" > `; } @@ -338,7 +343,7 @@ export class ChatComponent extends LitElement { ? '' : this.chatInputComponents .filter((component) => component.position === position) - .map((component) => component.render(this.handleInput)); + .map((component) => component.render(this.setQuestionInputValue)); } // Render the chat component as a web component diff --git a/packages/chat-component/src/components/chat-thread-component.ts b/packages/chat-component/src/components/chat-thread-component.ts index 68250246..958c51fa 100644 --- a/packages/chat-component/src/components/chat-thread-component.ts +++ b/packages/chat-component/src/components/chat-thread-component.ts @@ -4,13 +4,15 @@ import { customElement, property, query, state } from 'lit/decorators.js'; import { styles } from '../styles/chat-thread-component.js'; import { globalConfig } from '../config/global-config.js'; -import { unsafeSVG } from 'lit/directives/unsafe-svg.js'; import { unsafeHTML } from 'lit/directives/unsafe-html.js'; -import iconQuestion from '../../public/svg/bubblequestion-icon.svg?raw'; - import { type ChatActionButton } from './chat-action-button.js'; -import { type ChatEntryActionController, ControllerType, lazyMultiInject } from './composable.js'; +import { + type ChatEntryActionController, + type ChatEntryInlineInputController, + ControllerType, + lazyMultiInject, +} from './composable.js'; import { type ChatContextController } from './chat-context.js'; @customElement('chat-thread-component') @@ -41,6 +43,14 @@ export class ChatThreadComponent extends LitElement { @lazyMultiInject(ControllerType.ChatEntryAction) actionCompontents: ChatEntryActionController[] | undefined; + @lazyMultiInject(ControllerType.ChatEntryInlineInput) + inlineInputComponents: ChatEntryInlineInputController[] | undefined; + + public constructor() { + super(); + this.handleInput = this.handleInput.bind(this); + } + connectedCallback() { super.connectedCallback(); if (this.actionCompontents) { @@ -48,6 +58,12 @@ export class ChatThreadComponent extends LitElement { component.attach(this, this.context); } } + + if (this.inlineInputComponents) { + for (const component of this.inlineInputComponents) { + component.attach(this, this.context); + } + } } actionButtonClicked(actionButton: ChatActionButton, entry: ChatThreadEntry, event: Event) { @@ -75,12 +91,10 @@ export class ChatThreadComponent extends LitElement { }, 500); } - handleFollowupQuestionClick(question: string, entry: ChatThreadEntry, event: Event) { - event.preventDefault(); - const followUpClickEvent = new CustomEvent('on-followup-click', { + handleInput(input: string) { + const followUpClickEvent = new CustomEvent('on-input', { detail: { - value: question, - chatThreadEntry: entry, + value: input, }, bubbles: true, composed: true, @@ -150,36 +164,6 @@ export class ChatThreadComponent extends LitElement { return ''; } - renderFollowupQuestions(entry: ChatThreadEntry) { - const followupQuestions = entry.followupQuestions; - // render followup questions - // need to fix first after decoupling of teaserlist - if (followupQuestions && followupQuestions.length > 0) { - return html` -
- ${unsafeSVG(iconQuestion)} - -
- `; - } - - return ''; - } - renderError(error: { message: string }) { return html`

${error.message}

`; } @@ -193,7 +177,8 @@ export class ChatThreadComponent extends LitElement {
${message.isUserMessage ? '' : this.renderResponseActions(message)} ${message.text.map((textEntry) => this.renderTextEntry(textEntry))} ${this.renderCitation(message)} - ${this.renderFollowupQuestions(message)} ${message.error ? this.renderError(message.error) : ''} + ${this.inlineInputComponents?.map((component) => component.render(message, this.handleInput))} + ${message.error ? this.renderError(message.error) : ''}

${message.timestamp}, diff --git a/packages/chat-component/src/components/composable.ts b/packages/chat-component/src/components/composable.ts index a5540332..ea26febc 100644 --- a/packages/chat-component/src/components/composable.ts +++ b/packages/chat-component/src/components/composable.ts @@ -12,6 +12,7 @@ export const ControllerType = { ChatSection: Symbol.for('ChatSectionController'), ChatEntryAction: Symbol.for('ChatEntryActionController'), Citation: Symbol.for('CitationController'), + ChatEntryInlineInput: Symbol.for('ChatEntryInlineInputController'), }; export interface ComposableReactiveController extends ReactiveController { @@ -24,7 +25,7 @@ export abstract class ComposableReactiveControllerBase implements ComposableReac protected context: ChatContextController; attach(host: ReactiveControllerHost, context: ChatContextController) { - this.host = host; + (this.host = host).addController(this); this.context = context; } @@ -34,7 +35,7 @@ export abstract class ComposableReactiveControllerBase implements ComposableReac export interface ChatInputController extends ComposableReactiveController { position: 'left' | 'right' | 'top'; - render: (handleInput: (event: CustomEvent) => void) => TemplateResult; + render: (handleInput: (input: string) => void) => TemplateResult; } export interface ChatInputFooterController extends ComposableReactiveController { @@ -51,6 +52,10 @@ export interface ChatEntryActionController extends ComposableReactiveController render: (entry: ChatThreadEntry, isDisabled: boolean) => TemplateResult; } +export interface ChatEntryInlineInputController extends ComposableReactiveController { + render: (entry: ChatThreadEntry, handleInput: (event: CustomEvent) => void) => TemplateResult; +} + export interface CitationController extends ComposableReactiveController { render: (citation: Citation, url: string) => TemplateResult; } @@ -80,3 +85,4 @@ container.bind(ControllerType.ChatInputFooter).to(Def container.bind(ControllerType.ChatSection).to(DefaultChatSectionController); container.bind(ControllerType.ChatEntryAction).to(DefaultController); container.bind(ControllerType.Citation).to(DefaultController); +container.bind(ControllerType.ChatEntryInlineInput).to(DefaultController); diff --git a/packages/chat-component/src/components/default-questions.ts b/packages/chat-component/src/components/default-questions.ts index 325106e3..f528ddcf 100644 --- a/packages/chat-component/src/components/default-questions.ts +++ b/packages/chat-component/src/components/default-questions.ts @@ -13,7 +13,7 @@ import { html } from 'lit'; export class DefaultQuestionsInputController extends ComposableReactiveControllerBase implements ChatInputController { position = 'top'; - render(handleInput: (event: CustomEvent) => void) { + render(handleInput: (input: string) => void) { const promptTemplate = html` `; diff --git a/packages/chat-component/src/components/features.ts b/packages/chat-component/src/components/features.ts index eeae181a..1a677003 100644 --- a/packages/chat-component/src/components/features.ts +++ b/packages/chat-component/src/components/features.ts @@ -12,4 +12,5 @@ import './teaser-list-component.js'; import './document-previewer.js'; import './citation-previewer.js'; +import './follow-up-questions.js'; // [COMPOSE COMPONENTS END] diff --git a/packages/chat-component/src/components/follow-up-questions.ts b/packages/chat-component/src/components/follow-up-questions.ts new file mode 100644 index 00000000..cc5d50b1 --- /dev/null +++ b/packages/chat-component/src/components/follow-up-questions.ts @@ -0,0 +1,48 @@ +import { injectable } from 'inversify'; +import { + container, + type ChatEntryInlineInputController, + ControllerType, + ComposableReactiveControllerBase, +} from './composable.js'; +import { html } from 'lit'; +import { unsafeSVG } from 'lit/directives/unsafe-svg.js'; +import iconQuestion from '../../public/svg/bubblequestion-icon.svg?raw'; + +@injectable() +export class FollowupQuestionsController + extends ComposableReactiveControllerBase + implements ChatEntryInlineInputController +{ + render(entry: ChatThreadEntry, handleInput: (input: string) => void) { + const followupQuestions = entry.followupQuestions; + // render followup questions + // need to fix first after decoupling of teaserlist + if (followupQuestions && followupQuestions.length > 0) { + return html` +

+ ${unsafeSVG(iconQuestion)} + +
+ `; + } + + return ''; + } +} + +container.bind(ControllerType.ChatEntryInlineInput).to(FollowupQuestionsController); diff --git a/packages/chat-component/src/components/voice-input.ts b/packages/chat-component/src/components/voice-input.ts index 6ad4627b..04a286c8 100644 --- a/packages/chat-component/src/components/voice-input.ts +++ b/packages/chat-component/src/components/voice-input.ts @@ -6,8 +6,8 @@ import { html } from 'lit'; export class VoiceInputController extends ComposableReactiveControllerBase implements ChatInputController { position = 'right'; - render(handleInput: (event: CustomEvent) => void) { - return html``; + render(handleInput: (input: string) => void) { + return html``; } }