@adminforth/agent 1.43.18 → 1.43.19

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/build.log CHANGED
@@ -4,6 +4,8 @@
4
4
 
5
5
  sending incremental file list
6
6
  custom/
7
+ custom/ChatFooter.vue
8
+ custom/ChatHeader.vue
7
9
  custom/ChatSurface.vue
8
10
  custom/CustomAutoScrollContainer.vue
9
11
  custom/SessionsHistory.vue
@@ -60,5 +62,5 @@ custom/speech_recognition_frontend/voiceActivityDetection.ts
60
62
  custom/speech_recognition_frontend/types/
61
63
  custom/speech_recognition_frontend/types/voice-activity-detection.d.ts
62
64
 
63
- sent 1,665,515 bytes received 883 bytes 3,332,796.00 bytes/sec
64
- total size is 1,661,500 speedup is 1.00
65
+ sent 1,666,076 bytes received 921 bytes 1,111,331.33 bytes/sec
66
+ total size is 1,661,923 speedup is 1.00
@@ -0,0 +1,172 @@
1
+ <template>
2
+ <div
3
+ ref="promptInput"
4
+ class="w-full mb-2 flex items-center justify-center px-2 bg-transparent relative translate-x-[-50%] left-1/2"
5
+ :style="{
6
+ maxWidth: agentStore.isFullScreen ? remToPx(agentStore.MAX_WIDTH)+'px' : '100%',
7
+ transition: `transform ${agentTransitions.TRANSITION_DURATION}ms ease-in-out`
8
+ }"
9
+ >
10
+ <div
11
+ class="w-full border rounded-lg pb-8"
12
+ :class="agentStore.isAudioChatMode ? 'border-none mt-8' : 'border dark:bg-gray-700'"
13
+ >
14
+ <textarea
15
+ v-if="!agentStore.isAudioChatMode"
16
+ v-model="agentStore.userMessageInput"
17
+ ref="textInput"
18
+ @input="autoResize"
19
+ :class="[
20
+ 'min-h-12 px-4 pt-4 rounded-xl w-full resize-none overflow-hidden text-lightInputText dark:text-darkInputText rounded-md bg-transparent text-sm bg-gray-50 dark:bg-gray-700 dark:border-gray-600 focus:outline-none',
21
+ { '!text-base': coreStore.isIos }
22
+ ]"
23
+ :placeholder="agentStore.userMessagePlaceholder"
24
+ @keydown.enter.exact.prevent="sendMessage"
25
+ />
26
+ <div
27
+ v-if="agentStore.availableModes.length > 1"
28
+ ref="modeMenu"
29
+ class="absolute bottom-2 left-4"
30
+ >
31
+ <button
32
+ aria-label="Select mode"
33
+ class="flex px-2 py-1 items-center text-sm justify-center
34
+ rounded-md bg-white text-lightListTableHeadingText
35
+ transition-colors duration-200 hover:bg-gray-100
36
+ dark:text-darkListTableHeadingText dark:bg-gray-700 dark:hover:bg-gray-800"
37
+ :class="isModeMenuOpen ? 'bg-gray-100 dark:bg-gray-700' : ''"
38
+ :disabled="agentStore.isResponseInProgress"
39
+ title="Select mode"
40
+ type="button"
41
+ @click="toggleModeMenu"
42
+ >
43
+ {{ agentStore.activeModeName }}
44
+ <IconAngleDownOutline
45
+ class="w-4 h-4 ml-1"
46
+ />
47
+ </button>
48
+
49
+ <div
50
+ v-if="isModeMenuOpen"
51
+ class="absolute bottom-full left-0 mb-2 min-w-40 overflow-hidden rounded-md border border-gray-200 bg-white shadow-lg dark:border-gray-600 dark:bg-gray-800"
52
+ >
53
+ <button
54
+ v-for="mode in agentStore.availableModes"
55
+ :key="mode.name"
56
+ class="block w-full px-3 py-2 text-left text-sm text-lightInputText transition-colors duration-150 hover:bg-gray-100 dark:text-darkInputText dark:hover:bg-gray-700"
57
+ :class="mode.name === agentStore.activeModeName ? 'bg-gray-100 dark:bg-gray-700' : ''"
58
+ type="button"
59
+ @click="selectMode(mode.name)"
60
+ >
61
+ {{ mode.name }}
62
+ </button>
63
+ </div>
64
+ </div>
65
+ <MicrophoneButton
66
+ v-if="props.meta.hasAudioAdapter"
67
+ />
68
+ <template v-if="!agentStore.isAudioChatMode">
69
+ <Button
70
+ v-if="!agentStore.isResponseInProgress"
71
+ class="absolute right-4 bottom-2 !p-0 h-9 w-9 transition-opacity duration-200"
72
+ @click="sendMessage"
73
+ :disabled="!agentStore.trimmedUserMessage || agentStore.isResponseInProgress"
74
+ >
75
+ <IconArrowUpOutline
76
+ class="w-8 h-8 p-1
77
+ text-white"
78
+ />
79
+ </Button>
80
+ <Button
81
+ v-else
82
+ class="absolute right-4 bottom-2 !p-0 h-9 w-9"
83
+ @click="stopCurrentRequest"
84
+ >
85
+ <div
86
+ class="w-3 h-3 bg-white rounded-sm"
87
+ />
88
+ </Button>
89
+ </template>
90
+ </div>
91
+ </div>
92
+ </template>
93
+
94
+ <script setup lang="ts">
95
+ import { IconArrowUpOutline, IconAngleDownOutline } from '@iconify-prerendered/vue-flowbite';
96
+ import { useTemplateRef, onMounted, ref, onUnmounted } from 'vue';
97
+ import { onClickOutside } from '@vueuse/core'
98
+ import { useAgentStore } from './composables/useAgentStore';
99
+ import { useAgentTransitions } from './composables/useAgentTransitions';
100
+ import { Button } from '@/afcl';
101
+ import { useCoreStore } from '@/stores/core';
102
+ import { remToPx } from './utils';
103
+ import MicrophoneButton from './speech_recognition_frontend/MicrophoneButon.vue';
104
+
105
+ const props = defineProps<{
106
+ meta: {
107
+ pluginInstanceId: string;
108
+ modes: Array<{
109
+ name: string;
110
+ }>;
111
+ defaultModeName: string | null;
112
+ stickByDefault: boolean;
113
+ hasAudioAdapter: boolean;
114
+ }
115
+ adminUser: any
116
+ conversationAreaRef: any
117
+ }>();
118
+
119
+ const textInput = useTemplateRef('textInput');
120
+ const modeMenu = useTemplateRef('modeMenu');
121
+ const agentStore = useAgentStore();
122
+ const agentTransitions = useAgentTransitions();
123
+ const coreStore = useCoreStore();
124
+ const isModeMenuOpen = ref(false);
125
+
126
+ onClickOutside(modeMenu as any, () => { isModeMenuOpen.value = false; });
127
+
128
+ onMounted(async () => {
129
+ agentStore.setAvailableModes(props.meta.modes, props.meta.defaultModeName);
130
+ agentStore.setCurrentGenerationModeFromLocalStorage();
131
+ agentStore.regisrerTextInput(textInput.value);
132
+ textInput.value?.focus();
133
+ });
134
+
135
+ function autoResize() {
136
+ const el = textInput.value
137
+ if (!el) return
138
+
139
+ el.style.height = 'auto'
140
+ //max-w-48
141
+ const maxHeight = 192
142
+ if (el.scrollHeight > maxHeight) {
143
+ el.style.height = maxHeight + 'px'
144
+ el.style.overflowY = 'auto'
145
+ } else {
146
+ el.style.height = el.scrollHeight + 'px'
147
+ el.style.overflowY = 'hidden'
148
+ }
149
+ }
150
+
151
+ function toggleModeMenu() {
152
+ isModeMenuOpen.value = !isModeMenuOpen.value;
153
+ }
154
+
155
+ function selectMode(modeName: string) {
156
+ agentStore.setActiveMode(modeName);
157
+ isModeMenuOpen.value = false;
158
+ }
159
+
160
+ async function sendMessage() {
161
+ if (agentStore.isAudioChatMode) return;
162
+ isModeMenuOpen.value = false;
163
+ await agentStore.sendMessage();
164
+ autoResize();
165
+ props.conversationAreaRef?.handleSendMessage();
166
+ }
167
+
168
+ function stopCurrentRequest() {
169
+ agentStore.abortCurrentChatRequestAndAddSystemMessage();
170
+ }
171
+
172
+ </script>
@@ -0,0 +1,78 @@
1
+ <template>
2
+ <div
3
+ ref="headerRef"
4
+ class="flex items-center justify-between h-14 border-b border-gray-200 dark:border-gray-700"
5
+ >
6
+ <div
7
+ class="flex items-center"
8
+ >
9
+ <IconBarsOutline
10
+ class="m-2 w-8 h-8 p-1 cursor-pointer hover:scale-110 rounded transition-colors duration-200
11
+ text-lightNavbarIcons hover:text-lightNavbarIcons/80 hover:bg-lightNavbarIcons/20
12
+ dark:text-darkNavbarIcons hover:text-darkNavbarIcons/80 hover:bg-darkNavbarIcons/20 "
13
+ :class="agentStore.isSessionHistoryOpen ?
14
+ 'bg-lightNavbarIcons/20 text-lightNavbarIcons/80 dark:bg-darkNavbarIcons/20 dark:text-darkNavbarIcons/80' :
15
+ ''"
16
+ @click="agentStore.setSessionHistoryOpen(!agentStore.isSessionHistoryOpen)"
17
+ />
18
+ <IconOpenSidebarSolid
19
+ v-if="!agentStore.isTeleportedToBody && !coreStore.isMobile && !agentStore.isFullScreen"
20
+ class="m-2 w-8 h-8 p-1 cursor-pointer hover:scale-110 rounded transition-colors duration-200
21
+ text-lightNavbarIcons hover:text-lightNavbarIcons/80 hover:bg-lightNavbarIcons/20
22
+ dark:text-darkNavbarIcons hover:text-darkNavbarIcons/80 hover:bg-darkNavbarIcons/20 "
23
+ @click="agentStore.setIsTeleportedToBody(true)"
24
+ />
25
+ <IconCloseSidebarSolid
26
+ v-else-if="!coreStore.isMobile && !agentStore.isFullScreen"
27
+ class="m-2 w-8 h-8 p-1 cursor-pointer hover:scale-110 rounded transition-colors duration-200
28
+ text-lightNavbarIcons hover:text-lightNavbarIcons/80 bg-lightNavbarIcons/20
29
+ dark:bg-darkNavbarIcons/20 dark:text-darkNavbarIcons/80 hover:text-darkNavbarIcons/80 hover:bg-darkNavbarIcons/20 "
30
+ @click="agentStore.setIsTeleportedToBody(false)"
31
+ />
32
+ <IconArrowsPointingOut
33
+ v-if="!agentStore.isFullScreen && !coreStore.isMobile"
34
+ class="m-2 w-8 h-8 p-1 cursor-pointer hover:scale-110 rounded transition-colors duration-200
35
+ text-lightNavbarIcons hover:text-lightNavbarIcons/80 hover:bg-lightNavbarIcons/20
36
+ dark:text-darkNavbarIcons hover:text-darkNavbarIcons/80 hover:bg-darkNavbarIcons/20 "
37
+ @click="agentStore.setFullScreen(true)"
38
+ />
39
+ <IconArrowsPointingIn
40
+ v-else-if="!coreStore.isMobile"
41
+ class="m-2 w-8 h-8 p-1 cursor-pointer hover:scale-110 rounded transition-colors duration-200
42
+ text-lightNavbarIcons hover:text-lightNavbarIcons/80 hover:bg-lightNavbarIcons/20
43
+ dark:text-darkNavbarIcons hover:text-darkNavbarIcons/80 hover:bg-darkNavbarIcons/20 "
44
+ :class="agentStore.isFullScreen ?
45
+ 'bg-lightNavbarIcons/20 text-lightNavbarIcons/80 dark:bg-darkNavbarIcons/20 dark:text-darkNavbarIcons/80' :
46
+ ''"
47
+ @click="agentStore.setFullScreen(false)"
48
+ />
49
+ </div>
50
+ <div class="flex items-center justify-center">
51
+ <Button
52
+ @click="agentStore.createPreSession(); agentStore.setSessionHistoryOpen(false); agentStore.focusTextInput();"
53
+ class="!py-1 !px-2 rounded-3xl text-gray-800 dark:text-gray-200 max-w-64 mr-2"
54
+ >
55
+ <IconPlusOutline class="w-5 h-5" />
56
+ {{ $t('New chat') }}
57
+ </Button>
58
+ <IconCloseOutline
59
+ class="m-2 w-8 h-8 p-1 cursor-pointer hover:scale-110 rounded transition-colors duration-200
60
+ text-lightNavbarIcons hover:text-lightNavbarIcons/80 hover:bg-lightNavbarIcons/20
61
+ dark:text-darkNavbarIcons hover:text-darkNavbarIcons/80 hover:bg-darkNavbarIcons/20 "
62
+ @click="agentStore.setIsChatOpen(false)"
63
+ />
64
+ </div>
65
+ </div>
66
+ </template>
67
+
68
+
69
+ <script setup>
70
+ import { IconArrowsPointingOut, IconArrowsPointingIn } from '@iconify-prerendered/vue-heroicons';
71
+ import { IconCloseOutline, IconBarsOutline, IconCloseSidebarSolid, IconOpenSidebarSolid, IconPlusOutline } from '@iconify-prerendered/vue-flowbite';
72
+ import { useAgentStore } from './composables/useAgentStore';
73
+ import { useCoreStore } from '@/stores/core';
74
+ import { Button } from '@/afcl';
75
+
76
+ const agentStore = useAgentStore();
77
+ const coreStore = useCoreStore();
78
+ </script>
@@ -36,71 +36,7 @@
36
36
  height: !agentStore.isIos ? dvh + 'px' : '100dvh',
37
37
  }"
38
38
  >
39
- <div
40
- ref="headerRef"
41
- class="flex items-center justify-between h-14 border-b border-gray-200 dark:border-gray-700"
42
- >
43
- <div
44
- class="flex items-center"
45
- >
46
- <IconBarsOutline
47
- class="m-2 w-8 h-8 p-1 cursor-pointer hover:scale-110 rounded transition-colors duration-200
48
- text-lightNavbarIcons hover:text-lightNavbarIcons/80 hover:bg-lightNavbarIcons/20
49
- dark:text-darkNavbarIcons hover:text-darkNavbarIcons/80 hover:bg-darkNavbarIcons/20 "
50
- :class="agentStore.isSessionHistoryOpen ?
51
- 'bg-lightNavbarIcons/20 text-lightNavbarIcons/80 dark:bg-darkNavbarIcons/20 dark:text-darkNavbarIcons/80' :
52
- ''"
53
- @click="agentStore.setSessionHistoryOpen(!agentStore.isSessionHistoryOpen)"
54
- />
55
- <IconOpenSidebarSolid
56
- v-if="!agentStore.isTeleportedToBody && !coreStore.isMobile && !agentStore.isFullScreen"
57
- class="m-2 w-8 h-8 p-1 cursor-pointer hover:scale-110 rounded transition-colors duration-200
58
- text-lightNavbarIcons hover:text-lightNavbarIcons/80 hover:bg-lightNavbarIcons/20
59
- dark:text-darkNavbarIcons hover:text-darkNavbarIcons/80 hover:bg-darkNavbarIcons/20 "
60
- @click="agentStore.setIsTeleportedToBody(true)"
61
- />
62
- <IconCloseSidebarSolid
63
- v-else-if="!coreStore.isMobile && !agentStore.isFullScreen"
64
- class="m-2 w-8 h-8 p-1 cursor-pointer hover:scale-110 rounded transition-colors duration-200
65
- text-lightNavbarIcons hover:text-lightNavbarIcons/80 bg-lightNavbarIcons/20
66
- dark:bg-darkNavbarIcons/20 dark:text-darkNavbarIcons/80 hover:text-darkNavbarIcons/80 hover:bg-darkNavbarIcons/20 "
67
- @click="agentStore.setIsTeleportedToBody(false)"
68
- />
69
- <IconArrowsPointingOut
70
- v-if="!agentStore.isFullScreen && !coreStore.isMobile"
71
- class="m-2 w-8 h-8 p-1 cursor-pointer hover:scale-110 rounded transition-colors duration-200
72
- text-lightNavbarIcons hover:text-lightNavbarIcons/80 hover:bg-lightNavbarIcons/20
73
- dark:text-darkNavbarIcons hover:text-darkNavbarIcons/80 hover:bg-darkNavbarIcons/20 "
74
- @click="agentStore.setFullScreen(true)"
75
- />
76
- <IconArrowsPointingIn
77
- v-else-if="!coreStore.isMobile"
78
- class="m-2 w-8 h-8 p-1 cursor-pointer hover:scale-110 rounded transition-colors duration-200
79
- text-lightNavbarIcons hover:text-lightNavbarIcons/80 hover:bg-lightNavbarIcons/20
80
- dark:text-darkNavbarIcons hover:text-darkNavbarIcons/80 hover:bg-darkNavbarIcons/20 "
81
- :class="agentStore.isFullScreen ?
82
- 'bg-lightNavbarIcons/20 text-lightNavbarIcons/80 dark:bg-darkNavbarIcons/20 dark:text-darkNavbarIcons/80' :
83
- ''"
84
- @click="agentStore.setFullScreen(false)"
85
- />
86
- </div>
87
- <div class="flex items-center justify-center">
88
- <Button
89
- @click="agentStore.createPreSession(); agentStore.setSessionHistoryOpen(false); agentStore.focusTextInput();"
90
- class="!py-1 !px-2 rounded-3xl text-gray-800 dark:text-gray-200 max-w-64 mr-2"
91
- >
92
- <IconPlusOutline class="w-5 h-5" />
93
- {{ $t('New chat') }}
94
- </Button>
95
- <IconCloseOutline
96
- class="m-2 w-8 h-8 p-1 cursor-pointer hover:scale-110 rounded transition-colors duration-200
97
- text-lightNavbarIcons hover:text-lightNavbarIcons/80 hover:bg-lightNavbarIcons/20
98
- dark:text-darkNavbarIcons hover:text-darkNavbarIcons/80 hover:bg-darkNavbarIcons/20 "
99
- @click="agentStore.setFullScreen(false); agentStore.setIsChatOpen(false)"
100
- />
101
- </div>
102
-
103
- </div>
39
+ <ChatHeader />
104
40
  <div
105
41
  class="relative flex-1 min-h-0 flex flex-col overflow-hidden"
106
42
  >
@@ -109,97 +45,11 @@
109
45
  v-if="agentStore.isChatOpen"
110
46
  :messages="agentStore.chatMessages"
111
47
  />
112
-
113
- <div
114
- ref="promptInput"
115
- class="w-full mb-2 flex items-center justify-center px-2 bg-transparent relative translate-x-[-50%] left-1/2"
116
- :style="{
117
- maxWidth: agentStore.isFullScreen ? remToPx(agentStore.MAX_WIDTH)+'px' : '100%',
118
- transition: `transform ${agentTransitions.TRANSITION_DURATION}ms ease-in-out`
119
- }"
120
- >
121
- <div
122
- class="w-full border rounded-lg pb-8"
123
- :class="agentStore.isAudioChatMode ? 'border-none mt-8' : 'border dark:bg-gray-700'"
124
- >
125
- <textarea
126
- v-if="!agentStore.isAudioChatMode"
127
- v-model="agentStore.userMessageInput"
128
- ref="textInput"
129
- @input="autoResize"
130
- :class="[
131
- 'min-h-12 px-4 pt-4 rounded-xl w-full resize-none overflow-hidden text-lightInputText dark:text-darkInputText rounded-md bg-transparent text-sm bg-gray-50 dark:bg-gray-700 dark:border-gray-600 focus:outline-none',
132
- { '!text-base': coreStore.isIos }
133
- ]"
134
- :placeholder="agentStore.userMessagePlaceholder"
135
- @keydown.enter.exact.prevent="sendMessage"
136
- />
137
- <div
138
- v-if="agentStore.availableModes.length > 1"
139
- ref="modeMenu"
140
- class="absolute bottom-2 left-4"
141
- >
142
- <button
143
- aria-label="Select mode"
144
- class="flex px-2 py-1 items-center text-sm justify-center
145
- rounded-md bg-white text-lightListTableHeadingText
146
- transition-colors duration-200 hover:bg-gray-100
147
- dark:text-darkListTableHeadingText dark:bg-gray-700 dark:hover:bg-gray-800"
148
- :class="isModeMenuOpen ? 'bg-gray-100 dark:bg-gray-700' : ''"
149
- :disabled="agentStore.isResponseInProgress"
150
- title="Select mode"
151
- type="button"
152
- @click="toggleModeMenu"
153
- >
154
- {{ agentStore.activeModeName }}
155
- <IconAngleDownOutline
156
- class="w-4 h-4 ml-1"
157
- />
158
- </button>
159
-
160
- <div
161
- v-if="isModeMenuOpen"
162
- class="absolute bottom-full left-0 mb-2 min-w-40 overflow-hidden rounded-md border border-gray-200 bg-white shadow-lg dark:border-gray-600 dark:bg-gray-800"
163
- >
164
- <button
165
- v-for="mode in agentStore.availableModes"
166
- :key="mode.name"
167
- class="block w-full px-3 py-2 text-left text-sm text-lightInputText transition-colors duration-150 hover:bg-gray-100 dark:text-darkInputText dark:hover:bg-gray-700"
168
- :class="mode.name === agentStore.activeModeName ? 'bg-gray-100 dark:bg-gray-700' : ''"
169
- type="button"
170
- @click="selectMode(mode.name)"
171
- >
172
- {{ mode.name }}
173
- </button>
174
- </div>
175
- </div>
176
- <MicrophoneButton
177
- v-if="props.meta.hasAudioAdapter"
178
- />
179
- <template v-if="!agentStore.isAudioChatMode">
180
- <Button
181
- v-if="!agentStore.isResponseInProgress"
182
- class="absolute right-4 bottom-2 !p-0 h-9 w-9 transition-opacity duration-200"
183
- @click="sendMessage"
184
- :disabled="!agentStore.trimmedUserMessage || agentStore.isResponseInProgress"
185
- >
186
- <IconArrowUpOutline
187
- class="w-8 h-8 p-1
188
- text-white"
189
- />
190
- </Button>
191
- <Button
192
- v-else
193
- class="absolute right-4 bottom-2 !p-0 h-9 w-9"
194
- @click="stopCurrentRequest"
195
- >
196
- <div
197
- class="w-3 h-3 bg-white rounded-sm"
198
- />
199
- </Button>
200
- </template>
201
- </div>
202
- </div>
48
+ <ChatFooter
49
+ :meta="props.meta"
50
+ :adminUser="props.adminUser"
51
+ :conversationAreaRef="conversationArea"
52
+ />
203
53
  </div>
204
54
  </div>
205
55
  </div>
@@ -208,17 +58,16 @@
208
58
  </template>
209
59
 
210
60
  <script setup lang="ts">
211
- import { IconChatBubbleLeft20Solid, IconSparklesSolid, IconArrowsPointingOut, IconArrowsPointingIn } from '@iconify-prerendered/vue-heroicons';
212
- import { IconCloseOutline, IconBarsOutline, IconArrowUpOutline, IconCloseSidebarSolid, IconOpenSidebarSolid, IconAngleDownOutline, IconPlusOutline } from '@iconify-prerendered/vue-flowbite';
61
+ import { IconChatBubbleLeft20Solid, IconSparklesSolid } from '@iconify-prerendered/vue-heroicons';
213
62
  import { useTemplateRef, onMounted, ref, onUnmounted } from 'vue';
214
63
  import { onClickOutside } from '@vueuse/core'
215
64
  import ConversationArea from './conversation_area/ConversationArea.vue';
65
+ import ChatHeader from './ChatHeader.vue';
66
+ import ChatFooter from './ChatFooter.vue';
216
67
  import { useAgentStore } from './composables/useAgentStore';
217
68
  import { useAgentTransitions } from './composables/useAgentTransitions';
218
- import { Button } from '@/afcl';
219
69
  import { useCoreStore } from '@/stores/core';
220
70
  import { remToPx } from './utils';
221
- import MicrophoneButton from './speech_recognition_frontend/MicrophoneButon.vue';
222
71
 
223
72
  const props = defineProps<{
224
73
  meta: {
@@ -234,28 +83,20 @@ const props = defineProps<{
234
83
  }>();
235
84
 
236
85
  const chatSurface = useTemplateRef('chatSurface');
237
- const textInput = useTemplateRef('textInput');
238
- const modeMenu = useTemplateRef('modeMenu');
239
86
  const conversationArea = useTemplateRef('conversationArea');
240
87
  const agentStore = useAgentStore();
241
88
  const agentTransitions = useAgentTransitions();
242
89
  const coreStore = useCoreStore();
243
- const isModeMenuOpen = ref(false);
244
90
 
245
91
  const dvh = ref(window.innerHeight)
246
92
 
247
93
  let startX = 0
248
94
  let startWidth = 0
249
95
 
250
- onClickOutside(chatSurface, () => {if (!agentStore.isTeleportedToBody && !agentStore.isFullScreen) agentStore.setIsChatOpen(false);});
251
- onClickOutside(modeMenu, () => { isModeMenuOpen.value = false; });
96
+ onClickOutside(chatSurface as any, () => {if (!agentStore.isTeleportedToBody && !agentStore.isFullScreen) agentStore.setIsChatOpen(false);});
252
97
 
253
98
  onMounted(async () => {
254
- agentStore.setAvailableModes(props.meta.modes, props.meta.defaultModeName);
255
- agentStore.setCurrentGenerationModeFromLocalStorage();
256
- agentStore.regisrerTextInput(textInput.value);
257
99
  window.addEventListener('resize', updateHeight)
258
- textInput.value?.focus();
259
100
  const savedIsTeleportedToBody = agentStore.getLocalStorageItem('isTeleportedToBody');
260
101
  const savedIsTeleportedToBodyBeforeFullScreen = agentStore.getLocalStorageItem('isTeleportedToBodyBeforeFullScreen');
261
102
  let isTeleportedToBodyFromLocalStorage = true;
@@ -306,43 +147,6 @@ const stopResize = () => {
306
147
  }
307
148
  }
308
149
 
309
- function autoResize() {
310
- const el = textInput.value
311
- if (!el) return
312
-
313
- el.style.height = 'auto'
314
- //max-w-48
315
- const maxHeight = 192
316
- if (el.scrollHeight > maxHeight) {
317
- el.style.height = maxHeight + 'px'
318
- el.style.overflowY = 'auto'
319
- } else {
320
- el.style.height = el.scrollHeight + 'px'
321
- el.style.overflowY = 'hidden'
322
- }
323
- }
324
-
325
- function toggleModeMenu() {
326
- isModeMenuOpen.value = !isModeMenuOpen.value;
327
- }
328
-
329
- function selectMode(modeName: string) {
330
- agentStore.setActiveMode(modeName);
331
- isModeMenuOpen.value = false;
332
- }
333
-
334
- async function sendMessage() {
335
- if (agentStore.isAudioChatMode) return;
336
- isModeMenuOpen.value = false;
337
- await agentStore.sendMessage();
338
- autoResize();
339
- conversationArea.value?.handleSendMessage();
340
- }
341
-
342
- function stopCurrentRequest() {
343
- agentStore.abortCurrentChatRequestAndAddSystemMessage();
344
- }
345
-
346
150
  function updateHeight() {
347
151
  dvh.value = Math.round(window.visualViewport?.height || window.innerHeight);
348
152
  }
@@ -271,6 +271,9 @@ export const useAgentStore = defineStore('agent', () => {
271
271
  }
272
272
 
273
273
  async function closeChat() {
274
+ if (!isChatOpen.value) {
275
+ return;
276
+ }
274
277
  await executeOnBeforeChatCloseCallbacks();
275
278
  if(isFullScreen.value) {
276
279
  document.body.style.overflow = '';
@@ -279,7 +282,9 @@ export const useAgentStore = defineStore('agent', () => {
279
282
  return;
280
283
  }
281
284
  isChatOpen.value = false;
282
- setFullScreen(false);
285
+ if (isFullScreen.value) {
286
+ setFullScreen(false);
287
+ }
283
288
  isSessionHistoryOpen.value = false;
284
289
  }
285
290
 
@@ -0,0 +1,172 @@
1
+ <template>
2
+ <div
3
+ ref="promptInput"
4
+ class="w-full mb-2 flex items-center justify-center px-2 bg-transparent relative translate-x-[-50%] left-1/2"
5
+ :style="{
6
+ maxWidth: agentStore.isFullScreen ? remToPx(agentStore.MAX_WIDTH)+'px' : '100%',
7
+ transition: `transform ${agentTransitions.TRANSITION_DURATION}ms ease-in-out`
8
+ }"
9
+ >
10
+ <div
11
+ class="w-full border rounded-lg pb-8"
12
+ :class="agentStore.isAudioChatMode ? 'border-none mt-8' : 'border dark:bg-gray-700'"
13
+ >
14
+ <textarea
15
+ v-if="!agentStore.isAudioChatMode"
16
+ v-model="agentStore.userMessageInput"
17
+ ref="textInput"
18
+ @input="autoResize"
19
+ :class="[
20
+ 'min-h-12 px-4 pt-4 rounded-xl w-full resize-none overflow-hidden text-lightInputText dark:text-darkInputText rounded-md bg-transparent text-sm bg-gray-50 dark:bg-gray-700 dark:border-gray-600 focus:outline-none',
21
+ { '!text-base': coreStore.isIos }
22
+ ]"
23
+ :placeholder="agentStore.userMessagePlaceholder"
24
+ @keydown.enter.exact.prevent="sendMessage"
25
+ />
26
+ <div
27
+ v-if="agentStore.availableModes.length > 1"
28
+ ref="modeMenu"
29
+ class="absolute bottom-2 left-4"
30
+ >
31
+ <button
32
+ aria-label="Select mode"
33
+ class="flex px-2 py-1 items-center text-sm justify-center
34
+ rounded-md bg-white text-lightListTableHeadingText
35
+ transition-colors duration-200 hover:bg-gray-100
36
+ dark:text-darkListTableHeadingText dark:bg-gray-700 dark:hover:bg-gray-800"
37
+ :class="isModeMenuOpen ? 'bg-gray-100 dark:bg-gray-700' : ''"
38
+ :disabled="agentStore.isResponseInProgress"
39
+ title="Select mode"
40
+ type="button"
41
+ @click="toggleModeMenu"
42
+ >
43
+ {{ agentStore.activeModeName }}
44
+ <IconAngleDownOutline
45
+ class="w-4 h-4 ml-1"
46
+ />
47
+ </button>
48
+
49
+ <div
50
+ v-if="isModeMenuOpen"
51
+ class="absolute bottom-full left-0 mb-2 min-w-40 overflow-hidden rounded-md border border-gray-200 bg-white shadow-lg dark:border-gray-600 dark:bg-gray-800"
52
+ >
53
+ <button
54
+ v-for="mode in agentStore.availableModes"
55
+ :key="mode.name"
56
+ class="block w-full px-3 py-2 text-left text-sm text-lightInputText transition-colors duration-150 hover:bg-gray-100 dark:text-darkInputText dark:hover:bg-gray-700"
57
+ :class="mode.name === agentStore.activeModeName ? 'bg-gray-100 dark:bg-gray-700' : ''"
58
+ type="button"
59
+ @click="selectMode(mode.name)"
60
+ >
61
+ {{ mode.name }}
62
+ </button>
63
+ </div>
64
+ </div>
65
+ <MicrophoneButton
66
+ v-if="props.meta.hasAudioAdapter"
67
+ />
68
+ <template v-if="!agentStore.isAudioChatMode">
69
+ <Button
70
+ v-if="!agentStore.isResponseInProgress"
71
+ class="absolute right-4 bottom-2 !p-0 h-9 w-9 transition-opacity duration-200"
72
+ @click="sendMessage"
73
+ :disabled="!agentStore.trimmedUserMessage || agentStore.isResponseInProgress"
74
+ >
75
+ <IconArrowUpOutline
76
+ class="w-8 h-8 p-1
77
+ text-white"
78
+ />
79
+ </Button>
80
+ <Button
81
+ v-else
82
+ class="absolute right-4 bottom-2 !p-0 h-9 w-9"
83
+ @click="stopCurrentRequest"
84
+ >
85
+ <div
86
+ class="w-3 h-3 bg-white rounded-sm"
87
+ />
88
+ </Button>
89
+ </template>
90
+ </div>
91
+ </div>
92
+ </template>
93
+
94
+ <script setup lang="ts">
95
+ import { IconArrowUpOutline, IconAngleDownOutline } from '@iconify-prerendered/vue-flowbite';
96
+ import { useTemplateRef, onMounted, ref, onUnmounted } from 'vue';
97
+ import { onClickOutside } from '@vueuse/core'
98
+ import { useAgentStore } from './composables/useAgentStore';
99
+ import { useAgentTransitions } from './composables/useAgentTransitions';
100
+ import { Button } from '@/afcl';
101
+ import { useCoreStore } from '@/stores/core';
102
+ import { remToPx } from './utils';
103
+ import MicrophoneButton from './speech_recognition_frontend/MicrophoneButon.vue';
104
+
105
+ const props = defineProps<{
106
+ meta: {
107
+ pluginInstanceId: string;
108
+ modes: Array<{
109
+ name: string;
110
+ }>;
111
+ defaultModeName: string | null;
112
+ stickByDefault: boolean;
113
+ hasAudioAdapter: boolean;
114
+ }
115
+ adminUser: any
116
+ conversationAreaRef: any
117
+ }>();
118
+
119
+ const textInput = useTemplateRef('textInput');
120
+ const modeMenu = useTemplateRef('modeMenu');
121
+ const agentStore = useAgentStore();
122
+ const agentTransitions = useAgentTransitions();
123
+ const coreStore = useCoreStore();
124
+ const isModeMenuOpen = ref(false);
125
+
126
+ onClickOutside(modeMenu as any, () => { isModeMenuOpen.value = false; });
127
+
128
+ onMounted(async () => {
129
+ agentStore.setAvailableModes(props.meta.modes, props.meta.defaultModeName);
130
+ agentStore.setCurrentGenerationModeFromLocalStorage();
131
+ agentStore.regisrerTextInput(textInput.value);
132
+ textInput.value?.focus();
133
+ });
134
+
135
+ function autoResize() {
136
+ const el = textInput.value
137
+ if (!el) return
138
+
139
+ el.style.height = 'auto'
140
+ //max-w-48
141
+ const maxHeight = 192
142
+ if (el.scrollHeight > maxHeight) {
143
+ el.style.height = maxHeight + 'px'
144
+ el.style.overflowY = 'auto'
145
+ } else {
146
+ el.style.height = el.scrollHeight + 'px'
147
+ el.style.overflowY = 'hidden'
148
+ }
149
+ }
150
+
151
+ function toggleModeMenu() {
152
+ isModeMenuOpen.value = !isModeMenuOpen.value;
153
+ }
154
+
155
+ function selectMode(modeName: string) {
156
+ agentStore.setActiveMode(modeName);
157
+ isModeMenuOpen.value = false;
158
+ }
159
+
160
+ async function sendMessage() {
161
+ if (agentStore.isAudioChatMode) return;
162
+ isModeMenuOpen.value = false;
163
+ await agentStore.sendMessage();
164
+ autoResize();
165
+ props.conversationAreaRef?.handleSendMessage();
166
+ }
167
+
168
+ function stopCurrentRequest() {
169
+ agentStore.abortCurrentChatRequestAndAddSystemMessage();
170
+ }
171
+
172
+ </script>
@@ -0,0 +1,78 @@
1
+ <template>
2
+ <div
3
+ ref="headerRef"
4
+ class="flex items-center justify-between h-14 border-b border-gray-200 dark:border-gray-700"
5
+ >
6
+ <div
7
+ class="flex items-center"
8
+ >
9
+ <IconBarsOutline
10
+ class="m-2 w-8 h-8 p-1 cursor-pointer hover:scale-110 rounded transition-colors duration-200
11
+ text-lightNavbarIcons hover:text-lightNavbarIcons/80 hover:bg-lightNavbarIcons/20
12
+ dark:text-darkNavbarIcons hover:text-darkNavbarIcons/80 hover:bg-darkNavbarIcons/20 "
13
+ :class="agentStore.isSessionHistoryOpen ?
14
+ 'bg-lightNavbarIcons/20 text-lightNavbarIcons/80 dark:bg-darkNavbarIcons/20 dark:text-darkNavbarIcons/80' :
15
+ ''"
16
+ @click="agentStore.setSessionHistoryOpen(!agentStore.isSessionHistoryOpen)"
17
+ />
18
+ <IconOpenSidebarSolid
19
+ v-if="!agentStore.isTeleportedToBody && !coreStore.isMobile && !agentStore.isFullScreen"
20
+ class="m-2 w-8 h-8 p-1 cursor-pointer hover:scale-110 rounded transition-colors duration-200
21
+ text-lightNavbarIcons hover:text-lightNavbarIcons/80 hover:bg-lightNavbarIcons/20
22
+ dark:text-darkNavbarIcons hover:text-darkNavbarIcons/80 hover:bg-darkNavbarIcons/20 "
23
+ @click="agentStore.setIsTeleportedToBody(true)"
24
+ />
25
+ <IconCloseSidebarSolid
26
+ v-else-if="!coreStore.isMobile && !agentStore.isFullScreen"
27
+ class="m-2 w-8 h-8 p-1 cursor-pointer hover:scale-110 rounded transition-colors duration-200
28
+ text-lightNavbarIcons hover:text-lightNavbarIcons/80 bg-lightNavbarIcons/20
29
+ dark:bg-darkNavbarIcons/20 dark:text-darkNavbarIcons/80 hover:text-darkNavbarIcons/80 hover:bg-darkNavbarIcons/20 "
30
+ @click="agentStore.setIsTeleportedToBody(false)"
31
+ />
32
+ <IconArrowsPointingOut
33
+ v-if="!agentStore.isFullScreen && !coreStore.isMobile"
34
+ class="m-2 w-8 h-8 p-1 cursor-pointer hover:scale-110 rounded transition-colors duration-200
35
+ text-lightNavbarIcons hover:text-lightNavbarIcons/80 hover:bg-lightNavbarIcons/20
36
+ dark:text-darkNavbarIcons hover:text-darkNavbarIcons/80 hover:bg-darkNavbarIcons/20 "
37
+ @click="agentStore.setFullScreen(true)"
38
+ />
39
+ <IconArrowsPointingIn
40
+ v-else-if="!coreStore.isMobile"
41
+ class="m-2 w-8 h-8 p-1 cursor-pointer hover:scale-110 rounded transition-colors duration-200
42
+ text-lightNavbarIcons hover:text-lightNavbarIcons/80 hover:bg-lightNavbarIcons/20
43
+ dark:text-darkNavbarIcons hover:text-darkNavbarIcons/80 hover:bg-darkNavbarIcons/20 "
44
+ :class="agentStore.isFullScreen ?
45
+ 'bg-lightNavbarIcons/20 text-lightNavbarIcons/80 dark:bg-darkNavbarIcons/20 dark:text-darkNavbarIcons/80' :
46
+ ''"
47
+ @click="agentStore.setFullScreen(false)"
48
+ />
49
+ </div>
50
+ <div class="flex items-center justify-center">
51
+ <Button
52
+ @click="agentStore.createPreSession(); agentStore.setSessionHistoryOpen(false); agentStore.focusTextInput();"
53
+ class="!py-1 !px-2 rounded-3xl text-gray-800 dark:text-gray-200 max-w-64 mr-2"
54
+ >
55
+ <IconPlusOutline class="w-5 h-5" />
56
+ {{ $t('New chat') }}
57
+ </Button>
58
+ <IconCloseOutline
59
+ class="m-2 w-8 h-8 p-1 cursor-pointer hover:scale-110 rounded transition-colors duration-200
60
+ text-lightNavbarIcons hover:text-lightNavbarIcons/80 hover:bg-lightNavbarIcons/20
61
+ dark:text-darkNavbarIcons hover:text-darkNavbarIcons/80 hover:bg-darkNavbarIcons/20 "
62
+ @click="agentStore.setIsChatOpen(false)"
63
+ />
64
+ </div>
65
+ </div>
66
+ </template>
67
+
68
+
69
+ <script setup>
70
+ import { IconArrowsPointingOut, IconArrowsPointingIn } from '@iconify-prerendered/vue-heroicons';
71
+ import { IconCloseOutline, IconBarsOutline, IconCloseSidebarSolid, IconOpenSidebarSolid, IconPlusOutline } from '@iconify-prerendered/vue-flowbite';
72
+ import { useAgentStore } from './composables/useAgentStore';
73
+ import { useCoreStore } from '@/stores/core';
74
+ import { Button } from '@/afcl';
75
+
76
+ const agentStore = useAgentStore();
77
+ const coreStore = useCoreStore();
78
+ </script>
@@ -36,71 +36,7 @@
36
36
  height: !agentStore.isIos ? dvh + 'px' : '100dvh',
37
37
  }"
38
38
  >
39
- <div
40
- ref="headerRef"
41
- class="flex items-center justify-between h-14 border-b border-gray-200 dark:border-gray-700"
42
- >
43
- <div
44
- class="flex items-center"
45
- >
46
- <IconBarsOutline
47
- class="m-2 w-8 h-8 p-1 cursor-pointer hover:scale-110 rounded transition-colors duration-200
48
- text-lightNavbarIcons hover:text-lightNavbarIcons/80 hover:bg-lightNavbarIcons/20
49
- dark:text-darkNavbarIcons hover:text-darkNavbarIcons/80 hover:bg-darkNavbarIcons/20 "
50
- :class="agentStore.isSessionHistoryOpen ?
51
- 'bg-lightNavbarIcons/20 text-lightNavbarIcons/80 dark:bg-darkNavbarIcons/20 dark:text-darkNavbarIcons/80' :
52
- ''"
53
- @click="agentStore.setSessionHistoryOpen(!agentStore.isSessionHistoryOpen)"
54
- />
55
- <IconOpenSidebarSolid
56
- v-if="!agentStore.isTeleportedToBody && !coreStore.isMobile && !agentStore.isFullScreen"
57
- class="m-2 w-8 h-8 p-1 cursor-pointer hover:scale-110 rounded transition-colors duration-200
58
- text-lightNavbarIcons hover:text-lightNavbarIcons/80 hover:bg-lightNavbarIcons/20
59
- dark:text-darkNavbarIcons hover:text-darkNavbarIcons/80 hover:bg-darkNavbarIcons/20 "
60
- @click="agentStore.setIsTeleportedToBody(true)"
61
- />
62
- <IconCloseSidebarSolid
63
- v-else-if="!coreStore.isMobile && !agentStore.isFullScreen"
64
- class="m-2 w-8 h-8 p-1 cursor-pointer hover:scale-110 rounded transition-colors duration-200
65
- text-lightNavbarIcons hover:text-lightNavbarIcons/80 bg-lightNavbarIcons/20
66
- dark:bg-darkNavbarIcons/20 dark:text-darkNavbarIcons/80 hover:text-darkNavbarIcons/80 hover:bg-darkNavbarIcons/20 "
67
- @click="agentStore.setIsTeleportedToBody(false)"
68
- />
69
- <IconArrowsPointingOut
70
- v-if="!agentStore.isFullScreen && !coreStore.isMobile"
71
- class="m-2 w-8 h-8 p-1 cursor-pointer hover:scale-110 rounded transition-colors duration-200
72
- text-lightNavbarIcons hover:text-lightNavbarIcons/80 hover:bg-lightNavbarIcons/20
73
- dark:text-darkNavbarIcons hover:text-darkNavbarIcons/80 hover:bg-darkNavbarIcons/20 "
74
- @click="agentStore.setFullScreen(true)"
75
- />
76
- <IconArrowsPointingIn
77
- v-else-if="!coreStore.isMobile"
78
- class="m-2 w-8 h-8 p-1 cursor-pointer hover:scale-110 rounded transition-colors duration-200
79
- text-lightNavbarIcons hover:text-lightNavbarIcons/80 hover:bg-lightNavbarIcons/20
80
- dark:text-darkNavbarIcons hover:text-darkNavbarIcons/80 hover:bg-darkNavbarIcons/20 "
81
- :class="agentStore.isFullScreen ?
82
- 'bg-lightNavbarIcons/20 text-lightNavbarIcons/80 dark:bg-darkNavbarIcons/20 dark:text-darkNavbarIcons/80' :
83
- ''"
84
- @click="agentStore.setFullScreen(false)"
85
- />
86
- </div>
87
- <div class="flex items-center justify-center">
88
- <Button
89
- @click="agentStore.createPreSession(); agentStore.setSessionHistoryOpen(false); agentStore.focusTextInput();"
90
- class="!py-1 !px-2 rounded-3xl text-gray-800 dark:text-gray-200 max-w-64 mr-2"
91
- >
92
- <IconPlusOutline class="w-5 h-5" />
93
- {{ $t('New chat') }}
94
- </Button>
95
- <IconCloseOutline
96
- class="m-2 w-8 h-8 p-1 cursor-pointer hover:scale-110 rounded transition-colors duration-200
97
- text-lightNavbarIcons hover:text-lightNavbarIcons/80 hover:bg-lightNavbarIcons/20
98
- dark:text-darkNavbarIcons hover:text-darkNavbarIcons/80 hover:bg-darkNavbarIcons/20 "
99
- @click="agentStore.setFullScreen(false); agentStore.setIsChatOpen(false)"
100
- />
101
- </div>
102
-
103
- </div>
39
+ <ChatHeader />
104
40
  <div
105
41
  class="relative flex-1 min-h-0 flex flex-col overflow-hidden"
106
42
  >
@@ -109,97 +45,11 @@
109
45
  v-if="agentStore.isChatOpen"
110
46
  :messages="agentStore.chatMessages"
111
47
  />
112
-
113
- <div
114
- ref="promptInput"
115
- class="w-full mb-2 flex items-center justify-center px-2 bg-transparent relative translate-x-[-50%] left-1/2"
116
- :style="{
117
- maxWidth: agentStore.isFullScreen ? remToPx(agentStore.MAX_WIDTH)+'px' : '100%',
118
- transition: `transform ${agentTransitions.TRANSITION_DURATION}ms ease-in-out`
119
- }"
120
- >
121
- <div
122
- class="w-full border rounded-lg pb-8"
123
- :class="agentStore.isAudioChatMode ? 'border-none mt-8' : 'border dark:bg-gray-700'"
124
- >
125
- <textarea
126
- v-if="!agentStore.isAudioChatMode"
127
- v-model="agentStore.userMessageInput"
128
- ref="textInput"
129
- @input="autoResize"
130
- :class="[
131
- 'min-h-12 px-4 pt-4 rounded-xl w-full resize-none overflow-hidden text-lightInputText dark:text-darkInputText rounded-md bg-transparent text-sm bg-gray-50 dark:bg-gray-700 dark:border-gray-600 focus:outline-none',
132
- { '!text-base': coreStore.isIos }
133
- ]"
134
- :placeholder="agentStore.userMessagePlaceholder"
135
- @keydown.enter.exact.prevent="sendMessage"
136
- />
137
- <div
138
- v-if="agentStore.availableModes.length > 1"
139
- ref="modeMenu"
140
- class="absolute bottom-2 left-4"
141
- >
142
- <button
143
- aria-label="Select mode"
144
- class="flex px-2 py-1 items-center text-sm justify-center
145
- rounded-md bg-white text-lightListTableHeadingText
146
- transition-colors duration-200 hover:bg-gray-100
147
- dark:text-darkListTableHeadingText dark:bg-gray-700 dark:hover:bg-gray-800"
148
- :class="isModeMenuOpen ? 'bg-gray-100 dark:bg-gray-700' : ''"
149
- :disabled="agentStore.isResponseInProgress"
150
- title="Select mode"
151
- type="button"
152
- @click="toggleModeMenu"
153
- >
154
- {{ agentStore.activeModeName }}
155
- <IconAngleDownOutline
156
- class="w-4 h-4 ml-1"
157
- />
158
- </button>
159
-
160
- <div
161
- v-if="isModeMenuOpen"
162
- class="absolute bottom-full left-0 mb-2 min-w-40 overflow-hidden rounded-md border border-gray-200 bg-white shadow-lg dark:border-gray-600 dark:bg-gray-800"
163
- >
164
- <button
165
- v-for="mode in agentStore.availableModes"
166
- :key="mode.name"
167
- class="block w-full px-3 py-2 text-left text-sm text-lightInputText transition-colors duration-150 hover:bg-gray-100 dark:text-darkInputText dark:hover:bg-gray-700"
168
- :class="mode.name === agentStore.activeModeName ? 'bg-gray-100 dark:bg-gray-700' : ''"
169
- type="button"
170
- @click="selectMode(mode.name)"
171
- >
172
- {{ mode.name }}
173
- </button>
174
- </div>
175
- </div>
176
- <MicrophoneButton
177
- v-if="props.meta.hasAudioAdapter"
178
- />
179
- <template v-if="!agentStore.isAudioChatMode">
180
- <Button
181
- v-if="!agentStore.isResponseInProgress"
182
- class="absolute right-4 bottom-2 !p-0 h-9 w-9 transition-opacity duration-200"
183
- @click="sendMessage"
184
- :disabled="!agentStore.trimmedUserMessage || agentStore.isResponseInProgress"
185
- >
186
- <IconArrowUpOutline
187
- class="w-8 h-8 p-1
188
- text-white"
189
- />
190
- </Button>
191
- <Button
192
- v-else
193
- class="absolute right-4 bottom-2 !p-0 h-9 w-9"
194
- @click="stopCurrentRequest"
195
- >
196
- <div
197
- class="w-3 h-3 bg-white rounded-sm"
198
- />
199
- </Button>
200
- </template>
201
- </div>
202
- </div>
48
+ <ChatFooter
49
+ :meta="props.meta"
50
+ :adminUser="props.adminUser"
51
+ :conversationAreaRef="conversationArea"
52
+ />
203
53
  </div>
204
54
  </div>
205
55
  </div>
@@ -208,17 +58,16 @@
208
58
  </template>
209
59
 
210
60
  <script setup lang="ts">
211
- import { IconChatBubbleLeft20Solid, IconSparklesSolid, IconArrowsPointingOut, IconArrowsPointingIn } from '@iconify-prerendered/vue-heroicons';
212
- import { IconCloseOutline, IconBarsOutline, IconArrowUpOutline, IconCloseSidebarSolid, IconOpenSidebarSolid, IconAngleDownOutline, IconPlusOutline } from '@iconify-prerendered/vue-flowbite';
61
+ import { IconChatBubbleLeft20Solid, IconSparklesSolid } from '@iconify-prerendered/vue-heroicons';
213
62
  import { useTemplateRef, onMounted, ref, onUnmounted } from 'vue';
214
63
  import { onClickOutside } from '@vueuse/core'
215
64
  import ConversationArea from './conversation_area/ConversationArea.vue';
65
+ import ChatHeader from './ChatHeader.vue';
66
+ import ChatFooter from './ChatFooter.vue';
216
67
  import { useAgentStore } from './composables/useAgentStore';
217
68
  import { useAgentTransitions } from './composables/useAgentTransitions';
218
- import { Button } from '@/afcl';
219
69
  import { useCoreStore } from '@/stores/core';
220
70
  import { remToPx } from './utils';
221
- import MicrophoneButton from './speech_recognition_frontend/MicrophoneButon.vue';
222
71
 
223
72
  const props = defineProps<{
224
73
  meta: {
@@ -234,28 +83,20 @@ const props = defineProps<{
234
83
  }>();
235
84
 
236
85
  const chatSurface = useTemplateRef('chatSurface');
237
- const textInput = useTemplateRef('textInput');
238
- const modeMenu = useTemplateRef('modeMenu');
239
86
  const conversationArea = useTemplateRef('conversationArea');
240
87
  const agentStore = useAgentStore();
241
88
  const agentTransitions = useAgentTransitions();
242
89
  const coreStore = useCoreStore();
243
- const isModeMenuOpen = ref(false);
244
90
 
245
91
  const dvh = ref(window.innerHeight)
246
92
 
247
93
  let startX = 0
248
94
  let startWidth = 0
249
95
 
250
- onClickOutside(chatSurface, () => {if (!agentStore.isTeleportedToBody && !agentStore.isFullScreen) agentStore.setIsChatOpen(false);});
251
- onClickOutside(modeMenu, () => { isModeMenuOpen.value = false; });
96
+ onClickOutside(chatSurface as any, () => {if (!agentStore.isTeleportedToBody && !agentStore.isFullScreen) agentStore.setIsChatOpen(false);});
252
97
 
253
98
  onMounted(async () => {
254
- agentStore.setAvailableModes(props.meta.modes, props.meta.defaultModeName);
255
- agentStore.setCurrentGenerationModeFromLocalStorage();
256
- agentStore.regisrerTextInput(textInput.value);
257
99
  window.addEventListener('resize', updateHeight)
258
- textInput.value?.focus();
259
100
  const savedIsTeleportedToBody = agentStore.getLocalStorageItem('isTeleportedToBody');
260
101
  const savedIsTeleportedToBodyBeforeFullScreen = agentStore.getLocalStorageItem('isTeleportedToBodyBeforeFullScreen');
261
102
  let isTeleportedToBodyFromLocalStorage = true;
@@ -306,43 +147,6 @@ const stopResize = () => {
306
147
  }
307
148
  }
308
149
 
309
- function autoResize() {
310
- const el = textInput.value
311
- if (!el) return
312
-
313
- el.style.height = 'auto'
314
- //max-w-48
315
- const maxHeight = 192
316
- if (el.scrollHeight > maxHeight) {
317
- el.style.height = maxHeight + 'px'
318
- el.style.overflowY = 'auto'
319
- } else {
320
- el.style.height = el.scrollHeight + 'px'
321
- el.style.overflowY = 'hidden'
322
- }
323
- }
324
-
325
- function toggleModeMenu() {
326
- isModeMenuOpen.value = !isModeMenuOpen.value;
327
- }
328
-
329
- function selectMode(modeName: string) {
330
- agentStore.setActiveMode(modeName);
331
- isModeMenuOpen.value = false;
332
- }
333
-
334
- async function sendMessage() {
335
- if (agentStore.isAudioChatMode) return;
336
- isModeMenuOpen.value = false;
337
- await agentStore.sendMessage();
338
- autoResize();
339
- conversationArea.value?.handleSendMessage();
340
- }
341
-
342
- function stopCurrentRequest() {
343
- agentStore.abortCurrentChatRequestAndAddSystemMessage();
344
- }
345
-
346
150
  function updateHeight() {
347
151
  dvh.value = Math.round(window.visualViewport?.height || window.innerHeight);
348
152
  }
@@ -271,6 +271,9 @@ export const useAgentStore = defineStore('agent', () => {
271
271
  }
272
272
 
273
273
  async function closeChat() {
274
+ if (!isChatOpen.value) {
275
+ return;
276
+ }
274
277
  await executeOnBeforeChatCloseCallbacks();
275
278
  if(isFullScreen.value) {
276
279
  document.body.style.overflow = '';
@@ -279,7 +282,9 @@ export const useAgentStore = defineStore('agent', () => {
279
282
  return;
280
283
  }
281
284
  isChatOpen.value = false;
282
- setFullScreen(false);
285
+ if (isFullScreen.value) {
286
+ setFullScreen(false);
287
+ }
283
288
  isSessionHistoryOpen.value = false;
284
289
  }
285
290
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adminforth/agent",
3
- "version": "1.43.18",
3
+ "version": "1.43.19",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "type": "module",