@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.
Files changed (244) hide show
  1. package/api-extractor.json +4 -0
  2. package/dist/ai-assistant.api.json +4028 -0
  3. package/dist/ai-assistant.d.ts +396 -0
  4. package/dist/dts/channel/ai-activity-channel.d.ts +32 -0
  5. package/dist/dts/channel/ai-activity-channel.d.ts.map +1 -0
  6. package/dist/dts/components/activity-halo/activity-halo.d.ts +31 -0
  7. package/dist/dts/components/activity-halo/activity-halo.d.ts.map +1 -0
  8. package/dist/dts/components/chat-bubble/chat-bubble.d.ts +52 -0
  9. package/dist/dts/components/chat-bubble/chat-bubble.d.ts.map +1 -0
  10. package/dist/dts/components/chat-bubble/chat-bubble.styles.d.ts +2 -0
  11. package/dist/dts/components/chat-bubble/chat-bubble.styles.d.ts.map +1 -0
  12. package/dist/dts/components/chat-bubble/chat-bubble.template.d.ts +4 -0
  13. package/dist/dts/components/chat-bubble/chat-bubble.template.d.ts.map +1 -0
  14. package/dist/dts/components/chat-bubble/index.d.ts +2 -0
  15. package/dist/dts/components/chat-bubble/index.d.ts.map +1 -0
  16. package/dist/dts/components/chat-driver/chat-driver.d.ts +49 -0
  17. package/dist/dts/components/chat-driver/chat-driver.d.ts.map +1 -0
  18. package/dist/dts/components/chat-driver/index.d.ts +2 -0
  19. package/dist/dts/components/chat-driver/index.d.ts.map +1 -0
  20. package/dist/dts/components/chat-interaction-wrapper/chat-interaction-wrapper.d.ts +19 -0
  21. package/dist/dts/components/chat-interaction-wrapper/chat-interaction-wrapper.d.ts.map +1 -0
  22. package/dist/dts/components/chat-interaction-wrapper/chat-interaction-wrapper.styles.d.ts +2 -0
  23. package/dist/dts/components/chat-interaction-wrapper/chat-interaction-wrapper.styles.d.ts.map +1 -0
  24. package/dist/dts/components/chat-interaction-wrapper/chat-interaction-wrapper.template.d.ts +3 -0
  25. package/dist/dts/components/chat-interaction-wrapper/chat-interaction-wrapper.template.d.ts.map +1 -0
  26. package/dist/dts/components/chat-interaction-wrapper/index.d.ts +4 -0
  27. package/dist/dts/components/chat-interaction-wrapper/index.d.ts.map +1 -0
  28. package/dist/dts/components/chat-markdown/chat-markdown.d.ts +6 -0
  29. package/dist/dts/components/chat-markdown/chat-markdown.d.ts.map +1 -0
  30. package/dist/dts/components/chat-markdown/index.d.ts +2 -0
  31. package/dist/dts/components/chat-markdown/index.d.ts.map +1 -0
  32. package/dist/dts/components/halo-overlay.d.ts +25 -0
  33. package/dist/dts/components/halo-overlay.d.ts.map +1 -0
  34. package/dist/dts/config/config.d.ts +51 -0
  35. package/dist/dts/config/config.d.ts.map +1 -0
  36. package/dist/dts/config/configure.d.ts +1 -0
  37. package/dist/dts/config/configure.d.ts.map +1 -0
  38. package/dist/dts/config/index.d.ts +2 -0
  39. package/dist/dts/config/index.d.ts.map +1 -0
  40. package/dist/dts/index.d.ts +10 -0
  41. package/dist/dts/index.d.ts.map +1 -0
  42. package/dist/dts/main/index.d.ts +5 -0
  43. package/dist/dts/main/index.d.ts.map +1 -0
  44. package/dist/dts/main/main.d.ts +103 -0
  45. package/dist/dts/main/main.d.ts.map +1 -0
  46. package/dist/dts/main/main.styles.d.ts +2 -0
  47. package/dist/dts/main/main.styles.d.ts.map +1 -0
  48. package/dist/dts/main/main.template.d.ts +4 -0
  49. package/dist/dts/main/main.template.d.ts.map +1 -0
  50. package/dist/dts/main/main.types.d.ts +54 -0
  51. package/dist/dts/main/main.types.d.ts.map +1 -0
  52. package/dist/dts/styles/index.d.ts +2 -0
  53. package/dist/dts/styles/index.d.ts.map +1 -0
  54. package/dist/dts/styles/styles.d.ts +5 -0
  55. package/dist/dts/styles/styles.d.ts.map +1 -0
  56. package/dist/dts/tags/index.d.ts +1 -0
  57. package/dist/dts/tags/index.d.ts.map +1 -0
  58. package/dist/dts/types/ai-chat-widget.d.ts +12 -0
  59. package/dist/dts/types/ai-chat-widget.d.ts.map +1 -0
  60. package/dist/dts/utils/index.d.ts +2 -0
  61. package/dist/dts/utils/index.d.ts.map +1 -0
  62. package/dist/dts/utils/logger.d.ts +2 -0
  63. package/dist/dts/utils/logger.d.ts.map +1 -0
  64. package/dist/esm/channel/ai-activity-channel.js +2 -0
  65. package/dist/esm/components/activity-halo/activity-halo.js +119 -0
  66. package/dist/esm/components/chat-bubble/chat-bubble.js +381 -0
  67. package/dist/esm/components/chat-bubble/chat-bubble.styles.js +193 -0
  68. package/dist/esm/components/chat-bubble/chat-bubble.template.js +50 -0
  69. package/dist/esm/components/chat-bubble/index.js +1 -0
  70. package/dist/esm/components/chat-driver/chat-driver.js +209 -0
  71. package/dist/esm/components/chat-driver/index.js +1 -0
  72. package/dist/esm/components/chat-interaction-wrapper/chat-interaction-wrapper.js +90 -0
  73. package/dist/esm/components/chat-interaction-wrapper/chat-interaction-wrapper.styles.js +6 -0
  74. package/dist/esm/components/chat-interaction-wrapper/chat-interaction-wrapper.template.js +4 -0
  75. package/dist/esm/components/chat-interaction-wrapper/index.js +3 -0
  76. package/dist/esm/components/chat-markdown/chat-markdown.js +95 -0
  77. package/dist/esm/components/chat-markdown/index.js +1 -0
  78. package/dist/esm/components/halo-overlay.js +118 -0
  79. package/dist/esm/config/config.js +16 -0
  80. package/dist/esm/config/configure.js +0 -0
  81. package/dist/esm/config/index.js +1 -0
  82. package/dist/esm/index.js +8 -0
  83. package/dist/esm/main/index.js +4 -0
  84. package/dist/esm/main/main.js +491 -0
  85. package/dist/esm/main/main.styles.js +356 -0
  86. package/dist/esm/main/main.template.js +272 -0
  87. package/dist/esm/main/main.types.js +22 -0
  88. package/dist/esm/styles/index.js +1 -0
  89. package/dist/esm/styles/styles.js +237 -0
  90. package/dist/esm/tags/index.js +0 -0
  91. package/dist/esm/types/ai-chat-widget.js +1 -0
  92. package/dist/esm/utils/index.js +1 -0
  93. package/dist/esm/utils/logger.js +2 -0
  94. package/dist/tsconfig.tsbuildinfo +1 -0
  95. package/dist/tsdoc-metadata.json +11 -0
  96. package/docs/.gitattributes +2 -0
  97. package/docs/api/ai-assistant.ai_activity_channel_name.md +11 -0
  98. package/docs/api/ai-assistant.aiactivityevents.md +24 -0
  99. package/docs/api/ai-assistant.aiactivityhalo.channelname.md +16 -0
  100. package/docs/api/ai-assistant.aiactivityhalo.connectedcallback.md +18 -0
  101. package/docs/api/ai-assistant.aiactivityhalo.disconnectedcallback.md +18 -0
  102. package/docs/api/ai-assistant.aiactivityhalo.haloactive.md +14 -0
  103. package/docs/api/ai-assistant.aiactivityhalo.md +176 -0
  104. package/docs/api/ai-assistant.aiactivityhalo.minshowtime.md +16 -0
  105. package/docs/api/ai-assistant.aiactivityhalo.tools.md +16 -0
  106. package/docs/api/ai-assistant.aiassistantanimation.md +18 -0
  107. package/docs/api/ai-assistant.aiassistantanimationdef.label.md +16 -0
  108. package/docs/api/ai-assistant.aiassistantanimationdef.md +80 -0
  109. package/docs/api/ai-assistant.aiassistantanimationdef.tooltip.md +16 -0
  110. package/docs/api/ai-assistant.aiassistantconfig.chatconfig.md +16 -0
  111. package/docs/api/ai-assistant.aiassistantconfig.debugstatefactory.md +16 -0
  112. package/docs/api/ai-assistant.aiassistantconfig.headertitle.md +16 -0
  113. package/docs/api/ai-assistant.aiassistantconfig.md +157 -0
  114. package/docs/api/ai-assistant.aiassistantconfig.systemprompt.md +16 -0
  115. package/docs/api/ai-assistant.aiassistantconfig.tooldefinitions.md +16 -0
  116. package/docs/api/ai-assistant.aiassistantconfig.toolhandlers.md +16 -0
  117. package/docs/api/ai-assistant.aiassistantserializedstate.enabledanimations.md +14 -0
  118. package/docs/api/ai-assistant.aiassistantserializedstate.md +118 -0
  119. package/docs/api/ai-assistant.aiassistantserializedstate.messages.md +14 -0
  120. package/docs/api/ai-assistant.aiassistantserializedstate.showthinkingsteps.md +14 -0
  121. package/docs/api/ai-assistant.aiassistantserializedstate.showtoolcalls.md +14 -0
  122. package/docs/api/ai-assistant.aiassistantstate.md +16 -0
  123. package/docs/api/ai-assistant.aichatbubble.bubbleleft.md +11 -0
  124. package/docs/api/ai-assistant.aichatbubble.bubbletop.md +11 -0
  125. package/docs/api/ai-assistant.aichatbubble.channelname.md +11 -0
  126. package/docs/api/ai-assistant.aichatbubble.connectedcallback.md +15 -0
  127. package/docs/api/ai-assistant.aichatbubble.designsystemprefix.md +11 -0
  128. package/docs/api/ai-assistant.aichatbubble.dialogcontent.md +11 -0
  129. package/docs/api/ai-assistant.aichatbubble.dialogcontentchanged.md +64 -0
  130. package/docs/api/ai-assistant.aichatbubble.dialogheight.md +11 -0
  131. package/docs/api/ai-assistant.aichatbubble.dialogleft.md +11 -0
  132. package/docs/api/ai-assistant.aichatbubble.dialogopen.md +11 -0
  133. package/docs/api/ai-assistant.aichatbubble.dialogopenchanged.md +64 -0
  134. package/docs/api/ai-assistant.aichatbubble.dialogtop.md +11 -0
  135. package/docs/api/ai-assistant.aichatbubble.dialogwidth.md +11 -0
  136. package/docs/api/ai-assistant.aichatbubble.disconnectedcallback.md +15 -0
  137. package/docs/api/ai-assistant.aichatbubble.hovered.md +11 -0
  138. package/docs/api/ai-assistant.aichatbubble.imagesrc.md +11 -0
  139. package/docs/api/ai-assistant.aichatbubble.md +396 -0
  140. package/docs/api/ai-assistant.aichatbubble.onbubblemousedown.md +50 -0
  141. package/docs/api/ai-assistant.aichatbubble.onclosedialog.md +15 -0
  142. package/docs/api/ai-assistant.aichatbubble.ondialogheadermousedown.md +53 -0
  143. package/docs/api/ai-assistant.aichatbubble.onpopout.md +15 -0
  144. package/docs/api/ai-assistant.aichatbubble.title.md +11 -0
  145. package/docs/api/ai-assistant.aichatbubble.tooltipstyle.md +11 -0
  146. package/docs/api/ai-assistant.aichatwidget.data.md +14 -0
  147. package/docs/api/ai-assistant.aichatwidget.md +80 -0
  148. package/docs/api/ai-assistant.aichatwidget.resolved.md +16 -0
  149. package/docs/api/ai-assistant.animation_defs.md +25 -0
  150. package/docs/api/ai-assistant.chatdriver._constructor_.md +99 -0
  151. package/docs/api/ai-assistant.chatdriver.gethistory.md +18 -0
  152. package/docs/api/ai-assistant.chatdriver.isbusy.md +18 -0
  153. package/docs/api/ai-assistant.chatdriver.loadhistory.md +55 -0
  154. package/docs/api/ai-assistant.chatdriver.md +158 -0
  155. package/docs/api/ai-assistant.chatdriver.requestinteraction.md +73 -0
  156. package/docs/api/ai-assistant.chatdriver.resolveinteraction.md +69 -0
  157. package/docs/api/ai-assistant.chatdriver.sendmessage.md +69 -0
  158. package/docs/api/ai-assistant.chathistoryupdatedevent.md +16 -0
  159. package/docs/api/ai-assistant.defaultaiassistantconfig.md +16 -0
  160. package/docs/api/ai-assistant.foundationaiassistant.aiprovider.md +14 -0
  161. package/docs/api/ai-assistant.foundationaiassistant.applystate.md +55 -0
  162. package/docs/api/ai-assistant.foundationaiassistant.attachmenterrors.md +14 -0
  163. package/docs/api/ai-assistant.foundationaiassistant.attachments.md +14 -0
  164. package/docs/api/ai-assistant.foundationaiassistant.channelname.md +14 -0
  165. package/docs/api/ai-assistant.foundationaiassistant.chatconfig.md +14 -0
  166. package/docs/api/ai-assistant.foundationaiassistant.connectedcallback.md +18 -0
  167. package/docs/api/ai-assistant.foundationaiassistant.debugstatefactory.md +14 -0
  168. package/docs/api/ai-assistant.foundationaiassistant.designsystemprefix.md +14 -0
  169. package/docs/api/ai-assistant.foundationaiassistant.disconnectedcallback.md +18 -0
  170. package/docs/api/ai-assistant.foundationaiassistant.downloadhistory.md +18 -0
  171. package/docs/api/ai-assistant.foundationaiassistant.enabledanimations.md +16 -0
  172. package/docs/api/ai-assistant.foundationaiassistant.handlefileselect.md +53 -0
  173. package/docs/api/ai-assistant.foundationaiassistant.handleinteractioncompleted.md +53 -0
  174. package/docs/api/ai-assistant.foundationaiassistant.handlepopout.md +20 -0
  175. package/docs/api/ai-assistant.foundationaiassistant.handlesendclick.md +18 -0
  176. package/docs/api/ai-assistant.foundationaiassistant.hasactivependinginteraction.md +16 -0
  177. package/docs/api/ai-assistant.foundationaiassistant.headertitle.md +14 -0
  178. package/docs/api/ai-assistant.foundationaiassistant.imagesrc.md +14 -0
  179. package/docs/api/ai-assistant.foundationaiassistant.inputvalue.md +14 -0
  180. package/docs/api/ai-assistant.foundationaiassistant.md +826 -0
  181. package/docs/api/ai-assistant.foundationaiassistant.messages.md +14 -0
  182. package/docs/api/ai-assistant.foundationaiassistant.messageschanged.md +18 -0
  183. package/docs/api/ai-assistant.foundationaiassistant.onchatheadermousedown.md +53 -0
  184. package/docs/api/ai-assistant.foundationaiassistant.placeholder.md +14 -0
  185. package/docs/api/ai-assistant.foundationaiassistant.popoutmode.md +16 -0
  186. package/docs/api/ai-assistant.foundationaiassistant.removeattachment.md +53 -0
  187. package/docs/api/ai-assistant.foundationaiassistant.removeattachmenterror.md +53 -0
  188. package/docs/api/ai-assistant.foundationaiassistant.setenabledanimations.md +53 -0
  189. package/docs/api/ai-assistant.foundationaiassistant.settingsopen.md +16 -0
  190. package/docs/api/ai-assistant.foundationaiassistant.showhalo.md +16 -0
  191. package/docs/api/ai-assistant.foundationaiassistant.showhalochanged.md +18 -0
  192. package/docs/api/ai-assistant.foundationaiassistant.showloadingindicator.md +16 -0
  193. package/docs/api/ai-assistant.foundationaiassistant.showloadingindicatorchanged.md +18 -0
  194. package/docs/api/ai-assistant.foundationaiassistant.showthinkingsteps.md +16 -0
  195. package/docs/api/ai-assistant.foundationaiassistant.showtoolcalls.md +16 -0
  196. package/docs/api/ai-assistant.foundationaiassistant.state.md +14 -0
  197. package/docs/api/ai-assistant.foundationaiassistant.statechanged.md +18 -0
  198. package/docs/api/ai-assistant.foundationaiassistant.systemprompt.md +14 -0
  199. package/docs/api/ai-assistant.foundationaiassistant.togglesettings.md +18 -0
  200. package/docs/api/ai-assistant.foundationaiassistant.toggleshowthinkingsteps.md +18 -0
  201. package/docs/api/ai-assistant.foundationaiassistant.toggleshowtoolcalls.md +18 -0
  202. package/docs/api/ai-assistant.foundationaiassistant.tooldefinitions.md +14 -0
  203. package/docs/api/ai-assistant.foundationaiassistant.toolhandlers.md +14 -0
  204. package/docs/api/ai-assistant.foundationaiassistant.triggerfileinput.md +18 -0
  205. package/docs/api/ai-assistant.foundationaiassistant.visiblemessages.md +16 -0
  206. package/docs/api/ai-assistant.foundationaiassistanttemplate.md +50 -0
  207. package/docs/api/ai-assistant.md +262 -0
  208. package/docs/api/ai-assistant.popoutmode.md +16 -0
  209. package/docs/api/index.md +30 -0
  210. package/docs/api-report.md.api.md +280 -0
  211. package/license.txt +46 -0
  212. package/package.json +92 -0
  213. package/src/channel/ai-activity-channel.ts +30 -0
  214. package/src/components/activity-halo/activity-halo.ts +113 -0
  215. package/src/components/chat-bubble/chat-bubble.styles.ts +194 -0
  216. package/src/components/chat-bubble/chat-bubble.template.ts +63 -0
  217. package/src/components/chat-bubble/chat-bubble.ts +389 -0
  218. package/src/components/chat-bubble/index.ts +1 -0
  219. package/src/components/chat-driver/chat-driver.ts +254 -0
  220. package/src/components/chat-driver/index.ts +1 -0
  221. package/src/components/chat-interaction-wrapper/chat-interaction-wrapper.styles.ts +7 -0
  222. package/src/components/chat-interaction-wrapper/chat-interaction-wrapper.template.ts +6 -0
  223. package/src/components/chat-interaction-wrapper/chat-interaction-wrapper.ts +89 -0
  224. package/src/components/chat-interaction-wrapper/index.ts +3 -0
  225. package/src/components/chat-markdown/chat-markdown.ts +87 -0
  226. package/src/components/chat-markdown/index.ts +1 -0
  227. package/src/components/halo-overlay.ts +115 -0
  228. package/src/config/config.ts +59 -0
  229. package/src/config/configure.ts +0 -0
  230. package/src/config/index.ts +1 -0
  231. package/src/index.ts +9 -0
  232. package/src/main/index.ts +4 -0
  233. package/src/main/main.styles.ts +357 -0
  234. package/src/main/main.template.ts +364 -0
  235. package/src/main/main.ts +474 -0
  236. package/src/main/main.types.ts +58 -0
  237. package/src/styles/index.ts +1 -0
  238. package/src/styles/styles.ts +238 -0
  239. package/src/tags/index.ts +0 -0
  240. package/src/types/ai-chat-widget.ts +11 -0
  241. package/src/utils/index.ts +1 -0
  242. package/src/utils/logger.ts +3 -0
  243. package/temp/api-report.md.api.md +280 -0
  244. package/tsconfig.json +11 -0
@@ -0,0 +1,389 @@
1
+ import { createTypedBroadcastChannel } from '@genesislcap/foundation-broadcast-channel';
2
+ import type { TypedBroadcastChannel } from '@genesislcap/foundation-broadcast-channel';
3
+ import { attr, customElement, GenesisElement, html, observable } from '@genesislcap/web-core';
4
+ import { AI_ACTIVITY_CHANNEL_NAME } from '../../channel/ai-activity-channel';
5
+ import type { AiActivityEvents } from '../../channel/ai-activity-channel';
6
+ import { styles } from './chat-bubble.styles';
7
+ import { AiChatBubbleTemplate } from './chat-bubble.template';
8
+
9
+ /** Default dimensions of the dialog panel */
10
+ const DIALOG_DEFAULT_WIDTH = 400;
11
+ const DIALOG_DEFAULT_HEIGHT = 520;
12
+
13
+ /** Initial position: bubble starts this far from the right/bottom viewport edges */
14
+ const BUBBLE_INITIAL_RIGHT_OFFSET = 90;
15
+
16
+ /** Initial position: dialog starts this far from the right/bottom viewport edges */
17
+ const DIALOG_INITIAL_RIGHT_OFFSET = 440;
18
+ const DIALOG_INITIAL_BOTTOM_OFFSET = 570;
19
+
20
+ /** Minimum distance the dialog is allowed from any viewport edge */
21
+ const VIEWPORT_EDGE_MARGIN = 20;
22
+
23
+ /** Minimum pointer movement (px) to classify an interaction as a drag rather than a click */
24
+ const DRAG_THRESHOLD = 4;
25
+
26
+ /** Keeps the bubble fully inside the viewport when dragged (accounts for bubble size + border) */
27
+ const BUBBLE_DRAG_MARGIN = 64;
28
+
29
+ /** Horizontal offset between bubble center and dialog left edge when toggling open/close */
30
+ const DIALOG_BUBBLE_HORIZONTAL_OFFSET = 340;
31
+
32
+ /** How far above the bubble the dialog opens */
33
+ const DIALOG_OPEN_TOP_OFFSET = 470;
34
+
35
+ /** How far below the dialog top the bubble snaps when closing via the X button */
36
+ const BUBBLE_SNAP_BOTTOM_OFFSET = 458;
37
+
38
+ /** Fallback tooltip width used before the element has been measured in the DOM */
39
+ const TOOLTIP_FALLBACK_WIDTH = 155;
40
+
41
+ @customElement({
42
+ name: 'foundation-ai-chat-bubble',
43
+ template: html`
44
+ ${() => AiChatBubbleTemplate()}
45
+ `,
46
+ styles,
47
+ })
48
+ export class AiChatBubble extends GenesisElement {
49
+ @attr({ attribute: 'title' }) title: string = 'AI Assistant';
50
+ @attr({ attribute: 'image-src' }) imageSrc?: string;
51
+ @attr({ attribute: 'channel-name' }) channelName: string = AI_ACTIVITY_CHANNEL_NAME;
52
+ @attr({ attribute: 'design-system-prefix' }) designSystemPrefix: string = 'rapid';
53
+
54
+ @observable dialogContent: HTMLElement[] = [];
55
+ @observable private tooltipMeasuredWidth: number = 0;
56
+ @observable bubbleLeft: number = 0;
57
+ @observable bubbleTop: number = 0;
58
+ @observable dialogLeft: number = 0;
59
+ @observable dialogTop: number = 0;
60
+ @observable dialogWidth: number = DIALOG_DEFAULT_WIDTH;
61
+ @observable dialogHeight: number = DIALOG_DEFAULT_HEIGHT;
62
+ @observable dialogOpen: boolean = false;
63
+ @observable hovered: boolean = false;
64
+
65
+ private bubbleChannel?: TypedBroadcastChannel<AiActivityEvents>;
66
+ private injectedCloseBtn?: Element;
67
+ private resizeObserver?: ResizeObserver;
68
+ private dragging: boolean = false;
69
+ private dragTarget: 'bubble' | 'dialog' | null = null;
70
+ private startMouseX: number = 0;
71
+ private startMouseY: number = 0;
72
+ private startElemLeft: number = 0;
73
+ private startElemTop: number = 0;
74
+ private hasDragged: boolean = false;
75
+ private latestMouseX: number = 0;
76
+ private latestMouseY: number = 0;
77
+ private rafHandle?: number;
78
+ private closeAbort?: AbortController;
79
+
80
+ connectedCallback() {
81
+ super.connectedCallback();
82
+ this.bubbleLeft = window.innerWidth - BUBBLE_INITIAL_RIGHT_OFFSET;
83
+ this.bubbleTop = window.innerHeight - 100;
84
+ this.dialogLeft = Math.max(
85
+ VIEWPORT_EDGE_MARGIN,
86
+ window.innerWidth - DIALOG_INITIAL_RIGHT_OFFSET,
87
+ );
88
+ this.dialogTop = Math.max(
89
+ VIEWPORT_EDGE_MARGIN,
90
+ window.innerHeight - DIALOG_INITIAL_BOTTOM_OFFSET,
91
+ );
92
+ document.addEventListener('mousemove', this.onDocMouseMove);
93
+ document.addEventListener('mouseup', this.onDocMouseUp);
94
+ this.bubbleChannel = createTypedBroadcastChannel<AiActivityEvents>(this.channelName);
95
+ this.bubbleChannel.addEventListener('message', this.onBubbleChannelMessage);
96
+ // Measure tooltip width once after first render. The bubble is visible on connect
97
+ // (dialogOpen starts false), so the element is in the DOM by the time rAF fires.
98
+ // This value persists through open/close cycles — no re-measurement needed unless
99
+ // the title attribute changes.
100
+ requestAnimationFrame(() => {
101
+ const el = this.shadowRoot?.querySelector<HTMLElement>('.tooltip');
102
+ if (el) this.tooltipMeasuredWidth = el.offsetWidth;
103
+ });
104
+ }
105
+
106
+ disconnectedCallback() {
107
+ super.disconnectedCallback();
108
+ document.removeEventListener('mousemove', this.onDocMouseMove);
109
+ document.removeEventListener('mouseup', this.onDocMouseUp);
110
+ this.bubbleChannel?.removeEventListener('message', this.onBubbleChannelMessage);
111
+ this.bubbleChannel?.close();
112
+ this.bubbleChannel = undefined;
113
+ this.injectedCloseBtn?.removeEventListener('click', this.onCloseBtnClick);
114
+ this.injectedCloseBtn?.remove();
115
+ this.injectedCloseBtn = undefined;
116
+ this.resizeObserver?.disconnect();
117
+ this.resizeObserver = undefined;
118
+ if (this.rafHandle !== undefined) {
119
+ cancelAnimationFrame(this.rafHandle);
120
+ this.rafHandle = undefined;
121
+ }
122
+ this.closeAbort?.abort();
123
+ this.closeAbort = undefined;
124
+ }
125
+
126
+ dialogOpenChanged(_: boolean, next: boolean) {
127
+ if (next) {
128
+ requestAnimationFrame(() => {
129
+ const el = this.shadowRoot?.querySelector<HTMLElement>('.dialog');
130
+ if (!el) return;
131
+ this.resizeObserver = new ResizeObserver((entries) => {
132
+ const size = entries[0]?.borderBoxSize?.[0];
133
+ if (size) {
134
+ this.dialogWidth = Math.round(size.inlineSize);
135
+ this.dialogHeight = Math.round(size.blockSize);
136
+ }
137
+ });
138
+ this.resizeObserver.observe(el);
139
+ });
140
+ } else {
141
+ this.resizeObserver?.disconnect();
142
+ this.resizeObserver = undefined;
143
+ }
144
+ }
145
+
146
+ dialogContentChanged(_: HTMLElement[], next: HTMLElement[]) {
147
+ if (!next?.length || this.injectedCloseBtn) return;
148
+ for (const el of next) {
149
+ const assistant =
150
+ el.tagName.toLowerCase() === 'foundation-ai-assistant'
151
+ ? el
152
+ : el.shadowRoot?.querySelector('foundation-ai-assistant');
153
+ if (assistant) {
154
+ this.injectCloseButton(assistant);
155
+ break;
156
+ }
157
+ }
158
+ }
159
+
160
+ private injectCloseButton(assistant: Element) {
161
+ const prefix = this.designSystemPrefix;
162
+ const btn = document.createElement(`${prefix}-button`);
163
+ btn.setAttribute('slot', 'header-actions-extra');
164
+ btn.setAttribute('appearance', 'stealth');
165
+ btn.setAttribute('title', 'Close');
166
+ const icon = document.createElement(`${prefix}-icon`);
167
+ icon.setAttribute('name', 'times');
168
+ btn.appendChild(icon);
169
+ btn.addEventListener('click', this.onCloseBtnClick);
170
+ assistant.appendChild(btn);
171
+ this.injectedCloseBtn = btn;
172
+ }
173
+
174
+ private onBubbleChannelMessage = (event: MessageEvent) => {
175
+ if (event.data?.type === 'chat-popout') {
176
+ this.startCloseDialog();
177
+ this.hidden = true;
178
+ } else if (event.data?.type === 'chat-popin') {
179
+ this.cancelClose();
180
+ this.hidden = false;
181
+ this.dialogOpen = true;
182
+ }
183
+ };
184
+
185
+ onBubbleMouseDown(e: MouseEvent) {
186
+ e.preventDefault();
187
+ this.dragging = true;
188
+ this.dragTarget = 'bubble';
189
+ this.startMouseX = e.clientX;
190
+ this.startMouseY = e.clientY;
191
+ this.latestMouseX = e.clientX;
192
+ this.latestMouseY = e.clientY;
193
+ this.startElemLeft = this.bubbleLeft;
194
+ this.startElemTop = this.bubbleTop;
195
+ this.hasDragged = false;
196
+ }
197
+
198
+ onDialogHeaderMouseDown(e: CustomEvent<{ clientX: number; clientY: number }>) {
199
+ // Sync actual rendered size before dragging — the browser's CSS resize handle
200
+ // updates the element's size but our observables may not have caught up yet.
201
+ const dialogEl = this.shadowRoot?.querySelector<HTMLElement>('.dialog');
202
+ if (dialogEl) {
203
+ this.dialogWidth = dialogEl.offsetWidth;
204
+ this.dialogHeight = dialogEl.offsetHeight;
205
+ }
206
+ this.dragging = true;
207
+ this.dragTarget = 'dialog';
208
+ this.startMouseX = e.detail.clientX;
209
+ this.startMouseY = e.detail.clientY;
210
+ this.latestMouseX = e.detail.clientX;
211
+ this.latestMouseY = e.detail.clientY;
212
+ this.startElemLeft = this.dialogLeft;
213
+ this.startElemTop = this.dialogTop;
214
+ this.hasDragged = false;
215
+ }
216
+
217
+ private readonly onCloseBtnClick = () => this.onCloseDialog();
218
+
219
+ // Cancels an in-flight close animation and restores the dialog to its open state.
220
+ // Needed for the chat-popin case: startCloseDialog() may have been called (adding
221
+ // .closing and an animationend listener) but the host was hidden before the animation
222
+ // finished. When the host reappears via chat-popin, we cancel the pending close so
223
+ // the dialog stays open rather than disappearing once animationend eventually fires.
224
+ private cancelClose() {
225
+ this.closeAbort?.abort();
226
+ this.closeAbort = undefined;
227
+ const el = this.shadowRoot?.querySelector<HTMLElement>('.dialog');
228
+ el?.classList.remove('closing');
229
+ }
230
+
231
+ // Plays the closing animation before removing the dialog from the DOM.
232
+ // dialogOpen must stay true while the animation runs — setting it to false
233
+ // immediately would cause the repeat directive to remove the element before
234
+ // the animation has a chance to play. We only set dialogOpen = false inside
235
+ // the animationend callback, at which point FAST removes the element cleanly.
236
+ // If the user prefers reduced motion we skip the animation and close immediately.
237
+ private startCloseDialog() {
238
+ const el = this.shadowRoot?.querySelector<HTMLElement>('.dialog');
239
+ const reducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
240
+ if (!el || reducedMotion) {
241
+ this.dialogOpen = false;
242
+ return;
243
+ }
244
+ this.closeAbort = new AbortController();
245
+ el.classList.add('closing');
246
+ el.addEventListener(
247
+ 'animationend',
248
+ () => {
249
+ this.dialogOpen = false;
250
+ },
251
+ {
252
+ once: true,
253
+ signal: this.closeAbort.signal,
254
+ },
255
+ );
256
+ }
257
+
258
+ // Capture the latest pointer coords and schedule one rAF per display frame.
259
+ // Pointer events can fire far faster than the display refreshes (up to 1000Hz on
260
+ // gaming mice), so we only store the coords here and let applyDragFrame do the
261
+ // actual work. If a frame is already scheduled we just overwrite the coords —
262
+ // the pending frame will pick up the freshest values when it runs.
263
+ private onDocMouseMove = (e: MouseEvent) => {
264
+ if (!this.dragging) return;
265
+ this.latestMouseX = e.clientX;
266
+ this.latestMouseY = e.clientY;
267
+ if (this.rafHandle === undefined) {
268
+ this.rafHandle = requestAnimationFrame(this.applyDragFrame);
269
+ }
270
+ };
271
+
272
+ // Runs once per display frame during a drag. Writes the new position directly to
273
+ // the element's style.translate rather than updating FAST observables, which keeps
274
+ // the compositor thread doing the work instead of triggering FAST's reactivity
275
+ // pipeline on every frame. This means the element's visual position is temporarily
276
+ // ahead of the observables — they are only synced back on mouseup (see onDocMouseUp).
277
+ // Note: style.translate is an independent CSS property and does not conflict with
278
+ // the transform used for the bubble's hover scale or the dialog's open animation.
279
+ private applyDragFrame = () => {
280
+ this.rafHandle = undefined;
281
+ const dx = this.latestMouseX - this.startMouseX;
282
+ const dy = this.latestMouseY - this.startMouseY;
283
+ if (Math.abs(dx) > DRAG_THRESHOLD || Math.abs(dy) > DRAG_THRESHOLD) this.hasDragged = true;
284
+ if (this.dragTarget === 'bubble') {
285
+ const left = Math.max(
286
+ 0,
287
+ Math.min(window.innerWidth - BUBBLE_DRAG_MARGIN, this.startElemLeft + dx),
288
+ );
289
+ const top = Math.max(
290
+ 0,
291
+ Math.min(window.innerHeight - BUBBLE_DRAG_MARGIN, this.startElemTop + dy),
292
+ );
293
+ const el = this.shadowRoot?.querySelector<HTMLElement>('.bubble');
294
+ if (el) el.style.translate = `${left}px ${top}px`;
295
+ } else if (this.dragTarget === 'dialog') {
296
+ const left = Math.max(
297
+ 0,
298
+ Math.min(window.innerWidth - DIALOG_DEFAULT_WIDTH, this.startElemLeft + dx),
299
+ );
300
+ const top = Math.max(0, Math.min(window.innerHeight - 100, this.startElemTop + dy));
301
+ const el = this.shadowRoot?.querySelector<HTMLElement>('.dialog');
302
+ if (el) el.style.translate = `${left}px ${top}px`;
303
+ }
304
+ };
305
+
306
+ // Commits the final drag position back to the FAST observables, bringing them back
307
+ // in sync with what the user sees. This triggers a single FAST re-render which writes
308
+ // the same value already on the element — no visible jump. Any pending rAF is
309
+ // cancelled first; its work is redundant since we recompute from latestMouseX/Y here.
310
+ private onDocMouseUp = () => {
311
+ if (this.rafHandle !== undefined) {
312
+ cancelAnimationFrame(this.rafHandle);
313
+ this.rafHandle = undefined;
314
+ }
315
+ if (this.dragging) {
316
+ const dx = this.latestMouseX - this.startMouseX;
317
+ const dy = this.latestMouseY - this.startMouseY;
318
+ if (this.dragTarget === 'bubble') {
319
+ this.bubbleLeft = Math.max(
320
+ 0,
321
+ Math.min(window.innerWidth - BUBBLE_DRAG_MARGIN, this.startElemLeft + dx),
322
+ );
323
+ this.bubbleTop = Math.max(
324
+ 0,
325
+ Math.min(window.innerHeight - BUBBLE_DRAG_MARGIN, this.startElemTop + dy),
326
+ );
327
+ if (!this.hasDragged) {
328
+ if (this.dialogOpen) {
329
+ this.startCloseDialog();
330
+ } else {
331
+ this.dialogLeft = Math.max(
332
+ VIEWPORT_EDGE_MARGIN,
333
+ this.bubbleLeft - DIALOG_BUBBLE_HORIZONTAL_OFFSET,
334
+ );
335
+ this.dialogTop = Math.max(
336
+ VIEWPORT_EDGE_MARGIN,
337
+ this.bubbleTop - DIALOG_OPEN_TOP_OFFSET,
338
+ );
339
+ this.dialogOpen = true;
340
+ }
341
+ }
342
+ } else if (this.dragTarget === 'dialog') {
343
+ this.dialogLeft = Math.max(
344
+ 0,
345
+ Math.min(window.innerWidth - DIALOG_DEFAULT_WIDTH, this.startElemLeft + dx),
346
+ );
347
+ this.dialogTop = Math.max(0, Math.min(window.innerHeight - 100, this.startElemTop + dy));
348
+ }
349
+ }
350
+ this.dragging = false;
351
+ this.dragTarget = null;
352
+ };
353
+
354
+ onPopout() {
355
+ this.startCloseDialog();
356
+ }
357
+
358
+ onCloseDialog() {
359
+ // Snap the bubble orb to the bottom-right of wherever the dialog currently is,
360
+ // so it reappears near where the user last positioned the window.
361
+ this.bubbleLeft = Math.max(
362
+ 0,
363
+ Math.min(
364
+ window.innerWidth - BUBBLE_DRAG_MARGIN,
365
+ this.dialogLeft + DIALOG_BUBBLE_HORIZONTAL_OFFSET,
366
+ ),
367
+ );
368
+ this.bubbleTop = Math.max(
369
+ 0,
370
+ Math.min(window.innerHeight - BUBBLE_DRAG_MARGIN, this.dialogTop + BUBBLE_SNAP_BOTTOM_OFFSET),
371
+ );
372
+ this.startCloseDialog();
373
+ }
374
+
375
+ get tooltipStyle(): string {
376
+ const tooltipWidth = this.tooltipMeasuredWidth || TOOLTIP_FALLBACK_WIDTH;
377
+ const bubbleWidth = 62; // Fixed in CSS
378
+ const margin = 8;
379
+ const naturalLeft = (bubbleWidth - tooltipWidth) / 2;
380
+ const vpLeft = this.bubbleLeft + naturalLeft;
381
+ const vpRight = vpLeft + tooltipWidth;
382
+ let shift = 0;
383
+ if (vpLeft < margin) shift = margin - vpLeft;
384
+ else if (vpRight > window.innerWidth - margin) shift = window.innerWidth - margin - vpRight;
385
+ const tooltipLeft = naturalLeft + shift;
386
+ const arrowLeft = bubbleWidth / 2 - tooltipLeft;
387
+ return `left: ${tooltipLeft}px; --arrow-left: ${arrowLeft}px`;
388
+ }
389
+ }
@@ -0,0 +1 @@
1
+ export * from './chat-bubble';
@@ -0,0 +1,254 @@
1
+ import type {
2
+ AIProvider,
3
+ ChatAttachment,
4
+ ChatMessage,
5
+ ChatRequestOptions,
6
+ ChatToolDefinition,
7
+ ChatToolHandlers,
8
+ } from '@genesislcap/foundation-ai';
9
+ import { MalformedFunctionCallError } from '@genesislcap/foundation-ai';
10
+ import { logger } from '../../utils/logger';
11
+
12
+ const MAX_TOOL_ITERATIONS = 10;
13
+ const MAX_MALFORMED_RETRIES = 2;
14
+
15
+ /**
16
+ * Event emitted when the chat history is updated (new message appended).
17
+ *
18
+ * @beta
19
+ */
20
+ export type ChatHistoryUpdatedEvent = CustomEvent<ReadonlyArray<ChatMessage>>;
21
+
22
+ /**
23
+ * Plain TS class that drives a multi-turn chat conversation, including the tool-call loop.
24
+ * Owned by `FoundationAiAssistant` — created in `connectedCallback`, torn down in `disconnectedCallback`.
25
+ *
26
+ * Dispatches `'history-updated'` events on itself so the owning element can observe changes.
27
+ *
28
+ * @beta
29
+ */
30
+ export class ChatDriver extends EventTarget {
31
+ private history: ChatMessage[] = [];
32
+ private busy = false;
33
+ private pendingInteractions = new Map<
34
+ string,
35
+ { resolve: (value: any) => void; reject: (reason?: any) => void }
36
+ >();
37
+
38
+ constructor(
39
+ private readonly aiProvider: AIProvider,
40
+ private readonly toolHandlers: ChatToolHandlers = {},
41
+ private readonly toolDefinitions: ChatToolDefinition[] = [],
42
+ private readonly systemPrompt?: string,
43
+ ) {
44
+ super();
45
+ }
46
+
47
+ getHistory(): ReadonlyArray<ChatMessage> {
48
+ return this.history;
49
+ }
50
+
51
+ isBusy(): boolean {
52
+ return this.busy;
53
+ }
54
+
55
+ /**
56
+ * Request a custom UI interaction. Emits a new message with the interaction.
57
+ * Tool handlers can call this to pause execution until the user completes the UI interaction.
58
+ *
59
+ * @param componentName - The custom element name to render.
60
+ * @param data - Data to pass to the component.
61
+ */
62
+ public async requestInteraction<T>(componentName: string, data: any): Promise<T> {
63
+ const interactionId = crypto.randomUUID();
64
+ return new Promise((resolve, reject) => {
65
+ this.pendingInteractions.set(interactionId, { resolve, reject });
66
+ this.appendToHistory({
67
+ role: 'assistant',
68
+ content: '',
69
+ interaction: { interactionId, componentName, data },
70
+ });
71
+ });
72
+ }
73
+
74
+ /**
75
+ * Resolve a pending interaction. The wrapper component calls this on completion.
76
+ * Marks the interaction message as resolved so it renders read-only on re-render.
77
+ */
78
+ public resolveInteraction(interactionId: string, result: any): void {
79
+ const interaction = this.pendingInteractions.get(interactionId);
80
+ if (interaction) {
81
+ // Mark the message as resolved before resolving the promise so the next
82
+ // history-updated event carries the updated flag.
83
+ const msg = this.history.find((m) => m.interaction?.interactionId === interactionId);
84
+ if (msg?.interaction) {
85
+ msg.interaction.resolved = true;
86
+ this.dispatchEvent(
87
+ new CustomEvent<ReadonlyArray<ChatMessage>>('history-updated', {
88
+ detail: this.history,
89
+ }),
90
+ );
91
+ }
92
+ interaction.resolve(result);
93
+ this.pendingInteractions.delete(interactionId);
94
+ } else {
95
+ logger.warn(`Interaction with ID ${interactionId} not found.`);
96
+ }
97
+ }
98
+
99
+ /**
100
+ * Seeds the driver with a prior conversation history so a new component instance
101
+ * can continue an existing conversation. Call this before the first user message.
102
+ */
103
+ public loadHistory(messages: ChatMessage[]): void {
104
+ this.history = [...messages];
105
+ }
106
+
107
+ async sendMessage(userInput: string, attachments?: ChatAttachment[]): Promise<void> {
108
+ if (this.busy || (!userInput.trim() && !attachments?.length)) return;
109
+ if (!this.aiProvider.chat) {
110
+ logger.warn('ChatDriver: AIProvider does not implement chat()');
111
+ return;
112
+ }
113
+
114
+ this.busy = true;
115
+ this.appendToHistory({ role: 'user', content: userInput, attachments });
116
+
117
+ try {
118
+ await this.runToolLoop(userInput, attachments);
119
+ } catch (e) {
120
+ logger.error('ChatDriver error:', e);
121
+ this.appendToHistory({ role: 'assistant', content: 'Sorry, something went wrong.' });
122
+ } finally {
123
+ this.busy = false;
124
+ }
125
+ }
126
+
127
+ private async runToolLoop(userInput: string, attachments?: ChatAttachment[]): Promise<void> {
128
+ const baseOptions: ChatRequestOptions = {
129
+ systemPrompt: this.systemPrompt,
130
+ tools: this.toolDefinitions.length ? this.toolDefinitions : undefined,
131
+ };
132
+
133
+ // History has the user message at the end — pass everything before it as history,
134
+ // and the user input as the userMessage argument.
135
+ let currentInput = userInput;
136
+ let currentAttachments: ChatAttachment[] | undefined = attachments;
137
+ let iterations = 0;
138
+ let malformedAttempts = 0;
139
+
140
+ while (iterations < MAX_TOOL_ITERATIONS) {
141
+ iterations += 1;
142
+
143
+ // On the first iteration, the last item in history is the user message which is
144
+ // passed separately as currentInput — exclude it. On subsequent iterations, the
145
+ // full history (including tool results) should be sent and currentInput is empty.
146
+ const historyForCall = iterations === 1 ? this.history.slice(0, -1) : [...this.history];
147
+
148
+ // On malformed-call retries, augment the system prompt to steer the model
149
+ // away from generating Python-style batched function call syntax.
150
+ const systemPrompt =
151
+ malformedAttempts > 0
152
+ ? `${baseOptions.systemPrompt ?? ''}\n\nIMPORTANT: Use only the structured function-call API to invoke tools. Do not write Python code or use Python-style syntax to call tools.`
153
+ : baseOptions.systemPrompt;
154
+
155
+ const options: ChatRequestOptions = {
156
+ ...baseOptions,
157
+ systemPrompt,
158
+ attachments: currentAttachments,
159
+ };
160
+
161
+ let response: ChatMessage;
162
+ try {
163
+ // eslint-disable-next-line no-await-in-loop
164
+ response = await this.aiProvider.chat!(historyForCall, currentInput, options);
165
+ } catch (e) {
166
+ if (e instanceof MalformedFunctionCallError) {
167
+ malformedAttempts += 1;
168
+ if (malformedAttempts < MAX_MALFORMED_RETRIES) {
169
+ logger.warn(
170
+ `ChatDriver: MALFORMED_FUNCTION_CALL, retrying (${malformedAttempts}/${MAX_MALFORMED_RETRIES})`,
171
+ );
172
+ iterations -= 1; // don't consume an iteration budget slot for a failed attempt
173
+ continue;
174
+ }
175
+ logger.error('ChatDriver: MALFORMED_FUNCTION_CALL, max retries reached');
176
+ this.appendToHistory({
177
+ role: 'assistant',
178
+ content:
179
+ "I'm sorry, I wasn't able to complete that request. Please try rephrasing or breaking it into smaller steps.",
180
+ });
181
+ return;
182
+ }
183
+ throw e;
184
+ }
185
+
186
+ currentAttachments = undefined; // attachments only sent on first call
187
+
188
+ const isThinkingStep = response.content && response.toolCalls?.length;
189
+ const isEmptyResponse = !response.content?.trim() && !response.toolCalls?.length;
190
+
191
+ if (isEmptyResponse) {
192
+ // Do nothing, discard empty/whitespace-only responses
193
+ } else if (isThinkingStep) {
194
+ // Separate thinking message and tool call message
195
+ this.appendToHistory({ ...response, toolCalls: undefined, thinking: true });
196
+ this.appendToHistory({ ...response, content: '' });
197
+ } else {
198
+ this.appendToHistory(response);
199
+ }
200
+
201
+ if (!response.toolCalls?.length) {
202
+ // Terminal text response — done
203
+ break;
204
+ }
205
+
206
+ // Execute all tool calls for this iteration concurrently, then append results in order
207
+ // eslint-disable-next-line no-await-in-loop
208
+ const toolResults = await Promise.all(
209
+ response.toolCalls.map(async (tc) => {
210
+ const handler = this.toolHandlers[tc.name];
211
+ if (!handler) {
212
+ logger.warn(`ChatDriver: no handler registered for tool "${tc.name}"`);
213
+ return { toolCallId: tc.id, content: `Unknown tool: ${tc.name}` };
214
+ }
215
+ try {
216
+ const result = await handler(tc.args, {
217
+ requestInteraction: (componentName, data) =>
218
+ this.requestInteraction(componentName, data),
219
+ });
220
+ const content = typeof result === 'string' ? result : JSON.stringify(result);
221
+ return { toolCallId: tc.id, content };
222
+ } catch (e) {
223
+ logger.error(`ChatDriver tool "${tc.name}" failed:`, e);
224
+ return { toolCallId: tc.id, content: `Tool error: ${(e as Error).message}` };
225
+ }
226
+ }),
227
+ );
228
+
229
+ for (const result of toolResults) {
230
+ this.appendToHistory({
231
+ role: 'tool',
232
+ content: '',
233
+ toolResult: result,
234
+ });
235
+ }
236
+
237
+ // Next iteration sends an empty string — the tool results are in history
238
+ currentInput = '';
239
+ }
240
+
241
+ if (iterations >= MAX_TOOL_ITERATIONS) {
242
+ logger.warn('ChatDriver: reached max tool iterations, stopping');
243
+ }
244
+ }
245
+
246
+ private appendToHistory(message: ChatMessage): void {
247
+ this.history = [...this.history, message];
248
+ this.dispatchEvent(
249
+ new CustomEvent<ReadonlyArray<ChatMessage>>('history-updated', {
250
+ detail: this.history,
251
+ }),
252
+ );
253
+ }
254
+ }
@@ -0,0 +1 @@
1
+ export * from './chat-driver';
@@ -0,0 +1,7 @@
1
+ import { css } from '@genesislcap/web-core';
2
+
3
+ export const AiChatInteractionWrapperStyles = css`
4
+ :host {
5
+ display: block;
6
+ }
7
+ `;
@@ -0,0 +1,6 @@
1
+ import { html } from '@genesislcap/web-core';
2
+ import type { AiChatInteractionWrapper } from './chat-interaction-wrapper';
3
+
4
+ export const AiChatInteractionWrapperTemplate = html<AiChatInteractionWrapper>`
5
+ <div class="container" part="container"></div>
6
+ `;