@adminforth/agent 1.43.17 → 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 +4 -2
- package/custom/ChatFooter.vue +172 -0
- package/custom/ChatHeader.vue +78 -0
- package/custom/ChatSurface.vue +10 -206
- package/custom/composables/agentAudio/utils.ts +4 -1
- package/custom/composables/useAgentStore.ts +6 -1
- package/dist/custom/ChatFooter.vue +172 -0
- package/dist/custom/ChatHeader.vue +78 -0
- package/dist/custom/ChatSurface.vue +10 -206
- package/dist/custom/composables/agentAudio/utils.ts +4 -1
- package/dist/custom/composables/useAgentStore.ts +6 -1
- package/package.json +1 -1
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,
|
|
64
|
-
total size is 1,661,
|
|
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>
|
package/custom/ChatSurface.vue
CHANGED
|
@@ -36,71 +36,7 @@
|
|
|
36
36
|
height: !agentStore.isIos ? dvh + 'px' : '100dvh',
|
|
37
37
|
}"
|
|
38
38
|
>
|
|
39
|
-
<
|
|
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
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
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
|
|
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
|
}
|
|
@@ -38,7 +38,10 @@ export async function unlockAudio() {
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
export async function startStandByAudio() {
|
|
41
|
-
const standByAudio =
|
|
41
|
+
const standByAudio = new URL(
|
|
42
|
+
'../../public/agentAudio/agent-processing.mp3',
|
|
43
|
+
import.meta.url
|
|
44
|
+
).href;
|
|
42
45
|
const response = await fetch(standByAudio);
|
|
43
46
|
console.log('status', response.status);
|
|
44
47
|
console.log('content-type', response.headers.get('content-type'));
|
|
@@ -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
|
-
|
|
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
|
-
<
|
|
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
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
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
|
|
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
|
}
|
|
@@ -38,7 +38,10 @@ export async function unlockAudio() {
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
export async function startStandByAudio() {
|
|
41
|
-
const standByAudio =
|
|
41
|
+
const standByAudio = new URL(
|
|
42
|
+
'../../public/agentAudio/agent-processing.mp3',
|
|
43
|
+
import.meta.url
|
|
44
|
+
).href;
|
|
42
45
|
const response = await fetch(standByAudio);
|
|
43
46
|
console.log('status', response.status);
|
|
44
47
|
console.log('content-type', response.headers.get('content-type'));
|
|
@@ -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
|
-
|
|
285
|
+
if (isFullScreen.value) {
|
|
286
|
+
setFullScreen(false);
|
|
287
|
+
}
|
|
283
288
|
isSessionHistoryOpen.value = false;
|
|
284
289
|
}
|
|
285
290
|
|