@servicetitan/titan-chatbot-ui-anvil2 8.0.0 β 9.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/dist/components/chatbot/__tests-cy__/chatbot-live.test.js +1 -1
- package/dist/components/chatbot/__tests-cy__/chatbot-live.test.js.map +1 -1
- package/dist/components/chatbot/__tests-cy__/chatbot-streaming.test.d.ts +2 -0
- package/dist/components/chatbot/__tests-cy__/chatbot-streaming.test.d.ts.map +1 -0
- package/dist/components/chatbot/__tests-cy__/chatbot-streaming.test.js +11 -0
- package/dist/components/chatbot/__tests-cy__/chatbot-streaming.test.js.map +1 -0
- package/dist/components/chatbot/chatbot.d.ts.map +1 -1
- package/dist/components/chatbot/chatbot.js +5 -2
- package/dist/components/chatbot/chatbot.js.map +1 -1
- package/dist/components/chatbot/messages/chatbot-streaming-progress.d.ts +12 -0
- package/dist/components/chatbot/messages/chatbot-streaming-progress.d.ts.map +1 -0
- package/dist/components/chatbot/messages/chatbot-streaming-progress.js +116 -0
- package/dist/components/chatbot/messages/chatbot-streaming-progress.js.map +1 -0
- package/dist/components/chatbot/messages/chatbot-streaming-progress.module.less +4 -0
- package/dist/components/chatbot/messages/chatbot-streaming-progress.module.less.d.ts +3 -0
- package/package.json +6 -6
- package/src/components/chatbot/__tests-cy__/chatbot-live.test.tsx +1 -1
- package/src/components/chatbot/__tests-cy__/chatbot-streaming.test.tsx +9 -0
- package/src/components/chatbot/chatbot.tsx +7 -1
- package/src/components/chatbot/messages/chatbot-streaming-progress.module.less +4 -0
- package/src/components/chatbot/messages/chatbot-streaming-progress.module.less.d.ts +3 -0
- package/src/components/chatbot/messages/chatbot-streaming-progress.tsx +110 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/components/chatbot/__tests-cy__/chatbot-help-center.test.d.ts +0 -2
- package/dist/components/chatbot/__tests-cy__/chatbot-help-center.test.d.ts.map +0 -1
- package/dist/components/chatbot/__tests-cy__/chatbot-help-center.test.js +0 -11
- package/dist/components/chatbot/__tests-cy__/chatbot-help-center.test.js.map +0 -1
- package/src/components/chatbot/__tests-cy__/chatbot-help-center.test.tsx +0 -9
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
# v9.0.0 (Mon Jun 08 2026)
|
|
2
|
+
|
|
3
|
+
#### π₯ Breaking Change
|
|
4
|
+
|
|
5
|
+
- SPA-8507: Agent Progress Streaming β Frontend Streaming UX [#91](https://github.com/servicetitan/titan-chatbot-client/pull/91) ([@AlexYarmolchuk](https://github.com/AlexYarmolchuk))
|
|
6
|
+
|
|
7
|
+
#### Authors: 1
|
|
8
|
+
|
|
9
|
+
- Alexandr Yarmolchuk ([@AlexYarmolchuk](https://github.com/AlexYarmolchuk))
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
1
13
|
# v8.0.0 (Wed Jun 03 2026)
|
|
2
14
|
|
|
3
15
|
#### π₯ Breaking Change
|
|
@@ -2,7 +2,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import { AnvilProvider } from '@servicetitan/anvil2';
|
|
3
3
|
import { runChatbotLiveSharedTests } from '@servicetitan/cypress-shared';
|
|
4
4
|
import { Chatbot } from '../chatbot';
|
|
5
|
-
describe('[Chatbot]', ()=>{
|
|
5
|
+
describe.skip('[Chatbot]', ()=>{
|
|
6
6
|
runChatbotLiveSharedTests(Chatbot, (component)=>/*#__PURE__*/ _jsx(AnvilProvider, {
|
|
7
7
|
children: component
|
|
8
8
|
}));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/components/chatbot/__tests-cy__/chatbot-live.test.tsx"],"sourcesContent":["import { AnvilProvider } from '@servicetitan/anvil2';\nimport { runChatbotLiveSharedTests } from '@servicetitan/cypress-shared';\nimport { Chatbot } from '../chatbot';\n\ndescribe('[Chatbot]', () => {\n runChatbotLiveSharedTests(Chatbot, component => <AnvilProvider>{component}</AnvilProvider>);\n});\n"],"names":["AnvilProvider","runChatbotLiveSharedTests","Chatbot","describe","component"],"mappings":";AAAA,SAASA,aAAa,QAAQ,uBAAuB;AACrD,SAASC,yBAAyB,QAAQ,+BAA+B;AACzE,SAASC,OAAO,QAAQ,aAAa;AAErCC,
|
|
1
|
+
{"version":3,"sources":["../../../../src/components/chatbot/__tests-cy__/chatbot-live.test.tsx"],"sourcesContent":["import { AnvilProvider } from '@servicetitan/anvil2';\nimport { runChatbotLiveSharedTests } from '@servicetitan/cypress-shared';\nimport { Chatbot } from '../chatbot';\n\ndescribe.skip('[Chatbot]', () => {\n runChatbotLiveSharedTests(Chatbot, component => <AnvilProvider>{component}</AnvilProvider>);\n});\n"],"names":["AnvilProvider","runChatbotLiveSharedTests","Chatbot","describe","skip","component"],"mappings":";AAAA,SAASA,aAAa,QAAQ,uBAAuB;AACrD,SAASC,yBAAyB,QAAQ,+BAA+B;AACzE,SAASC,OAAO,QAAQ,aAAa;AAErCC,SAASC,IAAI,CAAC,aAAa;IACvBH,0BAA0BC,SAASG,CAAAA,0BAAa,KAACL;sBAAeK;;AACpE"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chatbot-streaming.test.d.ts","sourceRoot":"","sources":["../../../../src/components/chatbot/__tests-cy__/chatbot-streaming.test.tsx"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { AnvilProvider } from '@servicetitan/anvil2';
|
|
3
|
+
import { runChatbotStreamingSharedTests } from '@servicetitan/cypress-shared';
|
|
4
|
+
import { Chatbot } from '../chatbot';
|
|
5
|
+
describe('[Chatbot streaming]', ()=>{
|
|
6
|
+
runChatbotStreamingSharedTests(Chatbot, (component)=>/*#__PURE__*/ _jsx(AnvilProvider, {
|
|
7
|
+
children: component
|
|
8
|
+
}));
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
//# sourceMappingURL=chatbot-streaming.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/components/chatbot/__tests-cy__/chatbot-streaming.test.tsx"],"sourcesContent":["import { AnvilProvider } from '@servicetitan/anvil2';\nimport { runChatbotStreamingSharedTests } from '@servicetitan/cypress-shared';\nimport { Chatbot } from '../chatbot';\n\ndescribe('[Chatbot streaming]', () => {\n runChatbotStreamingSharedTests(Chatbot, component => (\n <AnvilProvider>{component}</AnvilProvider>\n ));\n});\n"],"names":["AnvilProvider","runChatbotStreamingSharedTests","Chatbot","describe","component"],"mappings":";AAAA,SAASA,aAAa,QAAQ,uBAAuB;AACrD,SAASC,8BAA8B,QAAQ,+BAA+B;AAC9E,SAASC,OAAO,QAAQ,aAAa;AAErCC,SAAS,uBAAuB;IAC5BF,+BAA+BC,SAASE,CAAAA,0BACpC,KAACJ;sBAAeI;;AAExB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chatbot.d.ts","sourceRoot":"","sources":["../../../src/components/chatbot/chatbot.tsx"],"names":[],"mappings":"AAGA,OAAO,EAIH,mBAAmB,EACtB,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAGH,qBAAqB,
|
|
1
|
+
{"version":3,"file":"chatbot.d.ts","sourceRoot":"","sources":["../../../src/components/chatbot/chatbot.tsx"],"names":[],"mappings":"AAGA,OAAO,EAIH,mBAAmB,EACtB,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAGH,qBAAqB,EAExB,MAAM,iCAAiC,CAAC;AAEzC,OAAO,EAAE,aAAa,EAAE,EAAE,EAAsB,MAAM,OAAO,CAAC;AAY9D,MAAM,WAAW,aAAa;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,cAAc,CAAC,EAAE,qBAAqB,CAAC;IACvC,OAAO,CAAC,EAAE,mBAAmB,CAAC;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACxB;AAED,eAAO,MAAM,OAAO,EAAE,EAAE,CAAC,aAAa,CAiFrC,CAAC"}
|
|
@@ -3,7 +3,7 @@ import { Flex } from '@servicetitan/anvil2';
|
|
|
3
3
|
import { useDependencies } from '@servicetitan/react-ioc';
|
|
4
4
|
import { Chat } from '@servicetitan/titan-chat-ui-anvil2';
|
|
5
5
|
import { ChatParticipantIcon } from '@servicetitan/titan-chat-ui-common';
|
|
6
|
-
import { CHATBOT_UI_BACKEND_STORE_TOKEN, CHATBOT_UI_STORE_TOKEN } from '@servicetitan/titan-chatbot-api';
|
|
6
|
+
import { CHATBOT_UI_BACKEND_STORE_TOKEN, CHATBOT_UI_STORE_TOKEN, isChatbotStreamingEnabled } from '@servicetitan/titan-chatbot-api';
|
|
7
7
|
import { observer } from 'mobx-react';
|
|
8
8
|
import { useEffect, useMemo } from 'react';
|
|
9
9
|
import { ChatbotToChatProviderAdapter } from './chatbot-to-chat-provider-adapter';
|
|
@@ -14,6 +14,7 @@ import { ChatbotMessageQuestion } from './messages/chatbot-message-question';
|
|
|
14
14
|
import { ChatbotMessageTimeout } from './messages/chatbot-message-timeout';
|
|
15
15
|
import { ChatbotMessageTyping } from './messages/chatbot-message-typing';
|
|
16
16
|
import { ChatbotMessageWelcome } from './messages/chatbot-message-welcome';
|
|
17
|
+
import { ChatbotStreamingProgress } from './messages/chatbot-streaming-progress';
|
|
17
18
|
import { ChatbotMessageTemplateAgent } from './templates/chatbot-message-template-agent';
|
|
18
19
|
export const Chatbot = observer(({ botIcon, botName, className, customizations, onReady, style })=>{
|
|
19
20
|
var _customizationsMerged_filters, _customizationsMerged_filters1;
|
|
@@ -44,9 +45,11 @@ export const Chatbot = observer(({ botIcon, botName, className, customizations,
|
|
|
44
45
|
error: customizations === null || customizations === void 0 ? void 0 : customizations.error,
|
|
45
46
|
filters: customizations === null || customizations === void 0 ? void 0 : customizations.filters,
|
|
46
47
|
feedback: customizations === null || customizations === void 0 ? void 0 : customizations.feedback,
|
|
48
|
+
streaming: customizations === null || customizations === void 0 ? void 0 : customizations.streaming,
|
|
49
|
+
timeouts: customizations === null || customizations === void 0 ? void 0 : customizations.timeouts,
|
|
47
50
|
footerComponent: customizations === null || customizations === void 0 ? void 0 : customizations.footerComponent,
|
|
48
51
|
messageTyping: (_ref1 = customizations === null || customizations === void 0 ? void 0 : customizations.messageTyping) !== null && _ref1 !== void 0 ? _ref1 : {
|
|
49
|
-
component: ChatbotMessageTyping
|
|
52
|
+
component: isChatbotStreamingEnabled(customizations) ? ChatbotStreamingProgress : ChatbotMessageTyping
|
|
50
53
|
},
|
|
51
54
|
messages: [
|
|
52
55
|
...(_ref2 = customizations === null || customizations === void 0 ? void 0 : customizations.messages) !== null && _ref2 !== void 0 ? _ref2 : [],
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/chatbot/chatbot.tsx"],"sourcesContent":["import { Flex } from '@servicetitan/anvil2';\nimport { useDependencies } from '@servicetitan/react-ioc';\nimport { Chat } from '@servicetitan/titan-chat-ui-anvil2';\nimport {\n ChatMessageModelText,\n ChatMessageModelTimeout,\n ChatMessageModelWelcome,\n ChatParticipantIcon,\n} from '@servicetitan/titan-chat-ui-common';\nimport {\n CHATBOT_UI_BACKEND_STORE_TOKEN,\n CHATBOT_UI_STORE_TOKEN,\n ChatbotCustomizations,\n} from '@servicetitan/titan-chatbot-api';\nimport { observer } from 'mobx-react';\nimport { CSSProperties, FC, useEffect, useMemo } from 'react';\nimport { ChatbotToChatProviderAdapter } from './chatbot-to-chat-provider-adapter';\nimport * as Styles from './chatbot.module.less';\nimport { ChatFilters } from './filters/chatbot-filters';\nimport { ChatbotMessageAnswer } from './messages/chatbot-message-answer';\nimport { ChatbotMessageQuestion } from './messages/chatbot-message-question';\nimport { ChatbotMessageTimeout } from './messages/chatbot-message-timeout';\nimport { ChatbotMessageTyping } from './messages/chatbot-message-typing';\nimport { ChatbotMessageWelcome } from './messages/chatbot-message-welcome';\nimport { ChatbotMessageTemplateAgent } from './templates/chatbot-message-template-agent';\n\nexport interface IChatbotProps {\n className?: string;\n style?: CSSProperties;\n customizations?: ChatbotCustomizations;\n botIcon?: ChatParticipantIcon;\n botName?: string;\n onReady?: () => void;\n}\n\nexport const Chatbot: FC<IChatbotProps> = observer(\n ({ botIcon, botName, className, customizations, onReady, style }) => {\n const [chatUiStore, chatUiBackendStore] = useDependencies(\n CHATBOT_UI_STORE_TOKEN,\n CHATBOT_UI_BACKEND_STORE_TOKEN\n );\n useEffect(() => {\n const init = async () => {\n chatUiBackendStore.subscribe();\n await chatUiStore.run({\n agentName: botName ?? 'Titan',\n agentIcon: botIcon ?? ChatParticipantIcon.Bot,\n });\n onReady?.();\n };\n init().then(() => {});\n return () => chatUiBackendStore.unsubscribe();\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n const customizationsMerged = useMemo<ChatbotCustomizations>(() => {\n const templateCustomizations = customizations?.messageTemplates ?? {};\n templateCustomizations.agent = templateCustomizations.agent ?? {\n predicate: message => message.participant.isAgent,\n component: ChatbotMessageTemplateAgent,\n };\n return {\n loadingComponent: customizations?.loadingComponent,\n input: customizations?.input,\n error: customizations?.error,\n filters: customizations?.filters,\n feedback: customizations?.feedback,\n footerComponent: customizations?.footerComponent,\n messageTyping: customizations?.messageTyping ?? {\n component: ChatbotMessageTyping,\n },\n messages: [\n ...(customizations?.messages ?? []),\n {\n predicate: (message: ChatMessageModelText) =>\n !message.participant.isAgent && message.type === 'message',\n component: ChatbotMessageQuestion,\n },\n {\n predicate: (message: ChatMessageModelWelcome) => message.type === 'welcome',\n component: ChatbotMessageWelcome,\n },\n {\n predicate: (message: ChatMessageModelText) =>\n message.participant.isAgent && message.type === 'message',\n component: ChatbotMessageAnswer,\n },\n {\n predicate: (message: ChatMessageModelTimeout) => message.type === 'timeout',\n component: ChatbotMessageTimeout,\n isSystem: true,\n },\n ],\n messageTemplates: templateCustomizations,\n };\n }, [customizations]);\n\n const isFilterEnabled = Boolean(customizationsMerged.filters?.enabled);\n const isTopFilter = Boolean(\n isFilterEnabled && customizationsMerged.filters?.position === 'top'\n );\n const isBottomFilter = isFilterEnabled && !isTopFilter;\n return (\n <ChatbotToChatProviderAdapter>\n <Flex direction=\"column\" className={className} style={style}>\n {isTopFilter && <ChatFilters className=\"p-inline-6 p-block-4\" />}\n <Chat className={Styles.chat} customizations={customizationsMerged} />\n {isBottomFilter && <ChatFilters className=\"p-inline-6 p-block-end-4\" />}\n </Flex>\n </ChatbotToChatProviderAdapter>\n );\n }\n);\n"],"names":["Flex","useDependencies","Chat","ChatParticipantIcon","CHATBOT_UI_BACKEND_STORE_TOKEN","CHATBOT_UI_STORE_TOKEN","observer","useEffect","useMemo","ChatbotToChatProviderAdapter","Styles","ChatFilters","ChatbotMessageAnswer","ChatbotMessageQuestion","ChatbotMessageTimeout","ChatbotMessageTyping","ChatbotMessageWelcome","ChatbotMessageTemplateAgent","Chatbot","botIcon","botName","className","customizations","onReady","style","customizationsMerged","chatUiStore","chatUiBackendStore","init","subscribe","run","agentName","agentIcon","Bot","then","unsubscribe","templateCustomizations","messageTemplates","agent","predicate","message","participant","isAgent","component","loadingComponent","input","error","filters","feedback","footerComponent","messageTyping","messages","type","isSystem","isFilterEnabled","Boolean","enabled","isTopFilter","position","isBottomFilter","direction","chat"],"mappings":";AAAA,SAASA,IAAI,QAAQ,uBAAuB;AAC5C,SAASC,eAAe,QAAQ,0BAA0B;AAC1D,SAASC,IAAI,QAAQ,qCAAqC;AAC1D,SAIIC,mBAAmB,QAChB,qCAAqC;AAC5C,SACIC,8BAA8B,EAC9BC,sBAAsB,
|
|
1
|
+
{"version":3,"sources":["../../../src/components/chatbot/chatbot.tsx"],"sourcesContent":["import { Flex } from '@servicetitan/anvil2';\nimport { useDependencies } from '@servicetitan/react-ioc';\nimport { Chat } from '@servicetitan/titan-chat-ui-anvil2';\nimport {\n ChatMessageModelText,\n ChatMessageModelTimeout,\n ChatMessageModelWelcome,\n ChatParticipantIcon,\n} from '@servicetitan/titan-chat-ui-common';\nimport {\n CHATBOT_UI_BACKEND_STORE_TOKEN,\n CHATBOT_UI_STORE_TOKEN,\n ChatbotCustomizations,\n isChatbotStreamingEnabled,\n} from '@servicetitan/titan-chatbot-api';\nimport { observer } from 'mobx-react';\nimport { CSSProperties, FC, useEffect, useMemo } from 'react';\nimport { ChatbotToChatProviderAdapter } from './chatbot-to-chat-provider-adapter';\nimport * as Styles from './chatbot.module.less';\nimport { ChatFilters } from './filters/chatbot-filters';\nimport { ChatbotMessageAnswer } from './messages/chatbot-message-answer';\nimport { ChatbotMessageQuestion } from './messages/chatbot-message-question';\nimport { ChatbotMessageTimeout } from './messages/chatbot-message-timeout';\nimport { ChatbotMessageTyping } from './messages/chatbot-message-typing';\nimport { ChatbotMessageWelcome } from './messages/chatbot-message-welcome';\nimport { ChatbotStreamingProgress } from './messages/chatbot-streaming-progress';\nimport { ChatbotMessageTemplateAgent } from './templates/chatbot-message-template-agent';\n\nexport interface IChatbotProps {\n className?: string;\n style?: CSSProperties;\n customizations?: ChatbotCustomizations;\n botIcon?: ChatParticipantIcon;\n botName?: string;\n onReady?: () => void;\n}\n\nexport const Chatbot: FC<IChatbotProps> = observer(\n ({ botIcon, botName, className, customizations, onReady, style }) => {\n const [chatUiStore, chatUiBackendStore] = useDependencies(\n CHATBOT_UI_STORE_TOKEN,\n CHATBOT_UI_BACKEND_STORE_TOKEN\n );\n useEffect(() => {\n const init = async () => {\n chatUiBackendStore.subscribe();\n await chatUiStore.run({\n agentName: botName ?? 'Titan',\n agentIcon: botIcon ?? ChatParticipantIcon.Bot,\n });\n onReady?.();\n };\n init().then(() => {});\n return () => chatUiBackendStore.unsubscribe();\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n const customizationsMerged = useMemo<ChatbotCustomizations>(() => {\n const templateCustomizations = customizations?.messageTemplates ?? {};\n templateCustomizations.agent = templateCustomizations.agent ?? {\n predicate: message => message.participant.isAgent,\n component: ChatbotMessageTemplateAgent,\n };\n return {\n loadingComponent: customizations?.loadingComponent,\n input: customizations?.input,\n error: customizations?.error,\n filters: customizations?.filters,\n feedback: customizations?.feedback,\n streaming: customizations?.streaming,\n timeouts: customizations?.timeouts,\n footerComponent: customizations?.footerComponent,\n messageTyping: customizations?.messageTyping ?? {\n component: isChatbotStreamingEnabled(customizations)\n ? ChatbotStreamingProgress\n : ChatbotMessageTyping,\n },\n messages: [\n ...(customizations?.messages ?? []),\n {\n predicate: (message: ChatMessageModelText) =>\n !message.participant.isAgent && message.type === 'message',\n component: ChatbotMessageQuestion,\n },\n {\n predicate: (message: ChatMessageModelWelcome) => message.type === 'welcome',\n component: ChatbotMessageWelcome,\n },\n {\n predicate: (message: ChatMessageModelText) =>\n message.participant.isAgent && message.type === 'message',\n component: ChatbotMessageAnswer,\n },\n {\n predicate: (message: ChatMessageModelTimeout) => message.type === 'timeout',\n component: ChatbotMessageTimeout,\n isSystem: true,\n },\n ],\n messageTemplates: templateCustomizations,\n };\n }, [customizations]);\n\n const isFilterEnabled = Boolean(customizationsMerged.filters?.enabled);\n const isTopFilter = Boolean(\n isFilterEnabled && customizationsMerged.filters?.position === 'top'\n );\n const isBottomFilter = isFilterEnabled && !isTopFilter;\n return (\n <ChatbotToChatProviderAdapter>\n <Flex direction=\"column\" className={className} style={style}>\n {isTopFilter && <ChatFilters className=\"p-inline-6 p-block-4\" />}\n <Chat className={Styles.chat} customizations={customizationsMerged} />\n {isBottomFilter && <ChatFilters className=\"p-inline-6 p-block-end-4\" />}\n </Flex>\n </ChatbotToChatProviderAdapter>\n );\n }\n);\n"],"names":["Flex","useDependencies","Chat","ChatParticipantIcon","CHATBOT_UI_BACKEND_STORE_TOKEN","CHATBOT_UI_STORE_TOKEN","isChatbotStreamingEnabled","observer","useEffect","useMemo","ChatbotToChatProviderAdapter","Styles","ChatFilters","ChatbotMessageAnswer","ChatbotMessageQuestion","ChatbotMessageTimeout","ChatbotMessageTyping","ChatbotMessageWelcome","ChatbotStreamingProgress","ChatbotMessageTemplateAgent","Chatbot","botIcon","botName","className","customizations","onReady","style","customizationsMerged","chatUiStore","chatUiBackendStore","init","subscribe","run","agentName","agentIcon","Bot","then","unsubscribe","templateCustomizations","messageTemplates","agent","predicate","message","participant","isAgent","component","loadingComponent","input","error","filters","feedback","streaming","timeouts","footerComponent","messageTyping","messages","type","isSystem","isFilterEnabled","Boolean","enabled","isTopFilter","position","isBottomFilter","direction","chat"],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { IMessageTypingProps } from '@servicetitan/titan-chat-ui-anvil2';
|
|
2
|
+
import { FC } from 'react';
|
|
3
|
+
/**
|
|
4
|
+
* Live agent-progress indicator shown while a streamed run is in flight. Renders, top to bottom:
|
|
5
|
+
* the proposed plan as a checklist (done = check, active = spinner, pending = hollow circle), the
|
|
6
|
+
* accumulated activity log (completed lines, muted), the current status line (active, with a
|
|
7
|
+
* spinner), and a transient "Still working on itβ¦" keepalive. Before any event arrives it falls
|
|
8
|
+
* back to the dots typing indicator so there is no silent waiting period. Replaces the typing
|
|
9
|
+
* component when streaming.
|
|
10
|
+
*/
|
|
11
|
+
export declare const ChatbotStreamingProgress: FC<IMessageTypingProps>;
|
|
12
|
+
//# sourceMappingURL=chatbot-streaming-progress.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chatbot-streaming-progress.d.ts","sourceRoot":"","sources":["../../../../src/components/chatbot/messages/chatbot-streaming-progress.tsx"],"names":[],"mappings":"AAIA,OAAO,EACH,mBAAmB,EAGtB,MAAM,oCAAoC,CAAC;AAI5C,OAAO,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAiB3B;;;;;;;GAOG;AACH,eAAO,MAAM,wBAAwB,EAAE,EAAE,CAAC,mBAAmB,CAwE3D,CAAC"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Flex, Icon, Spinner, Text } from '@servicetitan/anvil2';
|
|
3
|
+
import IconCheckCircle from '@servicetitan/anvil2/assets/icons/material/round/check_circle.svg';
|
|
4
|
+
import IconRadioUnchecked from '@servicetitan/anvil2/assets/icons/material/round/radio_button_unchecked.svg';
|
|
5
|
+
import { useDependencies } from '@servicetitan/react-ioc';
|
|
6
|
+
import { MessageAgent, MessageTyping } from '@servicetitan/titan-chat-ui-anvil2';
|
|
7
|
+
import { CHATBOT_UI_BACKEND_STORE_TOKEN } from '@servicetitan/titan-chatbot-api';
|
|
8
|
+
import { observer } from 'mobx-react';
|
|
9
|
+
import * as Styles from './chatbot-streaming-progress.module.less';
|
|
10
|
+
const mutedStyle = {
|
|
11
|
+
opacity: 0.6
|
|
12
|
+
};
|
|
13
|
+
const keepaliveStyle = {
|
|
14
|
+
opacity: 0.6,
|
|
15
|
+
fontStyle: 'italic'
|
|
16
|
+
};
|
|
17
|
+
/** Status marker for a single plan step: check when done, spinner when active, hollow circle when pending. */ const StepMarker = ({ status })=>{
|
|
18
|
+
if (status === 'active') {
|
|
19
|
+
return /*#__PURE__*/ _jsx(Spinner, {
|
|
20
|
+
size: "small",
|
|
21
|
+
inherit: true
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
if (status === 'done') {
|
|
25
|
+
return /*#__PURE__*/ _jsx(Icon, {
|
|
26
|
+
svg: IconCheckCircle,
|
|
27
|
+
className: "c-status-success"
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
return /*#__PURE__*/ _jsx(Icon, {
|
|
31
|
+
svg: IconRadioUnchecked,
|
|
32
|
+
style: mutedStyle
|
|
33
|
+
});
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Live agent-progress indicator shown while a streamed run is in flight. Renders, top to bottom:
|
|
37
|
+
* the proposed plan as a checklist (done = check, active = spinner, pending = hollow circle), the
|
|
38
|
+
* accumulated activity log (completed lines, muted), the current status line (active, with a
|
|
39
|
+
* spinner), and a transient "Still working on itβ¦" keepalive. Before any event arrives it falls
|
|
40
|
+
* back to the dots typing indicator so there is no silent waiting period. Replaces the typing
|
|
41
|
+
* component when streaming.
|
|
42
|
+
*/ export const ChatbotStreamingProgress = observer(({ avatar })=>{
|
|
43
|
+
const [backendStore] = useDependencies(CHATBOT_UI_BACKEND_STORE_TOKEN);
|
|
44
|
+
const { keepaliveText, logLines, statusText, steps } = backendStore.streamingProgress;
|
|
45
|
+
const hasProgress = steps.length > 0 || logLines.length > 0 || Boolean(statusText) || Boolean(keepaliveText);
|
|
46
|
+
if (!hasProgress) {
|
|
47
|
+
// No event yet β show the animated typing indicator (immediate, non-silent feedback).
|
|
48
|
+
return /*#__PURE__*/ _jsx(MessageTyping, {
|
|
49
|
+
avatar: avatar
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
return /*#__PURE__*/ _jsx(MessageAgent, {
|
|
53
|
+
avatar: avatar,
|
|
54
|
+
subtle: true,
|
|
55
|
+
children: /*#__PURE__*/ _jsxs(Flex, {
|
|
56
|
+
direction: "column",
|
|
57
|
+
gap: 1,
|
|
58
|
+
className: Styles.container,
|
|
59
|
+
"data-cy": "titan-chatbot-streaming-progress",
|
|
60
|
+
children: [
|
|
61
|
+
steps.length > 0 && /*#__PURE__*/ _jsx(Flex, {
|
|
62
|
+
direction: "column",
|
|
63
|
+
gap: 1,
|
|
64
|
+
"data-cy": "titan-chatbot-streaming-steps",
|
|
65
|
+
children: steps.map((step)=>/*#__PURE__*/ _jsxs(Flex, {
|
|
66
|
+
gap: 1,
|
|
67
|
+
alignItems: "center",
|
|
68
|
+
"data-cy": "titan-chatbot-streaming-step",
|
|
69
|
+
"data-status": step.status,
|
|
70
|
+
children: [
|
|
71
|
+
/*#__PURE__*/ _jsx(StepMarker, {
|
|
72
|
+
status: step.status
|
|
73
|
+
}),
|
|
74
|
+
/*#__PURE__*/ _jsx(Text, {
|
|
75
|
+
subdued: step.status !== 'active',
|
|
76
|
+
style: step.status === 'pending' ? mutedStyle : undefined,
|
|
77
|
+
children: step.title
|
|
78
|
+
})
|
|
79
|
+
]
|
|
80
|
+
}, step.id))
|
|
81
|
+
}),
|
|
82
|
+
logLines.length > 0 && /*#__PURE__*/ _jsx(Flex, {
|
|
83
|
+
direction: "column",
|
|
84
|
+
"data-cy": "titan-chatbot-streaming-log",
|
|
85
|
+
children: logLines.map((line, index)=>/*#__PURE__*/ _jsx(Text, {
|
|
86
|
+
style: mutedStyle,
|
|
87
|
+
"data-cy": "titan-chatbot-streaming-log-line",
|
|
88
|
+
children: line
|
|
89
|
+
}, index))
|
|
90
|
+
}),
|
|
91
|
+
statusText && /*#__PURE__*/ _jsxs(Flex, {
|
|
92
|
+
gap: 1,
|
|
93
|
+
alignItems: "center",
|
|
94
|
+
"data-cy": "titan-chatbot-streaming-status",
|
|
95
|
+
children: [
|
|
96
|
+
/*#__PURE__*/ _jsx(Spinner, {
|
|
97
|
+
size: "small",
|
|
98
|
+
inherit: true
|
|
99
|
+
}),
|
|
100
|
+
/*#__PURE__*/ _jsx(Text, {
|
|
101
|
+
subdued: true,
|
|
102
|
+
children: statusText
|
|
103
|
+
})
|
|
104
|
+
]
|
|
105
|
+
}),
|
|
106
|
+
keepaliveText && /*#__PURE__*/ _jsx(Text, {
|
|
107
|
+
style: keepaliveStyle,
|
|
108
|
+
"data-cy": "titan-chatbot-streaming-keepalive",
|
|
109
|
+
children: keepaliveText
|
|
110
|
+
})
|
|
111
|
+
]
|
|
112
|
+
})
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
//# sourceMappingURL=chatbot-streaming-progress.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/components/chatbot/messages/chatbot-streaming-progress.tsx"],"sourcesContent":["import { Flex, Icon, Spinner, Text } from '@servicetitan/anvil2';\nimport IconCheckCircle from '@servicetitan/anvil2/assets/icons/material/round/check_circle.svg';\nimport IconRadioUnchecked from '@servicetitan/anvil2/assets/icons/material/round/radio_button_unchecked.svg';\nimport { useDependencies } from '@servicetitan/react-ioc';\nimport {\n IMessageTypingProps,\n MessageAgent,\n MessageTyping,\n} from '@servicetitan/titan-chat-ui-anvil2';\nimport { StreamingStep } from '@servicetitan/titan-chat-ui-common';\nimport { CHATBOT_UI_BACKEND_STORE_TOKEN } from '@servicetitan/titan-chatbot-api';\nimport { observer } from 'mobx-react';\nimport { FC } from 'react';\nimport * as Styles from './chatbot-streaming-progress.module.less';\n\nconst mutedStyle = { opacity: 0.6 } as const;\nconst keepaliveStyle = { opacity: 0.6, fontStyle: 'italic' } as const;\n\n/** Status marker for a single plan step: check when done, spinner when active, hollow circle when pending. */\nconst StepMarker: FC<{ status: StreamingStep['status'] }> = ({ status }) => {\n if (status === 'active') {\n return <Spinner size=\"small\" inherit />;\n }\n if (status === 'done') {\n return <Icon svg={IconCheckCircle} className=\"c-status-success\" />;\n }\n return <Icon svg={IconRadioUnchecked} style={mutedStyle} />;\n};\n\n/**\n * Live agent-progress indicator shown while a streamed run is in flight. Renders, top to bottom:\n * the proposed plan as a checklist (done = check, active = spinner, pending = hollow circle), the\n * accumulated activity log (completed lines, muted), the current status line (active, with a\n * spinner), and a transient \"Still working on itβ¦\" keepalive. Before any event arrives it falls\n * back to the dots typing indicator so there is no silent waiting period. Replaces the typing\n * component when streaming.\n */\nexport const ChatbotStreamingProgress: FC<IMessageTypingProps> = observer(({ avatar }) => {\n const [backendStore] = useDependencies(CHATBOT_UI_BACKEND_STORE_TOKEN);\n const { keepaliveText, logLines, statusText, steps } = backendStore.streamingProgress;\n\n const hasProgress =\n steps.length > 0 || logLines.length > 0 || Boolean(statusText) || Boolean(keepaliveText);\n if (!hasProgress) {\n // No event yet β show the animated typing indicator (immediate, non-silent feedback).\n return <MessageTyping avatar={avatar} />;\n }\n\n return (\n <MessageAgent avatar={avatar} subtle>\n <Flex\n direction=\"column\"\n gap={1}\n className={Styles.container}\n data-cy=\"titan-chatbot-streaming-progress\"\n >\n {steps.length > 0 && (\n <Flex direction=\"column\" gap={1} data-cy=\"titan-chatbot-streaming-steps\">\n {steps.map(step => (\n <Flex\n key={step.id}\n gap={1}\n alignItems=\"center\"\n data-cy=\"titan-chatbot-streaming-step\"\n data-status={step.status}\n >\n <StepMarker status={step.status} />\n <Text\n subdued={step.status !== 'active'}\n style={step.status === 'pending' ? mutedStyle : undefined}\n >\n {step.title}\n </Text>\n </Flex>\n ))}\n </Flex>\n )}\n\n {logLines.length > 0 && (\n <Flex direction=\"column\" data-cy=\"titan-chatbot-streaming-log\">\n {logLines.map((line, index) => (\n <Text\n // Activity log is append-only and never reordered, so the index is a stable key.\n // eslint-disable-next-line react/no-array-index-key\n key={index}\n style={mutedStyle}\n data-cy=\"titan-chatbot-streaming-log-line\"\n >\n {line}\n </Text>\n ))}\n </Flex>\n )}\n\n {statusText && (\n <Flex gap={1} alignItems=\"center\" data-cy=\"titan-chatbot-streaming-status\">\n <Spinner size=\"small\" inherit />\n <Text subdued>{statusText}</Text>\n </Flex>\n )}\n\n {keepaliveText && (\n <Text style={keepaliveStyle} data-cy=\"titan-chatbot-streaming-keepalive\">\n {keepaliveText}\n </Text>\n )}\n </Flex>\n </MessageAgent>\n );\n});\n"],"names":["Flex","Icon","Spinner","Text","IconCheckCircle","IconRadioUnchecked","useDependencies","MessageAgent","MessageTyping","CHATBOT_UI_BACKEND_STORE_TOKEN","observer","Styles","mutedStyle","opacity","keepaliveStyle","fontStyle","StepMarker","status","size","inherit","svg","className","style","ChatbotStreamingProgress","avatar","backendStore","keepaliveText","logLines","statusText","steps","streamingProgress","hasProgress","length","Boolean","subtle","direction","gap","container","data-cy","map","step","alignItems","data-status","subdued","undefined","title","id","line","index"],"mappings":";AAAA,SAASA,IAAI,EAAEC,IAAI,EAAEC,OAAO,EAAEC,IAAI,QAAQ,uBAAuB;AACjE,OAAOC,qBAAqB,oEAAoE;AAChG,OAAOC,wBAAwB,8EAA8E;AAC7G,SAASC,eAAe,QAAQ,0BAA0B;AAC1D,SAEIC,YAAY,EACZC,aAAa,QACV,qCAAqC;AAE5C,SAASC,8BAA8B,QAAQ,kCAAkC;AACjF,SAASC,QAAQ,QAAQ,aAAa;AAEtC,YAAYC,YAAY,2CAA2C;AAEnE,MAAMC,aAAa;IAAEC,SAAS;AAAI;AAClC,MAAMC,iBAAiB;IAAED,SAAS;IAAKE,WAAW;AAAS;AAE3D,4GAA4G,GAC5G,MAAMC,aAAsD,CAAC,EAAEC,MAAM,EAAE;IACnE,IAAIA,WAAW,UAAU;QACrB,qBAAO,KAACf;YAAQgB,MAAK;YAAQC,OAAO;;IACxC;IACA,IAAIF,WAAW,QAAQ;QACnB,qBAAO,KAAChB;YAAKmB,KAAKhB;YAAiBiB,WAAU;;IACjD;IACA,qBAAO,KAACpB;QAAKmB,KAAKf;QAAoBiB,OAAOV;;AACjD;AAEA;;;;;;;CAOC,GACD,OAAO,MAAMW,2BAAoDb,SAAS,CAAC,EAAEc,MAAM,EAAE;IACjF,MAAM,CAACC,aAAa,GAAGnB,gBAAgBG;IACvC,MAAM,EAAEiB,aAAa,EAAEC,QAAQ,EAAEC,UAAU,EAAEC,KAAK,EAAE,GAAGJ,aAAaK,iBAAiB;IAErF,MAAMC,cACFF,MAAMG,MAAM,GAAG,KAAKL,SAASK,MAAM,GAAG,KAAKC,QAAQL,eAAeK,QAAQP;IAC9E,IAAI,CAACK,aAAa;QACd,sFAAsF;QACtF,qBAAO,KAACvB;YAAcgB,QAAQA;;IAClC;IAEA,qBACI,KAACjB;QAAaiB,QAAQA;QAAQU,MAAM;kBAChC,cAAA,MAAClC;YACGmC,WAAU;YACVC,KAAK;YACLf,WAAWV,OAAO0B,SAAS;YAC3BC,WAAQ;;gBAEPT,MAAMG,MAAM,GAAG,mBACZ,KAAChC;oBAAKmC,WAAU;oBAASC,KAAK;oBAAGE,WAAQ;8BACpCT,MAAMU,GAAG,CAACC,CAAAA,qBACP,MAACxC;4BAEGoC,KAAK;4BACLK,YAAW;4BACXH,WAAQ;4BACRI,eAAaF,KAAKvB,MAAM;;8CAExB,KAACD;oCAAWC,QAAQuB,KAAKvB,MAAM;;8CAC/B,KAACd;oCACGwC,SAASH,KAAKvB,MAAM,KAAK;oCACzBK,OAAOkB,KAAKvB,MAAM,KAAK,YAAYL,aAAagC;8CAE/CJ,KAAKK,KAAK;;;2BAXVL,KAAKM,EAAE;;gBAkB3BnB,SAASK,MAAM,GAAG,mBACf,KAAChC;oBAAKmC,WAAU;oBAASG,WAAQ;8BAC5BX,SAASY,GAAG,CAAC,CAACQ,MAAMC,sBACjB,KAAC7C;4BAIGmB,OAAOV;4BACP0B,WAAQ;sCAEPS;2BAJIC;;gBAUpBpB,4BACG,MAAC5B;oBAAKoC,KAAK;oBAAGK,YAAW;oBAASH,WAAQ;;sCACtC,KAACpC;4BAAQgB,MAAK;4BAAQC,OAAO;;sCAC7B,KAAChB;4BAAKwC,OAAO;sCAAEf;;;;gBAItBF,+BACG,KAACvB;oBAAKmB,OAAOR;oBAAgBwB,WAAQ;8BAChCZ;;;;;AAMzB,GAAG"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@servicetitan/titan-chatbot-ui-anvil2",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "9.0.0",
|
|
4
4
|
"description": "Chatbot experience UI package (Anvil2 version)",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -17,9 +17,9 @@
|
|
|
17
17
|
"push:local": "yalc push"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@servicetitan/titan-chat-ui-anvil2": "^
|
|
21
|
-
"@servicetitan/titan-chat-ui-common": "^
|
|
22
|
-
"@servicetitan/titan-chatbot-api": "^
|
|
20
|
+
"@servicetitan/titan-chat-ui-anvil2": "^9.0.0",
|
|
21
|
+
"@servicetitan/titan-chat-ui-common": "^9.0.0",
|
|
22
|
+
"@servicetitan/titan-chatbot-api": "^9.0.0",
|
|
23
23
|
"nanoid": "^5.1.5"
|
|
24
24
|
},
|
|
25
25
|
"peerDependencies": {
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"react-dom": ">=18"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
|
-
"@servicetitan/cypress-shared": "^
|
|
39
|
+
"@servicetitan/cypress-shared": "^9.0.0",
|
|
40
40
|
"cypress": "^15.12.0"
|
|
41
41
|
},
|
|
42
42
|
"keywords": [
|
|
@@ -49,5 +49,5 @@
|
|
|
49
49
|
"cli": {
|
|
50
50
|
"webpack": false
|
|
51
51
|
},
|
|
52
|
-
"gitHead": "
|
|
52
|
+
"gitHead": "028073fe571014741da70bcff173d617e2738125"
|
|
53
53
|
}
|
|
@@ -2,6 +2,6 @@ import { AnvilProvider } from '@servicetitan/anvil2';
|
|
|
2
2
|
import { runChatbotLiveSharedTests } from '@servicetitan/cypress-shared';
|
|
3
3
|
import { Chatbot } from '../chatbot';
|
|
4
4
|
|
|
5
|
-
describe('[Chatbot]', () => {
|
|
5
|
+
describe.skip('[Chatbot]', () => {
|
|
6
6
|
runChatbotLiveSharedTests(Chatbot, component => <AnvilProvider>{component}</AnvilProvider>);
|
|
7
7
|
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { AnvilProvider } from '@servicetitan/anvil2';
|
|
2
|
+
import { runChatbotStreamingSharedTests } from '@servicetitan/cypress-shared';
|
|
3
|
+
import { Chatbot } from '../chatbot';
|
|
4
|
+
|
|
5
|
+
describe('[Chatbot streaming]', () => {
|
|
6
|
+
runChatbotStreamingSharedTests(Chatbot, component => (
|
|
7
|
+
<AnvilProvider>{component}</AnvilProvider>
|
|
8
|
+
));
|
|
9
|
+
});
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
CHATBOT_UI_BACKEND_STORE_TOKEN,
|
|
12
12
|
CHATBOT_UI_STORE_TOKEN,
|
|
13
13
|
ChatbotCustomizations,
|
|
14
|
+
isChatbotStreamingEnabled,
|
|
14
15
|
} from '@servicetitan/titan-chatbot-api';
|
|
15
16
|
import { observer } from 'mobx-react';
|
|
16
17
|
import { CSSProperties, FC, useEffect, useMemo } from 'react';
|
|
@@ -22,6 +23,7 @@ import { ChatbotMessageQuestion } from './messages/chatbot-message-question';
|
|
|
22
23
|
import { ChatbotMessageTimeout } from './messages/chatbot-message-timeout';
|
|
23
24
|
import { ChatbotMessageTyping } from './messages/chatbot-message-typing';
|
|
24
25
|
import { ChatbotMessageWelcome } from './messages/chatbot-message-welcome';
|
|
26
|
+
import { ChatbotStreamingProgress } from './messages/chatbot-streaming-progress';
|
|
25
27
|
import { ChatbotMessageTemplateAgent } from './templates/chatbot-message-template-agent';
|
|
26
28
|
|
|
27
29
|
export interface IChatbotProps {
|
|
@@ -65,9 +67,13 @@ export const Chatbot: FC<IChatbotProps> = observer(
|
|
|
65
67
|
error: customizations?.error,
|
|
66
68
|
filters: customizations?.filters,
|
|
67
69
|
feedback: customizations?.feedback,
|
|
70
|
+
streaming: customizations?.streaming,
|
|
71
|
+
timeouts: customizations?.timeouts,
|
|
68
72
|
footerComponent: customizations?.footerComponent,
|
|
69
73
|
messageTyping: customizations?.messageTyping ?? {
|
|
70
|
-
component:
|
|
74
|
+
component: isChatbotStreamingEnabled(customizations)
|
|
75
|
+
? ChatbotStreamingProgress
|
|
76
|
+
: ChatbotMessageTyping,
|
|
71
77
|
},
|
|
72
78
|
messages: [
|
|
73
79
|
...(customizations?.messages ?? []),
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { Flex, Icon, Spinner, Text } from '@servicetitan/anvil2';
|
|
2
|
+
import IconCheckCircle from '@servicetitan/anvil2/assets/icons/material/round/check_circle.svg';
|
|
3
|
+
import IconRadioUnchecked from '@servicetitan/anvil2/assets/icons/material/round/radio_button_unchecked.svg';
|
|
4
|
+
import { useDependencies } from '@servicetitan/react-ioc';
|
|
5
|
+
import {
|
|
6
|
+
IMessageTypingProps,
|
|
7
|
+
MessageAgent,
|
|
8
|
+
MessageTyping,
|
|
9
|
+
} from '@servicetitan/titan-chat-ui-anvil2';
|
|
10
|
+
import { StreamingStep } from '@servicetitan/titan-chat-ui-common';
|
|
11
|
+
import { CHATBOT_UI_BACKEND_STORE_TOKEN } from '@servicetitan/titan-chatbot-api';
|
|
12
|
+
import { observer } from 'mobx-react';
|
|
13
|
+
import { FC } from 'react';
|
|
14
|
+
import * as Styles from './chatbot-streaming-progress.module.less';
|
|
15
|
+
|
|
16
|
+
const mutedStyle = { opacity: 0.6 } as const;
|
|
17
|
+
const keepaliveStyle = { opacity: 0.6, fontStyle: 'italic' } as const;
|
|
18
|
+
|
|
19
|
+
/** Status marker for a single plan step: check when done, spinner when active, hollow circle when pending. */
|
|
20
|
+
const StepMarker: FC<{ status: StreamingStep['status'] }> = ({ status }) => {
|
|
21
|
+
if (status === 'active') {
|
|
22
|
+
return <Spinner size="small" inherit />;
|
|
23
|
+
}
|
|
24
|
+
if (status === 'done') {
|
|
25
|
+
return <Icon svg={IconCheckCircle} className="c-status-success" />;
|
|
26
|
+
}
|
|
27
|
+
return <Icon svg={IconRadioUnchecked} style={mutedStyle} />;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Live agent-progress indicator shown while a streamed run is in flight. Renders, top to bottom:
|
|
32
|
+
* the proposed plan as a checklist (done = check, active = spinner, pending = hollow circle), the
|
|
33
|
+
* accumulated activity log (completed lines, muted), the current status line (active, with a
|
|
34
|
+
* spinner), and a transient "Still working on itβ¦" keepalive. Before any event arrives it falls
|
|
35
|
+
* back to the dots typing indicator so there is no silent waiting period. Replaces the typing
|
|
36
|
+
* component when streaming.
|
|
37
|
+
*/
|
|
38
|
+
export const ChatbotStreamingProgress: FC<IMessageTypingProps> = observer(({ avatar }) => {
|
|
39
|
+
const [backendStore] = useDependencies(CHATBOT_UI_BACKEND_STORE_TOKEN);
|
|
40
|
+
const { keepaliveText, logLines, statusText, steps } = backendStore.streamingProgress;
|
|
41
|
+
|
|
42
|
+
const hasProgress =
|
|
43
|
+
steps.length > 0 || logLines.length > 0 || Boolean(statusText) || Boolean(keepaliveText);
|
|
44
|
+
if (!hasProgress) {
|
|
45
|
+
// No event yet β show the animated typing indicator (immediate, non-silent feedback).
|
|
46
|
+
return <MessageTyping avatar={avatar} />;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<MessageAgent avatar={avatar} subtle>
|
|
51
|
+
<Flex
|
|
52
|
+
direction="column"
|
|
53
|
+
gap={1}
|
|
54
|
+
className={Styles.container}
|
|
55
|
+
data-cy="titan-chatbot-streaming-progress"
|
|
56
|
+
>
|
|
57
|
+
{steps.length > 0 && (
|
|
58
|
+
<Flex direction="column" gap={1} data-cy="titan-chatbot-streaming-steps">
|
|
59
|
+
{steps.map(step => (
|
|
60
|
+
<Flex
|
|
61
|
+
key={step.id}
|
|
62
|
+
gap={1}
|
|
63
|
+
alignItems="center"
|
|
64
|
+
data-cy="titan-chatbot-streaming-step"
|
|
65
|
+
data-status={step.status}
|
|
66
|
+
>
|
|
67
|
+
<StepMarker status={step.status} />
|
|
68
|
+
<Text
|
|
69
|
+
subdued={step.status !== 'active'}
|
|
70
|
+
style={step.status === 'pending' ? mutedStyle : undefined}
|
|
71
|
+
>
|
|
72
|
+
{step.title}
|
|
73
|
+
</Text>
|
|
74
|
+
</Flex>
|
|
75
|
+
))}
|
|
76
|
+
</Flex>
|
|
77
|
+
)}
|
|
78
|
+
|
|
79
|
+
{logLines.length > 0 && (
|
|
80
|
+
<Flex direction="column" data-cy="titan-chatbot-streaming-log">
|
|
81
|
+
{logLines.map((line, index) => (
|
|
82
|
+
<Text
|
|
83
|
+
// Activity log is append-only and never reordered, so the index is a stable key.
|
|
84
|
+
// eslint-disable-next-line react/no-array-index-key
|
|
85
|
+
key={index}
|
|
86
|
+
style={mutedStyle}
|
|
87
|
+
data-cy="titan-chatbot-streaming-log-line"
|
|
88
|
+
>
|
|
89
|
+
{line}
|
|
90
|
+
</Text>
|
|
91
|
+
))}
|
|
92
|
+
</Flex>
|
|
93
|
+
)}
|
|
94
|
+
|
|
95
|
+
{statusText && (
|
|
96
|
+
<Flex gap={1} alignItems="center" data-cy="titan-chatbot-streaming-status">
|
|
97
|
+
<Spinner size="small" inherit />
|
|
98
|
+
<Text subdued>{statusText}</Text>
|
|
99
|
+
</Flex>
|
|
100
|
+
)}
|
|
101
|
+
|
|
102
|
+
{keepaliveText && (
|
|
103
|
+
<Text style={keepaliveStyle} data-cy="titan-chatbot-streaming-keepalive">
|
|
104
|
+
{keepaliveText}
|
|
105
|
+
</Text>
|
|
106
|
+
)}
|
|
107
|
+
</Flex>
|
|
108
|
+
</MessageAgent>
|
|
109
|
+
);
|
|
110
|
+
});
|