@genesislcap/ai-assistant 14.403.0-ai-assistant.1
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/api-extractor.json +4 -0
- package/dist/ai-assistant.api.json +4028 -0
- package/dist/ai-assistant.d.ts +396 -0
- package/dist/dts/channel/ai-activity-channel.d.ts +32 -0
- package/dist/dts/channel/ai-activity-channel.d.ts.map +1 -0
- package/dist/dts/components/activity-halo/activity-halo.d.ts +31 -0
- package/dist/dts/components/activity-halo/activity-halo.d.ts.map +1 -0
- package/dist/dts/components/chat-bubble/chat-bubble.d.ts +52 -0
- package/dist/dts/components/chat-bubble/chat-bubble.d.ts.map +1 -0
- package/dist/dts/components/chat-bubble/chat-bubble.styles.d.ts +2 -0
- package/dist/dts/components/chat-bubble/chat-bubble.styles.d.ts.map +1 -0
- package/dist/dts/components/chat-bubble/chat-bubble.template.d.ts +4 -0
- package/dist/dts/components/chat-bubble/chat-bubble.template.d.ts.map +1 -0
- package/dist/dts/components/chat-bubble/index.d.ts +2 -0
- package/dist/dts/components/chat-bubble/index.d.ts.map +1 -0
- package/dist/dts/components/chat-driver/chat-driver.d.ts +49 -0
- package/dist/dts/components/chat-driver/chat-driver.d.ts.map +1 -0
- package/dist/dts/components/chat-driver/index.d.ts +2 -0
- package/dist/dts/components/chat-driver/index.d.ts.map +1 -0
- package/dist/dts/components/chat-interaction-wrapper/chat-interaction-wrapper.d.ts +19 -0
- package/dist/dts/components/chat-interaction-wrapper/chat-interaction-wrapper.d.ts.map +1 -0
- package/dist/dts/components/chat-interaction-wrapper/chat-interaction-wrapper.styles.d.ts +2 -0
- package/dist/dts/components/chat-interaction-wrapper/chat-interaction-wrapper.styles.d.ts.map +1 -0
- package/dist/dts/components/chat-interaction-wrapper/chat-interaction-wrapper.template.d.ts +3 -0
- package/dist/dts/components/chat-interaction-wrapper/chat-interaction-wrapper.template.d.ts.map +1 -0
- package/dist/dts/components/chat-interaction-wrapper/index.d.ts +4 -0
- package/dist/dts/components/chat-interaction-wrapper/index.d.ts.map +1 -0
- package/dist/dts/components/chat-markdown/chat-markdown.d.ts +6 -0
- package/dist/dts/components/chat-markdown/chat-markdown.d.ts.map +1 -0
- package/dist/dts/components/chat-markdown/index.d.ts +2 -0
- package/dist/dts/components/chat-markdown/index.d.ts.map +1 -0
- package/dist/dts/components/halo-overlay.d.ts +25 -0
- package/dist/dts/components/halo-overlay.d.ts.map +1 -0
- package/dist/dts/config/config.d.ts +51 -0
- package/dist/dts/config/config.d.ts.map +1 -0
- package/dist/dts/config/configure.d.ts +1 -0
- package/dist/dts/config/configure.d.ts.map +1 -0
- package/dist/dts/config/index.d.ts +2 -0
- package/dist/dts/config/index.d.ts.map +1 -0
- package/dist/dts/index.d.ts +10 -0
- package/dist/dts/index.d.ts.map +1 -0
- package/dist/dts/main/index.d.ts +5 -0
- package/dist/dts/main/index.d.ts.map +1 -0
- package/dist/dts/main/main.d.ts +103 -0
- package/dist/dts/main/main.d.ts.map +1 -0
- package/dist/dts/main/main.styles.d.ts +2 -0
- package/dist/dts/main/main.styles.d.ts.map +1 -0
- package/dist/dts/main/main.template.d.ts +4 -0
- package/dist/dts/main/main.template.d.ts.map +1 -0
- package/dist/dts/main/main.types.d.ts +54 -0
- package/dist/dts/main/main.types.d.ts.map +1 -0
- package/dist/dts/styles/index.d.ts +2 -0
- package/dist/dts/styles/index.d.ts.map +1 -0
- package/dist/dts/styles/styles.d.ts +5 -0
- package/dist/dts/styles/styles.d.ts.map +1 -0
- package/dist/dts/tags/index.d.ts +1 -0
- package/dist/dts/tags/index.d.ts.map +1 -0
- package/dist/dts/types/ai-chat-widget.d.ts +12 -0
- package/dist/dts/types/ai-chat-widget.d.ts.map +1 -0
- package/dist/dts/utils/index.d.ts +2 -0
- package/dist/dts/utils/index.d.ts.map +1 -0
- package/dist/dts/utils/logger.d.ts +2 -0
- package/dist/dts/utils/logger.d.ts.map +1 -0
- package/dist/esm/channel/ai-activity-channel.js +2 -0
- package/dist/esm/components/activity-halo/activity-halo.js +119 -0
- package/dist/esm/components/chat-bubble/chat-bubble.js +381 -0
- package/dist/esm/components/chat-bubble/chat-bubble.styles.js +193 -0
- package/dist/esm/components/chat-bubble/chat-bubble.template.js +50 -0
- package/dist/esm/components/chat-bubble/index.js +1 -0
- package/dist/esm/components/chat-driver/chat-driver.js +209 -0
- package/dist/esm/components/chat-driver/index.js +1 -0
- package/dist/esm/components/chat-interaction-wrapper/chat-interaction-wrapper.js +90 -0
- package/dist/esm/components/chat-interaction-wrapper/chat-interaction-wrapper.styles.js +6 -0
- package/dist/esm/components/chat-interaction-wrapper/chat-interaction-wrapper.template.js +4 -0
- package/dist/esm/components/chat-interaction-wrapper/index.js +3 -0
- package/dist/esm/components/chat-markdown/chat-markdown.js +95 -0
- package/dist/esm/components/chat-markdown/index.js +1 -0
- package/dist/esm/components/halo-overlay.js +118 -0
- package/dist/esm/config/config.js +16 -0
- package/dist/esm/config/configure.js +0 -0
- package/dist/esm/config/index.js +1 -0
- package/dist/esm/index.js +8 -0
- package/dist/esm/main/index.js +4 -0
- package/dist/esm/main/main.js +491 -0
- package/dist/esm/main/main.styles.js +356 -0
- package/dist/esm/main/main.template.js +272 -0
- package/dist/esm/main/main.types.js +22 -0
- package/dist/esm/styles/index.js +1 -0
- package/dist/esm/styles/styles.js +237 -0
- package/dist/esm/tags/index.js +0 -0
- package/dist/esm/types/ai-chat-widget.js +1 -0
- package/dist/esm/utils/index.js +1 -0
- package/dist/esm/utils/logger.js +2 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/dist/tsdoc-metadata.json +11 -0
- package/docs/.gitattributes +2 -0
- package/docs/api/ai-assistant.ai_activity_channel_name.md +11 -0
- package/docs/api/ai-assistant.aiactivityevents.md +24 -0
- package/docs/api/ai-assistant.aiactivityhalo.channelname.md +16 -0
- package/docs/api/ai-assistant.aiactivityhalo.connectedcallback.md +18 -0
- package/docs/api/ai-assistant.aiactivityhalo.disconnectedcallback.md +18 -0
- package/docs/api/ai-assistant.aiactivityhalo.haloactive.md +14 -0
- package/docs/api/ai-assistant.aiactivityhalo.md +176 -0
- package/docs/api/ai-assistant.aiactivityhalo.minshowtime.md +16 -0
- package/docs/api/ai-assistant.aiactivityhalo.tools.md +16 -0
- package/docs/api/ai-assistant.aiassistantanimation.md +18 -0
- package/docs/api/ai-assistant.aiassistantanimationdef.label.md +16 -0
- package/docs/api/ai-assistant.aiassistantanimationdef.md +80 -0
- package/docs/api/ai-assistant.aiassistantanimationdef.tooltip.md +16 -0
- package/docs/api/ai-assistant.aiassistantconfig.chatconfig.md +16 -0
- package/docs/api/ai-assistant.aiassistantconfig.debugstatefactory.md +16 -0
- package/docs/api/ai-assistant.aiassistantconfig.headertitle.md +16 -0
- package/docs/api/ai-assistant.aiassistantconfig.md +157 -0
- package/docs/api/ai-assistant.aiassistantconfig.systemprompt.md +16 -0
- package/docs/api/ai-assistant.aiassistantconfig.tooldefinitions.md +16 -0
- package/docs/api/ai-assistant.aiassistantconfig.toolhandlers.md +16 -0
- package/docs/api/ai-assistant.aiassistantserializedstate.enabledanimations.md +14 -0
- package/docs/api/ai-assistant.aiassistantserializedstate.md +118 -0
- package/docs/api/ai-assistant.aiassistantserializedstate.messages.md +14 -0
- package/docs/api/ai-assistant.aiassistantserializedstate.showthinkingsteps.md +14 -0
- package/docs/api/ai-assistant.aiassistantserializedstate.showtoolcalls.md +14 -0
- package/docs/api/ai-assistant.aiassistantstate.md +16 -0
- package/docs/api/ai-assistant.aichatbubble.bubbleleft.md +11 -0
- package/docs/api/ai-assistant.aichatbubble.bubbletop.md +11 -0
- package/docs/api/ai-assistant.aichatbubble.channelname.md +11 -0
- package/docs/api/ai-assistant.aichatbubble.connectedcallback.md +15 -0
- package/docs/api/ai-assistant.aichatbubble.designsystemprefix.md +11 -0
- package/docs/api/ai-assistant.aichatbubble.dialogcontent.md +11 -0
- package/docs/api/ai-assistant.aichatbubble.dialogcontentchanged.md +64 -0
- package/docs/api/ai-assistant.aichatbubble.dialogheight.md +11 -0
- package/docs/api/ai-assistant.aichatbubble.dialogleft.md +11 -0
- package/docs/api/ai-assistant.aichatbubble.dialogopen.md +11 -0
- package/docs/api/ai-assistant.aichatbubble.dialogopenchanged.md +64 -0
- package/docs/api/ai-assistant.aichatbubble.dialogtop.md +11 -0
- package/docs/api/ai-assistant.aichatbubble.dialogwidth.md +11 -0
- package/docs/api/ai-assistant.aichatbubble.disconnectedcallback.md +15 -0
- package/docs/api/ai-assistant.aichatbubble.hovered.md +11 -0
- package/docs/api/ai-assistant.aichatbubble.imagesrc.md +11 -0
- package/docs/api/ai-assistant.aichatbubble.md +396 -0
- package/docs/api/ai-assistant.aichatbubble.onbubblemousedown.md +50 -0
- package/docs/api/ai-assistant.aichatbubble.onclosedialog.md +15 -0
- package/docs/api/ai-assistant.aichatbubble.ondialogheadermousedown.md +53 -0
- package/docs/api/ai-assistant.aichatbubble.onpopout.md +15 -0
- package/docs/api/ai-assistant.aichatbubble.title.md +11 -0
- package/docs/api/ai-assistant.aichatbubble.tooltipstyle.md +11 -0
- package/docs/api/ai-assistant.aichatwidget.data.md +14 -0
- package/docs/api/ai-assistant.aichatwidget.md +80 -0
- package/docs/api/ai-assistant.aichatwidget.resolved.md +16 -0
- package/docs/api/ai-assistant.animation_defs.md +25 -0
- package/docs/api/ai-assistant.chatdriver._constructor_.md +99 -0
- package/docs/api/ai-assistant.chatdriver.gethistory.md +18 -0
- package/docs/api/ai-assistant.chatdriver.isbusy.md +18 -0
- package/docs/api/ai-assistant.chatdriver.loadhistory.md +55 -0
- package/docs/api/ai-assistant.chatdriver.md +158 -0
- package/docs/api/ai-assistant.chatdriver.requestinteraction.md +73 -0
- package/docs/api/ai-assistant.chatdriver.resolveinteraction.md +69 -0
- package/docs/api/ai-assistant.chatdriver.sendmessage.md +69 -0
- package/docs/api/ai-assistant.chathistoryupdatedevent.md +16 -0
- package/docs/api/ai-assistant.defaultaiassistantconfig.md +16 -0
- package/docs/api/ai-assistant.foundationaiassistant.aiprovider.md +14 -0
- package/docs/api/ai-assistant.foundationaiassistant.applystate.md +55 -0
- package/docs/api/ai-assistant.foundationaiassistant.attachmenterrors.md +14 -0
- package/docs/api/ai-assistant.foundationaiassistant.attachments.md +14 -0
- package/docs/api/ai-assistant.foundationaiassistant.channelname.md +14 -0
- package/docs/api/ai-assistant.foundationaiassistant.chatconfig.md +14 -0
- package/docs/api/ai-assistant.foundationaiassistant.connectedcallback.md +18 -0
- package/docs/api/ai-assistant.foundationaiassistant.debugstatefactory.md +14 -0
- package/docs/api/ai-assistant.foundationaiassistant.designsystemprefix.md +14 -0
- package/docs/api/ai-assistant.foundationaiassistant.disconnectedcallback.md +18 -0
- package/docs/api/ai-assistant.foundationaiassistant.downloadhistory.md +18 -0
- package/docs/api/ai-assistant.foundationaiassistant.enabledanimations.md +16 -0
- package/docs/api/ai-assistant.foundationaiassistant.handlefileselect.md +53 -0
- package/docs/api/ai-assistant.foundationaiassistant.handleinteractioncompleted.md +53 -0
- package/docs/api/ai-assistant.foundationaiassistant.handlepopout.md +20 -0
- package/docs/api/ai-assistant.foundationaiassistant.handlesendclick.md +18 -0
- package/docs/api/ai-assistant.foundationaiassistant.hasactivependinginteraction.md +16 -0
- package/docs/api/ai-assistant.foundationaiassistant.headertitle.md +14 -0
- package/docs/api/ai-assistant.foundationaiassistant.imagesrc.md +14 -0
- package/docs/api/ai-assistant.foundationaiassistant.inputvalue.md +14 -0
- package/docs/api/ai-assistant.foundationaiassistant.md +826 -0
- package/docs/api/ai-assistant.foundationaiassistant.messages.md +14 -0
- package/docs/api/ai-assistant.foundationaiassistant.messageschanged.md +18 -0
- package/docs/api/ai-assistant.foundationaiassistant.onchatheadermousedown.md +53 -0
- package/docs/api/ai-assistant.foundationaiassistant.placeholder.md +14 -0
- package/docs/api/ai-assistant.foundationaiassistant.popoutmode.md +16 -0
- package/docs/api/ai-assistant.foundationaiassistant.removeattachment.md +53 -0
- package/docs/api/ai-assistant.foundationaiassistant.removeattachmenterror.md +53 -0
- package/docs/api/ai-assistant.foundationaiassistant.setenabledanimations.md +53 -0
- package/docs/api/ai-assistant.foundationaiassistant.settingsopen.md +16 -0
- package/docs/api/ai-assistant.foundationaiassistant.showhalo.md +16 -0
- package/docs/api/ai-assistant.foundationaiassistant.showhalochanged.md +18 -0
- package/docs/api/ai-assistant.foundationaiassistant.showloadingindicator.md +16 -0
- package/docs/api/ai-assistant.foundationaiassistant.showloadingindicatorchanged.md +18 -0
- package/docs/api/ai-assistant.foundationaiassistant.showthinkingsteps.md +16 -0
- package/docs/api/ai-assistant.foundationaiassistant.showtoolcalls.md +16 -0
- package/docs/api/ai-assistant.foundationaiassistant.state.md +14 -0
- package/docs/api/ai-assistant.foundationaiassistant.statechanged.md +18 -0
- package/docs/api/ai-assistant.foundationaiassistant.systemprompt.md +14 -0
- package/docs/api/ai-assistant.foundationaiassistant.togglesettings.md +18 -0
- package/docs/api/ai-assistant.foundationaiassistant.toggleshowthinkingsteps.md +18 -0
- package/docs/api/ai-assistant.foundationaiassistant.toggleshowtoolcalls.md +18 -0
- package/docs/api/ai-assistant.foundationaiassistant.tooldefinitions.md +14 -0
- package/docs/api/ai-assistant.foundationaiassistant.toolhandlers.md +14 -0
- package/docs/api/ai-assistant.foundationaiassistant.triggerfileinput.md +18 -0
- package/docs/api/ai-assistant.foundationaiassistant.visiblemessages.md +16 -0
- package/docs/api/ai-assistant.foundationaiassistanttemplate.md +50 -0
- package/docs/api/ai-assistant.md +262 -0
- package/docs/api/ai-assistant.popoutmode.md +16 -0
- package/docs/api/index.md +30 -0
- package/docs/api-report.md.api.md +280 -0
- package/license.txt +46 -0
- package/package.json +92 -0
- package/src/channel/ai-activity-channel.ts +30 -0
- package/src/components/activity-halo/activity-halo.ts +113 -0
- package/src/components/chat-bubble/chat-bubble.styles.ts +194 -0
- package/src/components/chat-bubble/chat-bubble.template.ts +63 -0
- package/src/components/chat-bubble/chat-bubble.ts +389 -0
- package/src/components/chat-bubble/index.ts +1 -0
- package/src/components/chat-driver/chat-driver.ts +254 -0
- package/src/components/chat-driver/index.ts +1 -0
- package/src/components/chat-interaction-wrapper/chat-interaction-wrapper.styles.ts +7 -0
- package/src/components/chat-interaction-wrapper/chat-interaction-wrapper.template.ts +6 -0
- package/src/components/chat-interaction-wrapper/chat-interaction-wrapper.ts +89 -0
- package/src/components/chat-interaction-wrapper/index.ts +3 -0
- package/src/components/chat-markdown/chat-markdown.ts +87 -0
- package/src/components/chat-markdown/index.ts +1 -0
- package/src/components/halo-overlay.ts +115 -0
- package/src/config/config.ts +59 -0
- package/src/config/configure.ts +0 -0
- package/src/config/index.ts +1 -0
- package/src/index.ts +9 -0
- package/src/main/index.ts +4 -0
- package/src/main/main.styles.ts +357 -0
- package/src/main/main.template.ts +364 -0
- package/src/main/main.ts +474 -0
- package/src/main/main.types.ts +58 -0
- package/src/styles/index.ts +1 -0
- package/src/styles/styles.ts +238 -0
- package/src/tags/index.ts +0 -0
- package/src/types/ai-chat-widget.ts +11 -0
- package/src/utils/index.ts +1 -0
- package/src/utils/logger.ts +3 -0
- package/temp/api-report.md.api.md +280 -0
- package/tsconfig.json +11 -0
package/src/main/main.ts
ADDED
|
@@ -0,0 +1,474 @@
|
|
|
1
|
+
import { AIProvider } from '@genesislcap/foundation-ai';
|
|
2
|
+
import type {
|
|
3
|
+
ChatAttachment,
|
|
4
|
+
ChatConfig,
|
|
5
|
+
ChatMessage,
|
|
6
|
+
ChatToolDefinition,
|
|
7
|
+
ChatToolHandlers,
|
|
8
|
+
} from '@genesislcap/foundation-ai';
|
|
9
|
+
import { createTypedBroadcastChannel } from '@genesislcap/foundation-broadcast-channel';
|
|
10
|
+
import type { TypedBroadcastChannel } from '@genesislcap/foundation-broadcast-channel';
|
|
11
|
+
import {
|
|
12
|
+
customElement,
|
|
13
|
+
html,
|
|
14
|
+
GenesisElement,
|
|
15
|
+
observable,
|
|
16
|
+
volatile,
|
|
17
|
+
attr,
|
|
18
|
+
} from '@genesislcap/web-core';
|
|
19
|
+
import { AI_ACTIVITY_CHANNEL_NAME } from '../channel/ai-activity-channel';
|
|
20
|
+
import type { AiActivityEvents } from '../channel/ai-activity-channel';
|
|
21
|
+
import type { AiAssistantSerializedState } from '../channel/ai-activity-channel';
|
|
22
|
+
import { ChatDriver } from '../components/chat-driver/chat-driver';
|
|
23
|
+
import { AiChatInteractionWrapper } from '../components/chat-interaction-wrapper/chat-interaction-wrapper';
|
|
24
|
+
import { AiChatMarkdown } from '../components/chat-markdown/chat-markdown';
|
|
25
|
+
import { AiHaloOverlay } from '../components/halo-overlay';
|
|
26
|
+
import { logger } from '../utils/logger';
|
|
27
|
+
import { styles } from './main.styles';
|
|
28
|
+
import { FoundationAiAssistantTemplate } from './main.template';
|
|
29
|
+
import { ALL_ANIMATIONS } from './main.types';
|
|
30
|
+
import type { AiAssistantAnimation, AiAssistantState, PopoutMode } from './main.types';
|
|
31
|
+
|
|
32
|
+
AiChatMarkdown;
|
|
33
|
+
AiChatInteractionWrapper;
|
|
34
|
+
AiHaloOverlay;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Foundation AI Assistant component.
|
|
38
|
+
*
|
|
39
|
+
* @remarks
|
|
40
|
+
* Lazy-loaded via `configure()`. Inject an `AIProvider` and `AiAssistantConfig` through the DI
|
|
41
|
+
* container. The component creates a `ChatDriver` to manage the conversation loop.
|
|
42
|
+
*
|
|
43
|
+
* @beta
|
|
44
|
+
*/
|
|
45
|
+
@customElement({
|
|
46
|
+
name: 'foundation-ai-assistant',
|
|
47
|
+
template: html`
|
|
48
|
+
${(x: FoundationAiAssistant) => FoundationAiAssistantTemplate(x.designSystemPrefix)}
|
|
49
|
+
`,
|
|
50
|
+
styles,
|
|
51
|
+
})
|
|
52
|
+
export class FoundationAiAssistant extends GenesisElement {
|
|
53
|
+
@AIProvider aiProvider!: AIProvider;
|
|
54
|
+
|
|
55
|
+
@observable designSystemPrefix: string = 'rapid';
|
|
56
|
+
@attr({ attribute: 'header-title' }) headerTitle?: string;
|
|
57
|
+
@attr({ attribute: 'channel-name' }) channelName: string = AI_ACTIVITY_CHANNEL_NAME;
|
|
58
|
+
@attr({ attribute: 'image-src' }) imageSrc?: string;
|
|
59
|
+
@attr() placeholder: string = 'Message assistant...';
|
|
60
|
+
/**
|
|
61
|
+
* Controls the pop-out button shown next to the settings cog.
|
|
62
|
+
* "expand" — in bubble, expands to layout panel (fires chat-popout).
|
|
63
|
+
* "collapse" — in layout panel, collapses back to bubble (fires chat-popin).
|
|
64
|
+
* Absent — no button (default standalone behaviour, no breaking change).
|
|
65
|
+
*/
|
|
66
|
+
@attr({ attribute: 'popout-mode' }) popoutMode?: PopoutMode;
|
|
67
|
+
@observable systemPrompt?: string;
|
|
68
|
+
@observable toolDefinitions?: ChatToolDefinition[];
|
|
69
|
+
@observable toolHandlers?: ChatToolHandlers;
|
|
70
|
+
@observable chatConfig: ChatConfig = {};
|
|
71
|
+
@observable debugStateFactory?: () => unknown;
|
|
72
|
+
|
|
73
|
+
@observable messages: ChatMessage[] = [];
|
|
74
|
+
@observable state: AiAssistantState = 'idle';
|
|
75
|
+
@observable inputValue = '';
|
|
76
|
+
@observable attachments: ChatAttachment[] = [];
|
|
77
|
+
@observable attachmentErrors: string[] = [];
|
|
78
|
+
|
|
79
|
+
/** Current user-facing toggle state for tool call visibility. */
|
|
80
|
+
@observable showToolCalls = false;
|
|
81
|
+
/** Current user-facing toggle state for thinking step visibility. */
|
|
82
|
+
@observable showThinkingSteps = false;
|
|
83
|
+
/** Currently enabled animations. */
|
|
84
|
+
@observable enabledAnimations: AiAssistantAnimation[] = [];
|
|
85
|
+
/** Whether the loading spinner is currently visible. Controlled by the loading delay timer. */
|
|
86
|
+
@observable showLoadingIndicator = false;
|
|
87
|
+
/** Whether the settings panel is open. */
|
|
88
|
+
@observable settingsOpen = false;
|
|
89
|
+
|
|
90
|
+
private driver?: ChatDriver;
|
|
91
|
+
private loadingTimer: ReturnType<typeof setTimeout> | undefined;
|
|
92
|
+
private activityChannel?: TypedBroadcastChannel<AiActivityEvents>;
|
|
93
|
+
private haloStartPublished = false;
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Whether the halo animation should be shown.
|
|
97
|
+
* True when the AI is actively computing (loading state, no pending interaction).
|
|
98
|
+
*/
|
|
99
|
+
@observable showHalo: boolean = false;
|
|
100
|
+
|
|
101
|
+
private syncShowHalo() {
|
|
102
|
+
if (this.state !== 'loading') {
|
|
103
|
+
this.showHalo = false;
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
const last = this.messages[this.messages.length - 1];
|
|
107
|
+
this.showHalo = !last?.interaction;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** True when there is a pending (unresolved) interaction — disables the popout button. */
|
|
111
|
+
@volatile
|
|
112
|
+
get hasActivePendingInteraction(): boolean {
|
|
113
|
+
if (this.state !== 'loading') return false;
|
|
114
|
+
const last = this.messages[this.messages.length - 1];
|
|
115
|
+
return !!last?.interaction && !last.interaction.resolved;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
showHaloChanged() {
|
|
119
|
+
if (!this.enabledAnimations?.includes('halo')) return;
|
|
120
|
+
if (!this.showHalo) {
|
|
121
|
+
this.activityChannel?.postMessage('halo-stop');
|
|
122
|
+
this.haloStartPublished = false;
|
|
123
|
+
}
|
|
124
|
+
// halo-start is published exclusively from messagesChanged when toolCalls arrive,
|
|
125
|
+
// so we never re-activate grids on non-toolCall transitions (e.g. tool results
|
|
126
|
+
// arriving after an interaction resolves).
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
private getActiveToolNames(): string[] {
|
|
130
|
+
for (let i = this.messages.length - 1; i >= 0; i -= 1) {
|
|
131
|
+
const msg = this.messages[i];
|
|
132
|
+
if (msg.toolCalls?.length) {
|
|
133
|
+
return msg.toolCalls.map((tc) => tc.name);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return [];
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Messages filtered by the current toggle state.
|
|
141
|
+
* Tool-related messages (those with toolCalls or toolResult) are hidden when
|
|
142
|
+
* `showToolCalls` is false.
|
|
143
|
+
*/
|
|
144
|
+
@volatile
|
|
145
|
+
get visibleMessages(): ChatMessage[] {
|
|
146
|
+
return this.messages.filter((m) => {
|
|
147
|
+
// Never show tool messages to the user
|
|
148
|
+
if (m.role === 'tool') {
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
// Filter thinking messages based on toggle
|
|
152
|
+
if (m.thinking && !this.showThinkingSteps) {
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
// Filter out empty assistant messages that might have slipped through
|
|
156
|
+
if (m.role === 'assistant' && !m.content?.trim() && !m.toolCalls?.length && !m.interaction) {
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
const isToolRelated = !!(m.toolCalls?.length || m.toolResult);
|
|
160
|
+
return !isToolRelated || this.showToolCalls;
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
connectedCallback() {
|
|
165
|
+
super.connectedCallback();
|
|
166
|
+
this.activityChannel = createTypedBroadcastChannel<AiActivityEvents>(this.channelName);
|
|
167
|
+
const { showToolCalls, showThinkingSteps, animations } = this.chatConfig;
|
|
168
|
+
this.showToolCalls = showToolCalls === true;
|
|
169
|
+
this.showThinkingSteps = showThinkingSteps === true;
|
|
170
|
+
this.enabledAnimations =
|
|
171
|
+
(animations?.enabled as AiAssistantAnimation[]) ?? (animations ? [...ALL_ANIMATIONS] : []);
|
|
172
|
+
|
|
173
|
+
this.driver = new ChatDriver(
|
|
174
|
+
this.aiProvider,
|
|
175
|
+
this.toolHandlers ?? {},
|
|
176
|
+
this.toolDefinitions ?? [],
|
|
177
|
+
this.systemPrompt,
|
|
178
|
+
);
|
|
179
|
+
this.driver.addEventListener('history-updated', (e: Event) => {
|
|
180
|
+
this.messages = [...(e as CustomEvent<ChatMessage[]>).detail];
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
// When embedded in a bubble (expand mode), listen for chat-popin to restore state
|
|
184
|
+
// when the layout panel collapses back.
|
|
185
|
+
if (this.popoutMode === 'expand') {
|
|
186
|
+
this.activityChannel.addEventListener('message', this.onChannelMessage);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
logger.debug('FoundationAiAssistant connected');
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
disconnectedCallback() {
|
|
193
|
+
super.disconnectedCallback();
|
|
194
|
+
this.stopLoadingTimer();
|
|
195
|
+
this.state = 'idle';
|
|
196
|
+
this.activityChannel?.removeEventListener('message', this.onChannelMessage);
|
|
197
|
+
this.activityChannel?.close();
|
|
198
|
+
this.activityChannel = undefined;
|
|
199
|
+
this.driver = undefined;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
private onChannelMessage = (event: MessageEvent) => {
|
|
203
|
+
if (event.data?.type === 'chat-popin' && event.data.detail?.state) {
|
|
204
|
+
this.applyState(event.data.detail.state as AiAssistantSerializedState);
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
stateChanged() {
|
|
209
|
+
this.syncShowHalo();
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
messagesChanged() {
|
|
213
|
+
// Reset the loading timer when an assistant message arrives mid-loop so each
|
|
214
|
+
// individual step gets a fresh window before the spinner appears.
|
|
215
|
+
// If the last message is a blocking interaction, stop the timer — the AI is
|
|
216
|
+
// waiting for the user, not computing.
|
|
217
|
+
if (this.state === 'loading') {
|
|
218
|
+
const last = this.messages[this.messages.length - 1];
|
|
219
|
+
if (last?.interaction) {
|
|
220
|
+
this.stopLoadingTimer();
|
|
221
|
+
} else if (last?.role === 'assistant') {
|
|
222
|
+
this.startLoadingTimer();
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
this.syncShowHalo();
|
|
226
|
+
// Publish halo-start whenever a new toolCalls message arrives.
|
|
227
|
+
// If we've already published one this turn, send halo-stop first so grids
|
|
228
|
+
// not relevant to the new tools begin deactivating (fix #2).
|
|
229
|
+
// Never publish with empty toolNames (fix #3).
|
|
230
|
+
if (this.showHalo && this.enabledAnimations?.includes('halo')) {
|
|
231
|
+
const last = this.messages[this.messages.length - 1];
|
|
232
|
+
if (last?.toolCalls?.length) {
|
|
233
|
+
const toolNames = this.getActiveToolNames();
|
|
234
|
+
if (toolNames.length) {
|
|
235
|
+
if (this.haloStartPublished) {
|
|
236
|
+
this.activityChannel?.postMessage('halo-stop');
|
|
237
|
+
}
|
|
238
|
+
this.activityChannel?.postMessage('halo-start', { toolNames });
|
|
239
|
+
this.haloStartPublished = true;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
this.scrollToBottom();
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
showLoadingIndicatorChanged() {
|
|
247
|
+
// Scroll to bottom when the spinner appears so it is visible.
|
|
248
|
+
if (this.showLoadingIndicator) {
|
|
249
|
+
this.scrollToBottom();
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Double rAF: first frame lets the framework flush the DOM update,
|
|
254
|
+
// second frame reads the correct scrollHeight after layout.
|
|
255
|
+
private scrollToBottom() {
|
|
256
|
+
requestAnimationFrame(() => {
|
|
257
|
+
requestAnimationFrame(() => {
|
|
258
|
+
const el = this.shadowRoot?.querySelector('.messages');
|
|
259
|
+
if (el) el.scrollTop = el.scrollHeight;
|
|
260
|
+
});
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
private static readonly DEFAULT_LOADING_DELAY_S = 5;
|
|
265
|
+
private static readonly MS_PER_SECOND = 1000;
|
|
266
|
+
|
|
267
|
+
private startLoadingTimer() {
|
|
268
|
+
this.clearLoadingTimer();
|
|
269
|
+
const delay = this.chatConfig.loadingDelay ?? FoundationAiAssistant.DEFAULT_LOADING_DELAY_S;
|
|
270
|
+
if (delay === 0) {
|
|
271
|
+
this.showLoadingIndicator = true;
|
|
272
|
+
} else {
|
|
273
|
+
this.loadingTimer = setTimeout(() => {
|
|
274
|
+
this.showLoadingIndicator = true;
|
|
275
|
+
}, delay * FoundationAiAssistant.MS_PER_SECOND);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
private clearLoadingTimer() {
|
|
280
|
+
if (this.loadingTimer !== undefined) {
|
|
281
|
+
clearTimeout(this.loadingTimer);
|
|
282
|
+
this.loadingTimer = undefined;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
private stopLoadingTimer() {
|
|
287
|
+
this.clearLoadingTimer();
|
|
288
|
+
this.showLoadingIndicator = false;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/** Called when the user clicks the popout button. */
|
|
292
|
+
handlePopout(): void {
|
|
293
|
+
if (this.popoutMode === 'expand') {
|
|
294
|
+
this.activityChannel?.postMessage('chat-popout', { state: this.serializeState() });
|
|
295
|
+
} else if (this.popoutMode === 'collapse') {
|
|
296
|
+
this.activityChannel?.postMessage('chat-popin', { state: this.serializeState() });
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/** Applies a serialized state snapshot to this live instance (used on collapse path). */
|
|
301
|
+
applyState(state: AiAssistantSerializedState): void {
|
|
302
|
+
this.messages = [...state.messages];
|
|
303
|
+
this.showToolCalls = state.showToolCalls;
|
|
304
|
+
this.showThinkingSteps = state.showThinkingSteps;
|
|
305
|
+
this.enabledAnimations = [...state.enabledAnimations];
|
|
306
|
+
this.driver?.loadHistory(state.messages);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
private serializeState(): AiAssistantSerializedState {
|
|
310
|
+
return {
|
|
311
|
+
messages: [...this.messages],
|
|
312
|
+
showToolCalls: this.showToolCalls,
|
|
313
|
+
showThinkingSteps: this.showThinkingSteps,
|
|
314
|
+
enabledAnimations: [...this.enabledAnimations],
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
toggleSettings() {
|
|
319
|
+
this.settingsOpen = !this.settingsOpen;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
toggleShowToolCalls() {
|
|
323
|
+
this.showToolCalls = !this.showToolCalls;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
toggleShowThinkingSteps() {
|
|
327
|
+
this.showThinkingSteps = !this.showThinkingSteps;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
setEnabledAnimations(animations: AiAssistantAnimation[]) {
|
|
331
|
+
this.enabledAnimations = animations;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
downloadHistory() {
|
|
335
|
+
const timestamp = new Date().toISOString().replace(/:/g, '-');
|
|
336
|
+
const agentName = (this.headerTitle ?? 'chat').toLowerCase().replace(/\s+/g, '-');
|
|
337
|
+
const payload = {
|
|
338
|
+
messages: this.messages,
|
|
339
|
+
debug: this.debugStateFactory?.(),
|
|
340
|
+
};
|
|
341
|
+
const blob = new Blob([JSON.stringify(payload, null, 2)], { type: 'application/json' });
|
|
342
|
+
const url = URL.createObjectURL(blob);
|
|
343
|
+
const a = document.createElement('a');
|
|
344
|
+
a.href = url;
|
|
345
|
+
a.download = `${agentName}-${timestamp}.json`;
|
|
346
|
+
a.click();
|
|
347
|
+
URL.revokeObjectURL(url);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
triggerFileInput(): void {
|
|
351
|
+
this.shadowRoot?.querySelector<HTMLInputElement>('.file-input')?.click();
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
handleFileSelect(e: Event): void {
|
|
355
|
+
void this.loadSelectedFiles(e);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
removeAttachment(attachment: ChatAttachment): void {
|
|
359
|
+
this.attachments = this.attachments.filter((a) => a !== attachment);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
removeAttachmentError(message: string): void {
|
|
363
|
+
const idx = this.attachmentErrors.indexOf(message);
|
|
364
|
+
if (idx !== -1) {
|
|
365
|
+
this.attachmentErrors = [
|
|
366
|
+
...this.attachmentErrors.slice(0, idx),
|
|
367
|
+
...this.attachmentErrors.slice(idx + 1),
|
|
368
|
+
];
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
private isAcceptedFile(file: File): boolean {
|
|
373
|
+
const accepted = this.chatConfig.acceptedFiles;
|
|
374
|
+
if (!accepted) return false;
|
|
375
|
+
|
|
376
|
+
const acceptedList = accepted.split(',').map((s) => s.trim().toLowerCase());
|
|
377
|
+
const ext = `.${file.name.split('.').pop()?.toLowerCase() ?? ''}`;
|
|
378
|
+
|
|
379
|
+
return acceptedList.some(
|
|
380
|
+
(a) =>
|
|
381
|
+
a === ext ||
|
|
382
|
+
a === file.type ||
|
|
383
|
+
(a.endsWith('/*') && file.type.startsWith(a.replace('/*', '/'))),
|
|
384
|
+
);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
private readFileAsText(file: File): Promise<string> {
|
|
388
|
+
return new Promise((resolve, reject) => {
|
|
389
|
+
const reader = new FileReader();
|
|
390
|
+
reader.onload = () => resolve(reader.result as string);
|
|
391
|
+
reader.onerror = () => reject(new Error(`Failed to read file: ${file.name}`));
|
|
392
|
+
reader.readAsText(file);
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
private async loadSelectedFiles(e: Event): Promise<void> {
|
|
397
|
+
const input = e.target as HTMLInputElement;
|
|
398
|
+
const files = Array.from(input.files ?? []);
|
|
399
|
+
input.value = '';
|
|
400
|
+
|
|
401
|
+
type FileResult = { ok: true; attachment: ChatAttachment } | { ok: false; message: string };
|
|
402
|
+
const results = await Promise.all(
|
|
403
|
+
files.map(async (file): Promise<FileResult> => {
|
|
404
|
+
if (!this.isAcceptedFile(file)) {
|
|
405
|
+
return { ok: false, message: `"${file.name}" is not an accepted file type.` };
|
|
406
|
+
}
|
|
407
|
+
try {
|
|
408
|
+
const content = await this.readFileAsText(file);
|
|
409
|
+
return {
|
|
410
|
+
ok: true,
|
|
411
|
+
attachment: { name: file.name, content, mimeType: file.type || 'text/plain' },
|
|
412
|
+
};
|
|
413
|
+
} catch (readError) {
|
|
414
|
+
logger.error('Failed to read file:', readError);
|
|
415
|
+
return { ok: false, message: `Failed to read "${file.name}".` };
|
|
416
|
+
}
|
|
417
|
+
}),
|
|
418
|
+
);
|
|
419
|
+
|
|
420
|
+
const newAttachments = results
|
|
421
|
+
.filter((r): r is Extract<FileResult, { ok: true }> => r.ok)
|
|
422
|
+
.map((r) => r.attachment);
|
|
423
|
+
const newErrors = results
|
|
424
|
+
.filter((r): r is Extract<FileResult, { ok: false }> => !r.ok)
|
|
425
|
+
.map((r) => r.message);
|
|
426
|
+
|
|
427
|
+
if (newAttachments.length) this.attachments = [...this.attachments, ...newAttachments];
|
|
428
|
+
if (newErrors.length) this.attachmentErrors = [...this.attachmentErrors, ...newErrors];
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
handleSendClick() {
|
|
432
|
+
this.send();
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
private async send() {
|
|
436
|
+
const input = this.inputValue.trim();
|
|
437
|
+
if ((!input && !this.attachments.length) || this.state === 'loading') return;
|
|
438
|
+
const pendingAttachments = this.attachments.length ? [...this.attachments] : undefined;
|
|
439
|
+
this.inputValue = '';
|
|
440
|
+
this.attachments = [];
|
|
441
|
+
this.attachmentErrors = [];
|
|
442
|
+
this.state = 'loading';
|
|
443
|
+
this.startLoadingTimer();
|
|
444
|
+
const displayInput = pendingAttachments?.length
|
|
445
|
+
? `${input}\n\n*Attached: ${pendingAttachments.map((a) => a.name).join(', ')}*`
|
|
446
|
+
: input;
|
|
447
|
+
try {
|
|
448
|
+
await this.driver?.sendMessage(displayInput, pendingAttachments);
|
|
449
|
+
} finally {
|
|
450
|
+
this.stopLoadingTimer();
|
|
451
|
+
this.state = 'idle';
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
onChatHeaderMouseDown(e: MouseEvent) {
|
|
456
|
+
if (this.popoutMode !== 'expand') return;
|
|
457
|
+
e.preventDefault();
|
|
458
|
+
this.dispatchEvent(
|
|
459
|
+
new CustomEvent('chat-header-mousedown', {
|
|
460
|
+
bubbles: true,
|
|
461
|
+
composed: true,
|
|
462
|
+
detail: { clientX: e.clientX, clientY: e.clientY },
|
|
463
|
+
}),
|
|
464
|
+
);
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
handleInteractionCompleted(e: Event) {
|
|
468
|
+
const detail = (e as CustomEvent).detail;
|
|
469
|
+
if (detail && detail.interactionId) {
|
|
470
|
+
this.startLoadingTimer();
|
|
471
|
+
this.driver?.resolveInteraction(detail.interactionId, detail);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* State of the AI assistant component.
|
|
3
|
+
*
|
|
4
|
+
* @beta
|
|
5
|
+
*/
|
|
6
|
+
export type AiAssistantState = 'idle' | 'loading' | 'error';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Controls the pop-out button shown in the assistant header.
|
|
10
|
+
* - `"expand"` — assistant is embedded in the bubble dialog; button expands it to a layout panel.
|
|
11
|
+
* - `"collapse"` — assistant is in a layout panel; button collapses it back to the bubble.
|
|
12
|
+
*
|
|
13
|
+
* @beta
|
|
14
|
+
*/
|
|
15
|
+
export type PopoutMode = 'expand' | 'collapse';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Metadata describing a UI animation in the assistant.
|
|
19
|
+
*
|
|
20
|
+
* @beta
|
|
21
|
+
*/
|
|
22
|
+
export interface AiAssistantAnimationDef {
|
|
23
|
+
/** Display label shown in the settings multiselect. */
|
|
24
|
+
label: string;
|
|
25
|
+
/** Short description shown as a tooltip on the option. */
|
|
26
|
+
tooltip: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Registry of all available animations with their display metadata.
|
|
31
|
+
* Adding an entry here automatically extends the {@link AiAssistantAnimation} type.
|
|
32
|
+
*
|
|
33
|
+
* @beta
|
|
34
|
+
*/
|
|
35
|
+
export const ANIMATION_DEFS = {
|
|
36
|
+
loading: {
|
|
37
|
+
label: 'Loading indicator',
|
|
38
|
+
tooltip: 'Shows a pulsing animation while the assistant is generating a response.',
|
|
39
|
+
},
|
|
40
|
+
halo: {
|
|
41
|
+
label: 'Halo',
|
|
42
|
+
tooltip: 'Displays a glowing halo around the assistant avatar while a response is streaming.',
|
|
43
|
+
},
|
|
44
|
+
} satisfies Record<string, AiAssistantAnimationDef>;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Union of all available animation names, derived from {@link ANIMATION_DEFS}.
|
|
48
|
+
*
|
|
49
|
+
* @beta
|
|
50
|
+
*/
|
|
51
|
+
export type AiAssistantAnimation = keyof typeof ANIMATION_DEFS;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* All available animation names, derived from the registry.
|
|
55
|
+
*
|
|
56
|
+
* @internal
|
|
57
|
+
*/
|
|
58
|
+
export const ALL_ANIMATIONS = Object.keys(ANIMATION_DEFS) as AiAssistantAnimation[];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './styles';
|