From 237e913e53f0a77abe2ffb2dc069d15a9bdf658f Mon Sep 17 00:00:00 2001 From: Dogus Atasoy Date: Thu, 23 May 2024 18:37:15 +0200 Subject: [PATCH 1/3] updated rendered card maps to add normal cards to the list too --- example/src/main.ts | 12 +- example/src/samples/sample-data.ts | 739 ++++++++++----------- package-lock.json | 4 +- package.json | 2 +- src/components/chat-item/chat-item-card.ts | 13 +- src/components/chat-item/chat-wrapper.ts | 56 +- src/helper/chat-item.ts | 8 + 7 files changed, 400 insertions(+), 434 deletions(-) create mode 100644 src/helper/chat-item.ts diff --git a/example/src/main.ts b/example/src/main.ts index 23e1be90..67293c30 100644 --- a/example/src/main.ts +++ b/example/src/main.ts @@ -192,19 +192,19 @@ export const createMynahUI = (initialData?: MynahUIDataModel): MynahUI => { Log(`File clicked: ${filePath}`); }, onFileActionClick: (tabId, messageId, filePath, actionName) => { + console.log("YARAK!"); Log(`File action clicked: ${filePath} -> ${actionName}`); switch (actionName) { - case 'update-comment': - case 'comment-to-change': - showCustomForm(tabId); - break; case 'reject-change': - mynahUI.updateChatAnswerWithMessageId(tabId, 'file-list-message', exampleFileListChatItemForUpdate); + mynahUI.updateChatAnswerWithMessageId(tabId, messageId, exampleFileListChatItemForUpdate); + break; + case 'revert-rejection': + mynahUI.updateChatAnswerWithMessageId(tabId, messageId, {fileList: exampleFileListChatItem.fileList}); break; default: break; } - mynahUI.updateChatAnswerWithMessageId(tabId, 'file-list-message', exampleFileListChatItemForUpdate); + // mynahUI.updateChatAnswerWithMessageId(tabId, messageId, exampleFileListChatItemForUpdate); }, onCustomFormAction: (tabId, action) => { Log(`Custom form action clicked for tab ${tabId}:
diff --git a/example/src/samples/sample-data.ts b/example/src/samples/sample-data.ts index 18aeeb43..2bab82ca 100644 --- a/example/src/samples/sample-data.ts +++ b/example/src/samples/sample-data.ts @@ -19,29 +19,29 @@ import SampleCode from './sample-code.md'; import { Commands } from '../commands'; export const mynahUIQRImageBase64 = - 'data:image/png;base64,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'; + 'data:image/png;base64,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'; export const mynahUIQRMarkdown = `![Mynah UI](${mynahUIQRImageBase64})`; // react stateless function component example export const exampleSources = [ - { - url: 'https://github.com/aws/mynah-ui', - title: 'MynahUI', - body: '#### A Data & Event Drivent Chat Interface Library for Browsers and Webviews', - }, - { - url: 'https://github.com/aws/mynah-ui/blob/main/docs/STARTUP.md', - title: 'MynahUI initial setup', - body: `Simply install it from npm with your favorite package manager. + { + url: 'https://github.com/aws/mynah-ui', + title: 'MynahUI', + body: '#### A Data & Event Drivent Chat Interface Library for Browsers and Webviews', + }, + { + url: 'https://github.com/aws/mynah-ui/blob/main/docs/STARTUP.md', + title: 'MynahUI initial setup', + body: `Simply install it from npm with your favorite package manager. \`\`\` npm install @aws/mynah-ui \`\`\` `, - }, - { - url: 'https://github.com/aws/mynah-ui/blob/main/docs/USAGE.md', - title: 'How to use MynahUI', - body: `To see how to configure statics for MynahUI please refer to **[Configuration](./CONFIG.md)** document. + }, + { + url: 'https://github.com/aws/mynah-ui/blob/main/docs/USAGE.md', + title: 'How to use MynahUI', + body: `To see how to configure statics for MynahUI please refer to **[Configuration](./CONFIG.md)** document. Lastly before you start reading here, you can find more details on the **[Data Model](./DATAMODEL.md)** document. That document also contains visuals related with each type of the chat message in detail. @@ -56,344 +56,297 @@ export const exampleSources = [ mynahUI.updateStore(...); \`\`\` `, - }, + }, ] as SourceLink[]; export const sampleMarkdownList: Partial[] = [ - { body: `${sampleList0 as string}`}, - { body: `${sampleList1 as string}`}, - { body: `${sampleList2 as string}`}, - { body: `${sampleList3 as string}`}, - { body: `${sampleList4 as string}`}, + { body: `${sampleList0 as string}` }, + { body: `${sampleList1 as string}` }, + { body: `${sampleList2 as string}` }, + { body: `${sampleList3 as string}` }, + { body: `${sampleList4 as string}` }, ]; export const exampleStreamParts: Partial[] = [ - { body: `${md0 as string}` }, - { body: `${md1 as string}` }, - { body: `${md2 as string}` }, - { body: `${md3 as string}` }, - { body: `${md4 as string}` }, - { body: `${md5 as string}` }, - { body: `${md6 as string}` }, - { body: `${md7 as string}` }, - { body: `${md8 as string}` }, - { body: `${md9 as string}` }, - { body: `${md10 as string}` }, - { - relatedContent: { - content: exampleSources, - title: 'Sources', - }, - codeReference: [ - { - recommendationContentSpan: { - start: 952, - end: 967, - }, - information: 'Say Hello to **`MynahUI`**.', - }, - { - recommendationContentSpan: { - start: 1034, - end: 1409, + { body: `${md0 as string}` }, + { body: `${md1 as string}` }, + { body: `${md2 as string}` }, + { body: `${md3 as string}` }, + { body: `${md4 as string}` }, + { body: `${md5 as string}` }, + { body: `${md6 as string}` }, + { body: `${md7 as string}` }, + { body: `${md8 as string}` }, + { body: `${md9 as string}` }, + { body: `${md10 as string}` }, + { + relatedContent: { + content: exampleSources, + title: 'Sources', }, - information: 'Reference code *under the Apache License 2.0 license* from repository **`@aws/mynah-ui`**.', - }, - ], - }, + codeReference: [ + { + recommendationContentSpan: { + start: 952, + end: 967, + }, + information: 'Say Hello to **`MynahUI`**.', + }, + { + recommendationContentSpan: { + start: 1034, + end: 1409, + }, + information: 'Reference code *under the Apache License 2.0 license* from repository **`@aws/mynah-ui`**.', + }, + ], + }, ]; export const exampleCodeBlockToInsert = SampleCode; export const exampleRichFollowups: ChatItem = { - type: ChatItemType.SYSTEM_PROMPT, - messageId: new Date().getTime().toString(), - followUp: { - text: 'Rich followups', - options: [ - { - pillText: 'Accept', - icon: MynahIcons.OK, - description: 'You can accept by clicking this.', - status: 'success', - }, - { - pillText: 'Reject', - icon: MynahIcons.CANCEL, - status: 'error', - }, - { - pillText: 'Retry', - icon: MynahIcons.REFRESH, - status: 'warning', - }, - { - pillText: 'Do nothing', - icon: MynahIcons.BLOCK, - status: 'info', - }, - ], - }, + type: ChatItemType.SYSTEM_PROMPT, + messageId: new Date().getTime().toString(), + followUp: { + text: 'Rich followups', + options: [ + { + pillText: 'Accept', + icon: MynahIcons.OK, + description: 'You can accept by clicking this.', + status: 'success', + }, + { + pillText: 'Reject', + icon: MynahIcons.CANCEL, + status: 'error', + }, + { + pillText: 'Retry', + icon: MynahIcons.REFRESH, + status: 'warning', + }, + { + pillText: 'Do nothing', + icon: MynahIcons.BLOCK, + status: 'info', + }, + ], + }, }; export const defaultFollowUps: ChatItem = { - type: ChatItemType.ANSWER, - messageId: new Date().getTime().toString(), - followUp: { - text: 'Example card types', - options: [ - { - command: Commands.STATUS_CARDS, - pillText: 'Cards with status', - }, - { - command: Commands.FORM_CARD, - pillText: 'Form items', - }, - { - command: Commands.CARD_WITH_MARKDOWN_LIST, - pillText: 'Markdown list', - }, - { - command: Commands.CARD_SNAPS_TO_TOP, - pillText: 'Snaps to top', - }, - { - command: Commands.FILE_LIST_CARD, - pillText: 'File list', - }, - { - command: Commands.PROGRESSIVE_CARD, - pillText: 'Progressive', - }, - { - command: Commands.IMAGE_IN_CARD, - pillText: 'Image inside', - }, - { - command: Commands.CUSTOM_RENDERER_CARDS, - pillText: 'Custom renderers', - }, - { - pillText: 'Followups on right', - command: Commands.FOLLOWUPS_AT_RIGHT, - }, - { - pillText: 'Some auto reply', - prompt: 'Some random auto reply here.', - }, - ], - }, + type: ChatItemType.ANSWER, + messageId: new Date().getTime().toString(), + followUp: { + text: 'Example card types', + options: [ + { + command: Commands.STATUS_CARDS, + pillText: 'Cards with status', + }, + { + command: Commands.FORM_CARD, + pillText: 'Form items', + }, + { + command: Commands.CARD_WITH_MARKDOWN_LIST, + pillText: 'Markdown list', + }, + { + command: Commands.CARD_SNAPS_TO_TOP, + pillText: 'Snaps to top', + }, + { + command: Commands.FILE_LIST_CARD, + pillText: 'File list', + }, + { + command: Commands.PROGRESSIVE_CARD, + pillText: 'Progressive', + }, + { + command: Commands.IMAGE_IN_CARD, + pillText: 'Image inside', + }, + { + command: Commands.CUSTOM_RENDERER_CARDS, + pillText: 'Custom renderers', + }, + { + pillText: 'Followups on right', + command: Commands.FOLLOWUPS_AT_RIGHT, + }, + { + pillText: 'Some auto reply', + prompt: 'Some random auto reply here.', + }, + ], + }, }; export const exampleFileListChatItem: ChatItem = { - type: ChatItemType.CODE_RESULT, - body: '#### Here are the changed files:', - buttons: [ - { - id: 'open-diff-viewer', - text: 'Open Diff Viewer', - icon: MynahIcons.EXTERNAL, - status: 'info', - disabled: false, - }, - ], - fileList: { - filePaths: ['dummy.ts', 'src/App.tsx', 'devfile.yaml', 'src/App.test.tsx'], - deletedFiles: ['src/devfile.yaml'], - actions: { - 'src/App.tsx': [ - { - icon: MynahIcons.CANCEL_CIRCLE, - status: 'info', - name: 'reject-change', - description: 'Reject change', - }, - { - icon: MynahIcons.COMMENT, - name: 'comment-to-change', - description: 'Comment', - }, - ], - 'devfile.yaml': [ - { - icon: MynahIcons.CANCEL_CIRCLE, - status: 'info', - name: 'reject-change', - description: 'Reject change', + type: ChatItemType.ANSWER, + body: '', + messageId: `FILE_LIST_${new Date().getTime().toString()}`, + fileList: { + rootFolderTitle: 'Changes', + filePaths: ['src/index.ts'], + deletedFiles: [], + actions: { + 'src/index.ts': [ + { + icon: MynahIcons.CANCEL_CIRCLE, + name: 'reject-change', + description: 'Reject Change', + }, + ], }, - ], - }, - details: { - 'src/devfile.yaml': { - status: 'error', - label: 'Change rejected', - icon: MynahIcons.REVERT, - }, - }, - }, - codeReference: [ - { - information: 'Reference code *under the MIT license* from repository `amazon`.', - }, - { - information: 'Reference code *under the MIT license* from repository `aws`.', }, - ], - canBeVoted: true, - messageId: 'file-list-message', }; export const exampleFileListChatItemForUpdate: Partial = { - fileList: { - filePaths: ['src/App.tsx', 'src/App.test.tsx'], - details: { - 'src/App.tsx': { - status: 'error', - label: 'File rejected', - icon: MynahIcons.CANCEL_CIRCLE, - }, - 'src/App.test.tsx': { - status: 'warning', - label: 'Comment added', - icon: MynahIcons.COMMENT, - }, - }, - actions: { - 'src/App.tsx': [ - { - icon: MynahIcons.REVERT, - name: 'revert-rejection', - description: 'Revert rejection', + type: ChatItemType.ANSWER, + fileList: { + rootFolderTitle: 'Changes', + filePaths: ['src/index.ts'], + deletedFiles: [], + details: { + 'src/index.ts': { + status: 'error', + label: 'File rejected', + icon: MynahIcons.CANCEL_CIRCLE, + }, }, - ], - 'src/App.test.tsx': [ - { - icon: MynahIcons.PENCIL, - name: 'update-comment', - description: 'Update comment', + actions: { + 'src/index.ts': [ + { + icon: MynahIcons.REVERT, + name: 'revert-rejection', + description: 'Revert rejection', + }, + ], }, - ], }, - }, }; export const exampleFormChatItem: ChatItem = { - type: ChatItemType.ANSWER, - messageId: new Date().getTime().toString(), - body: `Can you help us to improve our AI Assistant? Please fill the form below and hit **Submit** to send your feedback. + type: ChatItemType.ANSWER, + messageId: new Date().getTime().toString(), + body: `Can you help us to improve our AI Assistant? Please fill the form below and hit **Submit** to send your feedback. _To send the form, mandatory items should be filled._`, - formItems: [ - { - id: 'expertise-area', - type: 'select', - title: `Area of expertise`, - options: [ + formItems: [ + { + id: 'expertise-area', + type: 'select', + title: `Area of expertise`, + options: [ + { + label: 'Frontend', + value: 'frontend', + }, + { + label: 'Backend', + value: 'backend', + }, + { + label: 'Data Science', + value: 'datascience', + }, + { + label: 'Other', + value: 'other', + }, + ], + }, { - label: 'Frontend', - value: 'frontend', + id: 'preferred-ide', + type: 'radiogroup', + title: `Preferred IDE`, + options: [ + { + label: 'VSCode', + value: 'vscode', + }, + { + label: 'JetBrains IntelliJ', + value: 'intellij', + }, + { + label: 'Visual Studio', + value: 'visualstudio', + }, + ], }, { - label: 'Backend', - value: 'backend', + id: 'working-hours', + type: 'numericinput', + title: `How many hours are you using an IDE weekly?`, + placeholder: 'IDE working hours', }, { - label: 'Data Science', - value: 'datascience', + id: 'email', + type: 'textinput', + mandatory: true, + title: `Email`, + placeholder: 'email', }, { - label: 'Other', - value: 'other', + id: 'name', + type: 'textinput', + mandatory: true, + title: `Name`, + placeholder: 'Name and Surname', }, - ], - }, - { - id: 'preferred-ide', - type: 'radiogroup', - title: `Preferred IDE`, - options: [ { - label: 'VSCode', - value: 'vscode', + id: 'ease-of-usage-rating', + type: 'stars', + mandatory: true, + title: `How easy is it to use our AI assistant?`, }, { - label: 'JetBrains IntelliJ', - value: 'intellij', + id: 'accuracy-rating', + type: 'stars', + mandatory: true, + title: `How accurate are the answers you get from our AI assistant?`, }, { - label: 'Visual Studio', - value: 'visualstudio', + id: 'general-rating', + type: 'stars', + title: `How do feel about our AI assistant in general?`, }, - ], - }, - { - id: 'working-hours', - type: 'numericinput', - title: `How many hours are you using an IDE weekly?`, - placeholder: 'IDE working hours', - }, - { - id: 'email', - type: 'textinput', - mandatory: true, - title: `Email`, - placeholder: 'email', - }, - { - id: 'name', - type: 'textinput', - mandatory: true, - title: `Name`, - placeholder: 'Name and Surname', - }, - { - id: 'ease-of-usage-rating', - type: 'stars', - mandatory: true, - title: `How easy is it to use our AI assistant?`, - }, - { - id: 'accuracy-rating', - type: 'stars', - mandatory: true, - title: `How accurate are the answers you get from our AI assistant?`, - }, - { - id: 'general-rating', - type: 'stars', - title: `How do feel about our AI assistant in general?`, - }, - { - id: 'description', - type: 'textarea', - title: `Any other things you would like to share?`, - placeholder: 'Write your feelings about our tool', - }, - ], - buttons: [ - { - id: 'submit', - text: 'Submit', - status: 'info', - }, - { - id: 'cancel-feedback', - text: 'Cancel', - keepCardAfterClick: false, - waitMandatoryFormItems: false, - }, - ], + { + id: 'description', + type: 'textarea', + title: `Any other things you would like to share?`, + placeholder: 'Write your feelings about our tool', + }, + ], + buttons: [ + { + id: 'submit', + text: 'Submit', + status: 'info', + }, + { + id: 'cancel-feedback', + text: 'Cancel', + keepCardAfterClick: false, + waitMandatoryFormItems: false, + }, + ], }; const checkIcons = { - wait: '☐', - current: '☐', - done: '☑', + wait: '☐', + current: '☐', + done: '☑', }; export const exampleProgressCards: Partial[] = [ - { - body: `Hi there, we're currently working on your task. You can follow the steps below: + { + body: `Hi there, we're currently working on your task. You can follow the steps below: ${checkIcons.wait} Reading your files in the project, @@ -408,9 +361,9 @@ ${checkIcons.wait} Creating a refactor plan ${checkIcons.wait} Showing the plan details Once it is done, you'll be notified.`, - }, - { - body: `Hi there, we're currently working on your task. You can follow the steps below: + }, + { + body: `Hi there, we're currently working on your task. You can follow the steps below: ${checkIcons.current} Reading your files in the project, @@ -425,9 +378,9 @@ ${checkIcons.wait} Creating a refactor plan ${checkIcons.wait} Showing the plan details Once it is done, you'll be notified.`, - }, - { - body: `Hi there, we're currently working on your task. You can follow the steps below: + }, + { + body: `Hi there, we're currently working on your task. You can follow the steps below: ${checkIcons.done} Reading your files in the project, @@ -442,9 +395,9 @@ ${checkIcons.wait} Creating a refactor plan ${checkIcons.wait} Showing the plan details Once it is done, you'll be notified.`, - }, - { - body: `Hi there, we're currently working on your task. You can follow the steps below: + }, + { + body: `Hi there, we're currently working on your task. You can follow the steps below: ${checkIcons.done} Reading your files in the project, @@ -459,9 +412,9 @@ ${checkIcons.wait} Creating a refactor plan ${checkIcons.wait} Showing the plan details Once it is done, you'll be notified.`, - }, - { - body: `Hi there, we're currently working on your task. You can follow the steps below: + }, + { + body: `Hi there, we're currently working on your task. You can follow the steps below: ${checkIcons.done} Reading your files in the project, @@ -476,9 +429,9 @@ ${checkIcons.wait} Creating a refactor plan ${checkIcons.wait} Showing the plan details Once it is done, you'll be notified.`, - }, - { - body: `Hi there, we're currently working on your task. You can follow the steps below: + }, + { + body: `Hi there, we're currently working on your task. You can follow the steps below: ${checkIcons.done} Reading your files in the project, @@ -493,9 +446,9 @@ ${checkIcons.current} Creating a refactor plan ${checkIcons.wait} Showing the plan details Once it is done, you'll be notified.`, - }, - { - body: `Hi there, we're currently working on your task. You can follow the steps below: + }, + { + body: `Hi there, we're currently working on your task. You can follow the steps below: ${checkIcons.done} Reading your files in the project, @@ -510,9 +463,9 @@ ${checkIcons.done} Creating a refactor plan ${checkIcons.current} Showing the plan details Once it is done, you'll be notified.`, - }, - { - body: `Hi there, we're currently working on your task. You can follow the steps below: + }, + { + body: `Hi there, we're currently working on your task. You can follow the steps below: ${checkIcons.done} Reading your files in the project, @@ -531,32 +484,32 @@ Your Refactor analysis is ready! You can review it by opening the Markdown file: You can also ask me any follow-up questions that you have or adjust any part by generating a revised analysis. `, - fileList: { - fileTreeTitle: '', - filePaths: ['Refactor_analysis_[id].pdf'], + fileList: { + fileTreeTitle: '', + filePaths: ['Refactor_analysis_[id].pdf'], + }, }, - }, ]; export const exampleImageCard = (): ChatItem => { - return { - messageId: new Date().getTime().toString(), - type: ChatItemType.ANSWER, - body: ` + return { + messageId: new Date().getTime().toString(), + type: ChatItemType.ANSWER, + body: ` ### Image! Here's a QR code for mynah-ui github link: ${mynahUIQRMarkdown} `, - }; + }; }; export const exampleCustomRendererWithHTMLMarkup = (): ChatItem => { - return { - messageId: new Date().getTime().toString(), - type: ChatItemType.ANSWER, - canBeVoted: true, - customRenderer: ` + return { + messageId: new Date().getTime().toString(), + type: ChatItemType.ANSWER, + canBeVoted: true, + customRenderer: `

Custom renderer's with HTML markup string

Here you will find some custom html rendering examples which may not be available with markdown or pretty hard to generate. @@ -640,7 +593,7 @@ You can find more information and references documentation for details and limitations.

`, - }; + }; }; const attachmentIcon = ` @@ -648,68 +601,68 @@ const attachmentIcon = ` `; export const exampleCustomRendererWithDomBuilderJson: ChatItem = { - messageId: new Date().getTime().toString(), - type: ChatItemType.ANSWER, - canBeVoted: true, - body: `Your Refactor analysis is ready! You can review it by opening the Markdown file: [file_name](#hello-pdf) + messageId: new Date().getTime().toString(), + type: ChatItemType.ANSWER, + canBeVoted: true, + body: `Your Refactor analysis is ready! You can review it by opening the Markdown file: [file_name](#hello-pdf) You can also ask me any follow-up questions that you have or adjust any part by generating a revised analysis.`, - customRenderer: [ - { - type: 'blockquote', - events: { - click: (e: Event) => { - console.log('Hello!', e); - }, - }, - - children: [ + customRenderer: [ { - type: 'table', - children: [ - { - type: 'tr', - children: [ - { - type: 'td', - attributes: { - style: 'min-width: 30px; width: 30px;', - }, - children: [ - { - type: 'img', - attributes: { - src: `data:image/svg+xml;base64,${window.btoa(attachmentIcon)}`, - }, - }, - ], + type: 'blockquote', + events: { + click: (e: Event) => { + console.log('Hello!', e); }, + }, + + children: [ { - type: 'td', - children: [ - { - type: 'strong', - children: ['Refactor_analysis_[id] .pdf'], - }, - ], + type: 'table', + children: [ + { + type: 'tr', + children: [ + { + type: 'td', + attributes: { + style: 'min-width: 30px; width: 30px;', + }, + children: [ + { + type: 'img', + attributes: { + src: `data:image/svg+xml;base64,${window.btoa(attachmentIcon)}`, + }, + }, + ], + }, + { + type: 'td', + children: [ + { + type: 'strong', + children: ['Refactor_analysis_[id] .pdf'], + }, + ], + }, + ], + }, + ], }, - ], - }, - ], + ], }, - ], - }, - ], + ], }; export const exampleDownloadFile: ChatItem = { - messageId: new Date().getTime().toString(), - type: ChatItemType.ANSWER, - canBeVoted: true, - body: `Your Refactor analysis is ready! You can review it by opening the Markdown file: [file_name](#hello-pdf) + messageId: new Date().getTime().toString(), + type: ChatItemType.ANSWER, + canBeVoted: true, + body: `Your Refactor analysis is ready! You can review it by opening the Markdown file: [file_name](#hello-pdf) You can also ask me any follow-up questions that you have or adjust any part by generating a revised analysis.`, - fileList: { - fileTreeTitle: 'Report', - rootFolderTitle: '', - filePaths: ['Refactor_analysis_[id] .pdf'], - }, + fileList: { + fileTreeTitle: 'Report', + rootFolderTitle: '', + filePaths: ['Refactor_analysis_[id] .pdf'], + }, }; diff --git a/package-lock.json b/package-lock.json index 1f9ed289..8f1bc2bf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@aws/mynah-ui", - "version": "4.9.0", + "version": "4.9.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@aws/mynah-ui", - "version": "4.9.0", + "version": "4.9.1", "hasInstallScript": true, "license": "Apache License 2.0", "dependencies": { diff --git a/package.json b/package.json index af8a69b2..c663e44f 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@aws/mynah-ui", "displayName": "AWS Mynah UI", - "version": "4.9.0", + "version": "4.9.1", "description": "AWS Toolkit VSCode and Intellij IDE Extension Mynah UI", "publisher": "Amazon Web Services", "license": "Apache License 2.0", diff --git a/src/components/chat-item/chat-item-card.ts b/src/components/chat-item/chat-item-card.ts index 06e419ff..75dcf35c 100644 --- a/src/components/chat-item/chat-item-card.ts +++ b/src/components/chat-item/chat-item-card.ts @@ -20,6 +20,7 @@ import { ChatItemFormItemsWrapper } from './chat-item-form-items'; import { ChatItemButtonsWrapper } from './chat-item-buttons'; import { cleanHtml } from '../../helper/sanitize'; import { CONTAINER_GAP } from './chat-wrapper'; +import { chatItemHasContent } from '../../helper/chat-item'; const TYPEWRITER_STACK_TIME = 500; export interface ChatItemCardProps { @@ -98,15 +99,9 @@ export class ChatItemCard { return generatedCard; }; - private readonly cardHasContent = (): boolean => ((this.props.chatItem.body != null && this.props.chatItem.body !== '') || - this.props.chatItem.fileList != null || - this.props.chatItem.formItems != null || - this.props.chatItem.customRenderer != null || - this.props.chatItem.buttons != null); - private readonly getCardClasses = (): string[] => { const isNoContent = - !this.cardHasContent() && + !chatItemHasContent(this.props.chatItem) && this.props.chatItem.followUp == null && this.props.chatItem.relatedContent == null && this.props.chatItem.type === ChatItemType.ANSWER; @@ -115,7 +110,7 @@ export class ChatItemCard { `mynah-chat-item-card-status-${this.props.chatItem.status ?? 'default'}`, 'mynah-chat-item-card', `mynah-chat-item-${this.props.chatItem.type ?? ChatItemType.ANSWER}`, - ...(!this.cardHasContent() ? [ 'mynah-chat-item-empty' ] : []), + ...(!chatItemHasContent(this.props.chatItem) ? [ 'mynah-chat-item-empty' ] : []), ...(isNoContent ? [ 'mynah-chat-item-no-content' ] : []), ]; }; @@ -293,7 +288,7 @@ export class ChatItemCard { return [ ...(MynahUITabsStore.getInstance().getTabDataStore(this.props.tabId).getValue('showChatAvatars') === true ? [ this.chatAvatar ] : []), - ...(this.cardHasContent() + ...(chatItemHasContent(this.props.chatItem) ? [ new Card({ onCardEngaged: engagement => { diff --git a/src/components/chat-item/chat-wrapper.ts b/src/components/chat-item/chat-wrapper.ts index 00cd99da..03ee54b0 100644 --- a/src/components/chat-item/chat-wrapper.ts +++ b/src/components/chat-item/chat-wrapper.ts @@ -3,6 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ +import { chatItemHasContent } from '../../helper/chat-item'; import { Config } from '../../helper/config'; import { DomBuilder, ExtendedHTMLElement } from '../../helper/dom'; import { generateUID } from '../../helper/guid'; @@ -28,8 +29,8 @@ export class ChatWrapper { private readonly promptInput: ChatPromptInput; private readonly promptInfo: ExtendedHTMLElement; private readonly promptStickyCard: ExtendedHTMLElement; - private lastChatItemCard: ChatItemCard | null; - private lastChatItemMessageId: string | null; + private lastStreamingChatItemCard: ChatItemCard | null; + private lastStreamingChatItemMessageId: string | null; private allRenderedChatItems: Record = {}; render: ExtendedHTMLElement; constructor (props: ChatWrapperProps) { @@ -141,31 +142,40 @@ export class ChatWrapper { } private readonly insertChatItem = (chatItem: ChatItem): void => { - this.lastChatItemMessageId = (chatItem.messageId != null && chatItem.messageId !== '') ? chatItem.messageId : `TEMP_${generateUID()}`; + const currentMessageId: string = (chatItem.messageId != null && chatItem.messageId !== '') ? chatItem.messageId : `TEMP_${generateUID()}`; const chatItemCard = new ChatItemCard({ tabId: this.props.tabId, chatItem: { ...chatItem, - messageId: this.lastChatItemMessageId + messageId: currentMessageId } }); + if (chatItem.type === ChatItemType.ANSWER_STREAM) { - this.lastChatItemCard?.render.addClass('stream-ended'); - this.lastChatItemCard = chatItemCard; + // End previous streaming card if there is + this.lastStreamingChatItemCard?.render.addClass('stream-ended'); + + // Update the lastStreaming variables with the new one + this.lastStreamingChatItemMessageId = currentMessageId; + this.lastStreamingChatItemCard = chatItemCard; } else if ( - (chatItem.type === ChatItemType.ANSWER || - chatItem.type === ChatItemType.PROMPT || - chatItem.type === ChatItemType.AI_PROMPT || - chatItem.type === ChatItemType.SYSTEM_PROMPT) && chatItem.body !== undefined) { - this.lastChatItemCard?.render.addClass('stream-ended'); - this.lastChatItemCard = null; - this.lastChatItemMessageId = null; + chatItem.type !== ChatItemType.ANSWER && + chatItem.type !== ChatItemType.ANSWER_PART && + chatItemHasContent(chatItem)) { + // If the new card is not a streaming one and it has any kind of content, + // it means that the last card is not a streaming card anymore. + // So end the previous stream and reset the lastStreaming variables + this.lastStreamingChatItemCard?.render.addClass('stream-ended'); + this.lastStreamingChatItemCard = null; + this.lastStreamingChatItemMessageId = null; } + + // Add to render this.chatItemsContainer.insertChild('afterbegin', chatItemCard.render); - if (this.lastChatItemMessageId != null) { - this.allRenderedChatItems[this.lastChatItemMessageId] = chatItemCard; - } + // Add to all rendered chat items map + this.allRenderedChatItems[currentMessageId] = chatItemCard; + if (chatItem.type === ChatItemType.PROMPT || chatItem.type === ChatItemType.SYSTEM_PROMPT) { // Make sure we scroll the chat window to the bottom // Only if it is a PROMPT @@ -174,20 +184,20 @@ export class ChatWrapper { }; public updateLastChatAnswer = (updateWith: Partial): void => { - if (this.lastChatItemCard !== null) { - this.lastChatItemCard.updateCardStack(updateWith); + if (this.lastStreamingChatItemCard !== null) { + this.lastStreamingChatItemCard.updateCardStack(updateWith); if (updateWith.messageId != null && updateWith.messageId !== '') { - if (this.lastChatItemMessageId != null && this.lastChatItemMessageId !== updateWith.messageId) { - const renderChatItemInMap = this.allRenderedChatItems[this.lastChatItemMessageId]; + if (this.lastStreamingChatItemMessageId != null && this.lastStreamingChatItemMessageId !== updateWith.messageId) { + const renderChatItemInMap = this.allRenderedChatItems[this.lastStreamingChatItemMessageId]; if (renderChatItemInMap != null) { this.allRenderedChatItems[updateWith.messageId] = renderChatItemInMap; - if (this.lastChatItemMessageId != null) { + if (this.lastStreamingChatItemMessageId != null) { // eslint-disable-next-line @typescript-eslint/no-dynamic-delete - delete this.allRenderedChatItems[this.lastChatItemMessageId]; + delete this.allRenderedChatItems[this.lastStreamingChatItemMessageId]; } } } - this.lastChatItemMessageId = updateWith.messageId; + this.lastStreamingChatItemMessageId = updateWith.messageId; } } }; diff --git a/src/helper/chat-item.ts b/src/helper/chat-item.ts new file mode 100644 index 00000000..58eeeb8d --- /dev/null +++ b/src/helper/chat-item.ts @@ -0,0 +1,8 @@ +import { ChatItem } from '../static'; + +export const chatItemHasContent = (chatItem: Partial): boolean => ( + (chatItem.body != null && chatItem.body !== '') || +chatItem.fileList != null || +chatItem.formItems != null || +chatItem.customRenderer != null || +chatItem.buttons != null); From b5aced36882c159bf8c808416836f79457b62fae Mon Sep 17 00:00:00 2001 From: Dogus Atasoy Date: Thu, 23 May 2024 18:38:09 +0200 Subject: [PATCH 2/3] removed unnecessary comment --- example/src/main.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/example/src/main.ts b/example/src/main.ts index 67293c30..96fa8978 100644 --- a/example/src/main.ts +++ b/example/src/main.ts @@ -204,7 +204,6 @@ export const createMynahUI = (initialData?: MynahUIDataModel): MynahUI => { default: break; } - // mynahUI.updateChatAnswerWithMessageId(tabId, messageId, exampleFileListChatItemForUpdate); }, onCustomFormAction: (tabId, action) => { Log(`Custom form action clicked for tab ${tabId}:
From 02ad7fdf2f521a44c597e7d8e0e5eafce4080fdd Mon Sep 17 00:00:00 2001 From: Dogus Atasoy Date: Fri, 24 May 2024 11:39:58 +0200 Subject: [PATCH 3/3] removed unnecesaary comments --- example/src/main.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/example/src/main.ts b/example/src/main.ts index 96fa8978..836c527a 100644 --- a/example/src/main.ts +++ b/example/src/main.ts @@ -192,7 +192,6 @@ export const createMynahUI = (initialData?: MynahUIDataModel): MynahUI => { Log(`File clicked: ${filePath}`); }, onFileActionClick: (tabId, messageId, filePath, actionName) => { - console.log("YARAK!"); Log(`File action clicked: ${filePath} -> ${actionName}`); switch (actionName) { case 'reject-change':