@adminforth/agent 1.11.0 → 1.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build.log +5 -3
- package/custom/ChatSurface.vue +136 -106
- package/custom/ConversationArea.vue +17 -10
- package/custom/SessionsHistory.vue +1 -1
- package/custom/{useAgentStore.ts → composables/useAgentStore.ts} +35 -17
- package/custom/composables/useAgentTransitions.ts +58 -0
- package/dist/custom/ChatSurface.vue +136 -106
- package/dist/custom/ConversationArea.vue +17 -10
- package/dist/custom/SessionsHistory.vue +1 -1
- package/dist/custom/{useAgentStore.ts → composables/useAgentStore.ts} +35 -17
- package/dist/custom/composables/useAgentTransitions.ts +58 -0
- package/package.json +1 -1
package/build.log
CHANGED
|
@@ -15,7 +15,9 @@ custom/package.json
|
|
|
15
15
|
custom/pnpm-lock.yaml
|
|
16
16
|
custom/tsconfig.json
|
|
17
17
|
custom/types.ts
|
|
18
|
-
custom/
|
|
18
|
+
custom/composables/
|
|
19
|
+
custom/composables/useAgentStore.ts
|
|
20
|
+
custom/composables/useAgentTransitions.ts
|
|
19
21
|
custom/incremark_code_renderers/
|
|
20
22
|
custom/incremark_code_renderers/IncremarkShikiCodeBlock.vue
|
|
21
23
|
custom/incremark_code_renderers/incremarkCodeHighlight.ts
|
|
@@ -29,5 +31,5 @@ custom/skills/fetch_data/SKILL.md
|
|
|
29
31
|
custom/skills/mutate_data/
|
|
30
32
|
custom/skills/mutate_data/SKILL.md
|
|
31
33
|
|
|
32
|
-
sent
|
|
33
|
-
total size is
|
|
34
|
+
sent 180,279 bytes received 444 bytes 361,446.00 bytes/sec
|
|
35
|
+
total size is 178,505 speedup is 0.99
|
package/custom/ChatSurface.vue
CHANGED
|
@@ -16,132 +16,163 @@
|
|
|
16
16
|
</div>
|
|
17
17
|
</div>
|
|
18
18
|
|
|
19
|
-
<
|
|
20
|
-
ref="chatSurface"
|
|
21
|
-
class="fixed bg-lightNavbar dark:bg-darkNavbar h-screen top-0 right-0 border-x border-b border-gray-200 dark:border-gray-700
|
|
22
|
-
transition-transform duration-200 ease-in-out
|
|
23
|
-
flex flex z-10"
|
|
24
|
-
:class="[agentStore.isChatOpen ? 'translate-x-0' : 'translate-x-full', !agentStore.isTeleportedToBody ? 'shadow-2xl' : '']"
|
|
25
|
-
:style="{ width: agentStore.chatWidth + 'px' }"
|
|
26
|
-
>
|
|
19
|
+
<Teleport to="body">
|
|
27
20
|
<div
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
21
|
+
ref="chatSurface"
|
|
22
|
+
id="adminforth-agent-chat-surface"
|
|
23
|
+
class="fixed bg-lightNavbar dark:bg-darkNavbar h-screen top-0 right-0 border-x border-b border-gray-200 dark:border-gray-700
|
|
24
|
+
flex flex z-40"
|
|
25
|
+
:class="[agentStore.isChatOpen ? 'translate-x-0' : 'translate-x-full', !agentStore.isTeleportedToBody ? 'shadow-2xl' : '']"
|
|
26
|
+
:style="{ width: agentStore.chatWidth + 'px' }"
|
|
27
|
+
>
|
|
28
|
+
<div
|
|
29
|
+
v-if="!coreStore.isMobile"
|
|
30
|
+
class="w-2 cursor-ew-resize absolute left-0 top-0 h-full z-30"
|
|
31
|
+
@mousedown="startResize"
|
|
32
|
+
></div>
|
|
33
|
+
|
|
34
|
+
<div
|
|
35
|
+
class="w-full h-full flex flex-col"
|
|
36
|
+
:class="{ 'ml-4': agentStore.isFullScreen }"
|
|
37
|
+
>
|
|
38
|
+
<div class="flex items-center justify-between border-b border-gray-200 dark:border-gray-700">
|
|
39
|
+
<div
|
|
40
|
+
class="flex items-center h-[55px]"
|
|
41
|
+
>
|
|
42
|
+
<IconBarsOutline
|
|
43
|
+
class="m-2 w-8 h-8 p-1 cursor-pointer hover:scale-110 rounded transition-colors duration-200
|
|
44
|
+
text-lightNavbarIcons hover:text-lightNavbarIcons/80 hover:bg-lightNavbarIcons/20
|
|
45
|
+
dark:text-darkNavbarIcons hover:text-darkNavbarIcons/80 hover:bg-darkNavbarIcons/20 "
|
|
46
|
+
:class="agentStore.isSessionHistoryOpen ?
|
|
47
|
+
'bg-lightNavbarIcons/20 text-lightNavbarIcons/80 dark:bg-darkNavbarIcons/20 dark:text-darkNavbarIcons/80' :
|
|
48
|
+
''"
|
|
49
|
+
@click="agentStore.setSessionHistoryOpen(!agentStore.isSessionHistoryOpen)"
|
|
50
|
+
/>
|
|
51
|
+
<IconOpenSidebarSolid
|
|
52
|
+
v-if="!agentStore.isTeleportedToBody && !coreStore.isMobile && !agentStore.isFullScreen"
|
|
53
|
+
class="m-2 w-8 h-8 p-1 cursor-pointer hover:scale-110 rounded transition-colors duration-200
|
|
54
|
+
text-lightNavbarIcons hover:text-lightNavbarIcons/80 hover:bg-lightNavbarIcons/20
|
|
55
|
+
dark:text-darkNavbarIcons hover:text-darkNavbarIcons/80 hover:bg-darkNavbarIcons/20 "
|
|
56
|
+
@click="agentStore.setIsTeleportedToBody(true)"
|
|
57
|
+
/>
|
|
58
|
+
<IconCloseSidebarSolid
|
|
59
|
+
v-else-if="!coreStore.isMobile && !agentStore.isFullScreen"
|
|
60
|
+
class="m-2 w-8 h-8 p-1 cursor-pointer hover:scale-110 rounded transition-colors duration-200
|
|
61
|
+
text-lightNavbarIcons hover:text-lightNavbarIcons/80 bg-lightNavbarIcons/20
|
|
62
|
+
dark:bg-darkNavbarIcons/20 dark:text-darkNavbarIcons/80 hover:text-darkNavbarIcons/80 hover:bg-darkNavbarIcons/20 "
|
|
63
|
+
@click="agentStore.setIsTeleportedToBody(false)"
|
|
64
|
+
/>
|
|
65
|
+
<IconArrowsPointingOut
|
|
66
|
+
v-if="!agentStore.isFullScreen && !coreStore.isMobile"
|
|
67
|
+
class="m-2 w-8 h-8 p-1 cursor-pointer hover:scale-110 rounded transition-colors duration-200
|
|
68
|
+
text-lightNavbarIcons hover:text-lightNavbarIcons/80 hover:bg-lightNavbarIcons/20
|
|
69
|
+
dark:text-darkNavbarIcons hover:text-darkNavbarIcons/80 hover:bg-darkNavbarIcons/20 "
|
|
70
|
+
@click="agentStore.setFullScreen(true)"
|
|
71
|
+
/>
|
|
72
|
+
<IconArrowsPointingIn
|
|
73
|
+
v-else-if="!coreStore.isMobile"
|
|
74
|
+
class="m-2 w-8 h-8 p-1 cursor-pointer hover:scale-110 rounded transition-colors duration-200
|
|
75
|
+
text-lightNavbarIcons hover:text-lightNavbarIcons/80 hover:bg-lightNavbarIcons/20
|
|
76
|
+
dark:text-darkNavbarIcons hover:text-darkNavbarIcons/80 hover:bg-darkNavbarIcons/20 "
|
|
77
|
+
:class="agentStore.isFullScreen ?
|
|
78
|
+
'bg-lightNavbarIcons/20 text-lightNavbarIcons/80 dark:bg-darkNavbarIcons/20 dark:text-darkNavbarIcons/80' :
|
|
79
|
+
''"
|
|
80
|
+
@click="agentStore.setFullScreen(false)"
|
|
81
|
+
/>
|
|
82
|
+
</div>
|
|
83
|
+
|
|
84
|
+
<IconCloseOutline
|
|
47
85
|
class="m-2 w-8 h-8 p-1 cursor-pointer hover:scale-110 rounded transition-colors duration-200
|
|
48
86
|
text-lightNavbarIcons hover:text-lightNavbarIcons/80 hover:bg-lightNavbarIcons/20
|
|
49
87
|
dark:text-darkNavbarIcons hover:text-darkNavbarIcons/80 hover:bg-darkNavbarIcons/20 "
|
|
50
|
-
@click="agentStore.
|
|
51
|
-
/>
|
|
52
|
-
<IconCloseSidebarSolid
|
|
53
|
-
v-else-if="!coreStore.isMobile"
|
|
54
|
-
class="m-2 w-8 h-8 p-1 cursor-pointer hover:scale-110 rounded transition-colors duration-200
|
|
55
|
-
text-lightNavbarIcons hover:text-lightNavbarIcons/80 bg-lightNavbarIcons/20
|
|
56
|
-
dark:bg-darkNavbarIcons/20 dark:text-darkNavbarIcons/80 hover:text-darkNavbarIcons/80 hover:bg-darkNavbarIcons/20 "
|
|
57
|
-
@click="agentStore.setIsTeleportedToBody(false)"
|
|
88
|
+
@click="agentStore.setIsChatOpen(false)"
|
|
58
89
|
/>
|
|
59
|
-
</div>
|
|
60
|
-
|
|
61
|
-
<IconCloseOutline
|
|
62
|
-
class="m-2 w-8 h-8 p-1 cursor-pointer hover:scale-110 rounded transition-colors duration-200
|
|
63
|
-
text-lightNavbarIcons hover:text-lightNavbarIcons/80 hover:bg-lightNavbarIcons/20
|
|
64
|
-
dark:text-darkNavbarIcons hover:text-darkNavbarIcons/80 hover:bg-darkNavbarIcons/20 "
|
|
65
|
-
@click="agentStore.setIsChatOpen(false)"
|
|
66
|
-
/>
|
|
67
90
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
<div class="w-full mb-2 flex items-center justify-center px-2 bg-transparent relative">
|
|
77
|
-
<textarea
|
|
78
|
-
v-model="agentStore.userMessageInput"
|
|
79
|
-
ref="textInput"
|
|
80
|
-
@input="autoResize"
|
|
81
|
-
:class="[
|
|
82
|
-
'min-h-12 w-full resize-none overflow-hidden border 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',
|
|
83
|
-
agentStore.availableModes.length > 1 ? 'p-4 pr-12 pb-12' : 'p-4 pr-12',
|
|
84
|
-
]"
|
|
85
|
-
placeholder="Type a message..."
|
|
86
|
-
@keydown.enter.exact.prevent="sendMessage"
|
|
91
|
+
</div>
|
|
92
|
+
<div
|
|
93
|
+
class="relative flex-1 flex flex-col overflow-hidden"
|
|
94
|
+
>
|
|
95
|
+
<ConversationArea
|
|
96
|
+
v-if="agentStore.isChatOpen"
|
|
97
|
+
class="flex-1 overflow-auto"
|
|
98
|
+
:messages="agentStore.chatMessages"
|
|
87
99
|
/>
|
|
88
|
-
<div
|
|
89
|
-
v-if="agentStore.availableModes.length > 1"
|
|
90
|
-
ref="modeMenu"
|
|
91
|
-
class="absolute bottom-2 left-4"
|
|
92
|
-
>
|
|
93
|
-
<button
|
|
94
|
-
aria-label="Select mode"
|
|
95
|
-
class="flex h-8 w-8 items-center justify-center rounded-md border border-gray-200 bg-white text-lightNavbarIcons transition-colors duration-200 hover:bg-gray-100 dark:border-gray-600 dark:bg-gray-800 dark:text-darkNavbarIcons dark:hover:bg-gray-700"
|
|
96
|
-
:class="isModeMenuOpen ? 'bg-gray-100 dark:bg-gray-700' : ''"
|
|
97
|
-
:disabled="agentStore.isResponseInProgress"
|
|
98
|
-
title="Select mode"
|
|
99
|
-
type="button"
|
|
100
|
-
@click="toggleModeMenu"
|
|
101
|
-
>
|
|
102
|
-
<IconBrainOutline class="h-4 w-4" />
|
|
103
|
-
</button>
|
|
104
100
|
|
|
101
|
+
<div
|
|
102
|
+
class="w-full mb-2 flex items-center justify-center px-2 bg-transparent relative"
|
|
103
|
+
:class="agentStore.isFullScreen ? 'mx-auto' : ''"
|
|
104
|
+
:style="{ maxWidth: agentStore.isFullScreen ? agentStore.MAX_WIDTH+'px' : '100%' }"
|
|
105
|
+
>
|
|
106
|
+
<textarea
|
|
107
|
+
v-model="agentStore.userMessageInput"
|
|
108
|
+
ref="textInput"
|
|
109
|
+
@input="autoResize"
|
|
110
|
+
:class="[
|
|
111
|
+
'min-h-12 w-full resize-none overflow-hidden border 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',
|
|
112
|
+
agentStore.availableModes.length > 1 ? 'p-4 pr-12 pb-12' : 'p-4 pr-12',
|
|
113
|
+
]"
|
|
114
|
+
placeholder="Type a message..."
|
|
115
|
+
@keydown.enter.exact.prevent="sendMessage"
|
|
116
|
+
/>
|
|
105
117
|
<div
|
|
106
|
-
v-if="
|
|
107
|
-
|
|
118
|
+
v-if="agentStore.availableModes.length > 1"
|
|
119
|
+
ref="modeMenu"
|
|
120
|
+
class="absolute bottom-2 left-4"
|
|
108
121
|
>
|
|
109
122
|
<button
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
class="
|
|
113
|
-
:
|
|
123
|
+
aria-label="Select mode"
|
|
124
|
+
class="flex h-8 w-8 items-center justify-center rounded-md border border-gray-200 bg-white text-lightNavbarIcons transition-colors duration-200 hover:bg-gray-100 dark:border-gray-600 dark:bg-gray-800 dark:text-darkNavbarIcons dark:hover:bg-gray-700"
|
|
125
|
+
:class="isModeMenuOpen ? 'bg-gray-100 dark:bg-gray-700' : ''"
|
|
126
|
+
:disabled="agentStore.isResponseInProgress"
|
|
127
|
+
title="Select mode"
|
|
114
128
|
type="button"
|
|
115
|
-
@click="
|
|
129
|
+
@click="toggleModeMenu"
|
|
116
130
|
>
|
|
117
|
-
|
|
131
|
+
<IconBrainOutline class="h-4 w-4" />
|
|
118
132
|
</button>
|
|
133
|
+
|
|
134
|
+
<div
|
|
135
|
+
v-if="isModeMenuOpen"
|
|
136
|
+
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"
|
|
137
|
+
>
|
|
138
|
+
<button
|
|
139
|
+
v-for="mode in agentStore.availableModes"
|
|
140
|
+
:key="mode.name"
|
|
141
|
+
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"
|
|
142
|
+
:class="mode.name === agentStore.activeModeName ? 'bg-gray-100 dark:bg-gray-700' : ''"
|
|
143
|
+
type="button"
|
|
144
|
+
@click="selectMode(mode.name)"
|
|
145
|
+
>
|
|
146
|
+
{{ mode.name }}
|
|
147
|
+
</button>
|
|
148
|
+
</div>
|
|
119
149
|
</div>
|
|
150
|
+
<Button
|
|
151
|
+
class="absolute right-4 bottom-2 !p-0 h-[34px] w-[34px]"
|
|
152
|
+
@click="sendMessage"
|
|
153
|
+
:disabled="!agentStore.trimmedUserMessage || agentStore.isResponseInProgress"
|
|
154
|
+
>
|
|
155
|
+
<IconArrowUpOutline
|
|
156
|
+
class="w-8 h-8 p-1
|
|
157
|
+
text-white"
|
|
158
|
+
/>
|
|
159
|
+
</Button>
|
|
120
160
|
</div>
|
|
121
|
-
<Button
|
|
122
|
-
class="absolute right-4 bottom-2 !p-0 h-[34px] w-[34px]"
|
|
123
|
-
@click="sendMessage"
|
|
124
|
-
:disabled="!agentStore.trimmedUserMessage || agentStore.isResponseInProgress"
|
|
125
|
-
>
|
|
126
|
-
<IconArrowUpOutline
|
|
127
|
-
class="w-8 h-8 p-1
|
|
128
|
-
text-white"
|
|
129
|
-
/>
|
|
130
|
-
</Button>
|
|
131
161
|
</div>
|
|
132
162
|
</div>
|
|
133
163
|
</div>
|
|
134
|
-
</
|
|
164
|
+
</Teleport>
|
|
135
165
|
|
|
136
166
|
</template>
|
|
137
167
|
|
|
138
168
|
<script setup lang="ts">
|
|
139
|
-
import { IconChatBubbleLeft20Solid, IconSparklesSolid } from '@iconify-prerendered/vue-heroicons';
|
|
169
|
+
import { IconChatBubbleLeft20Solid, IconSparklesSolid, IconArrowsPointingOut, IconArrowsPointingIn } from '@iconify-prerendered/vue-heroicons';
|
|
140
170
|
import { IconCloseOutline, IconBarsOutline, IconArrowUpOutline, IconCloseSidebarSolid, IconOpenSidebarSolid, IconBrainOutline } from '@iconify-prerendered/vue-flowbite';
|
|
141
|
-
import { useTemplateRef, onMounted, ref } from 'vue';
|
|
171
|
+
import { useTemplateRef, onMounted, ref,computed } from 'vue';
|
|
142
172
|
import { onClickOutside } from '@vueuse/core'
|
|
143
173
|
import ConversationArea from './ConversationArea.vue';
|
|
144
|
-
import { useAgentStore } from './useAgentStore';
|
|
174
|
+
import { useAgentStore } from './composables/useAgentStore';
|
|
175
|
+
import { useAgentTransitions } from './composables/useAgentTransitions';
|
|
145
176
|
import { Button } from '@/afcl';
|
|
146
177
|
import { useCoreStore } from '@/stores/core';
|
|
147
178
|
|
|
@@ -159,12 +190,10 @@ const chatSurface = useTemplateRef('chatSurface');
|
|
|
159
190
|
const textInput = useTemplateRef('textInput');
|
|
160
191
|
const modeMenu = useTemplateRef('modeMenu');
|
|
161
192
|
const agentStore = useAgentStore();
|
|
193
|
+
const agentTransitions = useAgentTransitions();
|
|
162
194
|
const coreStore = useCoreStore();
|
|
163
195
|
const isModeMenuOpen = ref(false);
|
|
164
196
|
|
|
165
|
-
const MAX_WIDTH = 800;
|
|
166
|
-
const MIN_WIDTH = 382; //w-96
|
|
167
|
-
|
|
168
197
|
let startX = 0
|
|
169
198
|
let startWidth = 0
|
|
170
199
|
|
|
@@ -181,7 +210,8 @@ const startResize = (e: MouseEvent) => {
|
|
|
181
210
|
|
|
182
211
|
const onResize = (e: MouseEvent) => {
|
|
183
212
|
const dx = startX - e.clientX
|
|
184
|
-
agentStore.setChatWidth(Math.min(Math.max(startWidth + dx, MIN_WIDTH), MAX_WIDTH))
|
|
213
|
+
agentStore.setChatWidth(Math.min(Math.max(startWidth + dx, agentStore.MIN_WIDTH), agentStore.MAX_WIDTH))
|
|
214
|
+
agentTransitions.setChatSurfaceTransition(true);
|
|
185
215
|
}
|
|
186
216
|
|
|
187
217
|
const stopResize = () => {
|
|
@@ -194,8 +224,8 @@ const stopResize = () => {
|
|
|
194
224
|
const appRoot = document.getElementById('app');
|
|
195
225
|
const header = document.getElementById('af-header-nav');
|
|
196
226
|
if (appRoot && header) {
|
|
197
|
-
|
|
198
|
-
|
|
227
|
+
agentTransitions.setAppRootTransition(false);
|
|
228
|
+
agentTransitions.setChatSurfaceTransition(false);
|
|
199
229
|
}
|
|
200
230
|
}
|
|
201
231
|
|
|
@@ -1,11 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
|
|
3
|
-
<IconArrowDownOutline
|
|
4
|
-
class="absolute bottom-32 right-4 bg-lightPrimary dark:bg-darkPrimary text-white p-2 w-10 h-10 rounded-full transition-opacity duration-100 ease-in"
|
|
5
|
-
:class="showScrollToBottomButton ? 'opacity-100' : 'opacity-0 pointer-events-none'"
|
|
6
|
-
:disabled="!showScrollToBottomButton"
|
|
7
|
-
/>
|
|
8
|
-
</button>
|
|
2
|
+
|
|
9
3
|
<SessionsHistory
|
|
10
4
|
:class="agentStore.isSessionHistoryOpen ? 'translate-x-0' : '-translate-x-full'"
|
|
11
5
|
/>
|
|
@@ -18,12 +12,24 @@
|
|
|
18
12
|
</div>
|
|
19
13
|
<AutoScrollContainer
|
|
20
14
|
:enabled="!showScrollToBottomButton"
|
|
21
|
-
class="flex flex-col overflow-y-auto
|
|
15
|
+
class="relative flex flex-col overflow-y-auto"
|
|
22
16
|
ref="scrollContainer"
|
|
23
17
|
:threshold="10"
|
|
24
18
|
behavior="smooth"
|
|
19
|
+
:class="agentStore.isFullScreen ? 'mx-auto' : ''"
|
|
20
|
+
:style="{
|
|
21
|
+
maxWidth: agentStore.isFullScreen ? agentStore.MAX_WIDTH+'px' : '100%',
|
|
22
|
+
transition: `max-width ${agentTransitions.TRANSITION_DURATION}ms ease-in-out`
|
|
23
|
+
}"
|
|
25
24
|
>
|
|
26
25
|
|
|
26
|
+
<button @click="scrollContainer.scrollToBottom()">
|
|
27
|
+
<IconArrowDownOutline
|
|
28
|
+
class="fixed bottom-32 left-1/2 bg-lightPrimary dark:bg-darkPrimary text-white p-2 w-10 h-10 rounded-full transition-opacity duration-100 ease-in"
|
|
29
|
+
:class="showScrollToBottomButton ? 'opacity-100' : 'opacity-0 pointer-events-none'"
|
|
30
|
+
:disabled="!showScrollToBottomButton"
|
|
31
|
+
/>
|
|
32
|
+
</button>
|
|
27
33
|
<div
|
|
28
34
|
v-for="message in props.messages" :key="message.id"
|
|
29
35
|
class="flex flex-col w-full"
|
|
@@ -71,15 +77,16 @@ import type { IMessage, IPart } from './types';
|
|
|
71
77
|
import { useTemplateRef, ref, defineAsyncComponent, onMounted, watch, computed } from 'vue';
|
|
72
78
|
import { IconArrowDownOutline } from '@iconify-prerendered/vue-flowbite';
|
|
73
79
|
import SessionsHistory from './SessionsHistory.vue';
|
|
74
|
-
import { useAgentStore } from './useAgentStore';
|
|
75
|
-
import ToolRenderer from './ToolRenderer.vue';
|
|
80
|
+
import { useAgentStore } from './composables/useAgentStore';
|
|
76
81
|
import ToolsGroup from './ToolsGroup.vue';
|
|
82
|
+
import { useAgentTransitions } from './composables/useAgentTransitions';
|
|
77
83
|
|
|
78
84
|
const scrollContainer = useTemplateRef('scrollContainer');
|
|
79
85
|
const showScrollToBottomButton = ref(false);
|
|
80
86
|
const innerScrollContainerRef = ref(null);
|
|
81
87
|
const AutoScrollContainer = defineAsyncComponent(() => import('@incremark/vue').then(module => module.AutoScrollContainer))
|
|
82
88
|
const agentStore = useAgentStore();
|
|
89
|
+
const agentTransitions = useAgentTransitions();
|
|
83
90
|
const clicks = ref(0);
|
|
84
91
|
|
|
85
92
|
function recalculateScroll() {
|
|
@@ -49,7 +49,7 @@ import { Button, Spinner } from '@/afcl'
|
|
|
49
49
|
import { computed } from 'vue';
|
|
50
50
|
import { IconPlusOutline } from '@iconify-prerendered/vue-flowbite';
|
|
51
51
|
import type { ISessionsListItem } from './types';
|
|
52
|
-
import { useAgentStore } from './useAgentStore';
|
|
52
|
+
import { useAgentStore } from './composables/useAgentStore';
|
|
53
53
|
|
|
54
54
|
const agentStore = useAgentStore();
|
|
55
55
|
|
|
@@ -1,17 +1,23 @@
|
|
|
1
1
|
import { defineStore } from 'pinia';
|
|
2
|
-
import { IAgentSession, ISessionsListItem, IMessage } from '
|
|
2
|
+
import { IAgentSession, ISessionsListItem, IMessage } from '../types';
|
|
3
3
|
import { ref, nextTick, computed, watch, onMounted, shallowRef } from 'vue';
|
|
4
4
|
import { callAdminForthApi } from '@/utils';
|
|
5
5
|
import { useAdminforth } from '@/adminforth';
|
|
6
|
-
import { Chat } from '
|
|
7
|
-
import {
|
|
6
|
+
import { Chat } from '../chat';
|
|
7
|
+
import { DefaultChatTransport } from 'ai';
|
|
8
8
|
import { useCoreStore } from '@/stores/core';
|
|
9
|
+
import { useAgentTransitions } from './useAgentTransitions';
|
|
9
10
|
|
|
10
11
|
type AgentMode = {
|
|
11
12
|
name: string;
|
|
12
13
|
};
|
|
13
14
|
|
|
14
15
|
export const useAgentStore = defineStore('agent', () => {
|
|
16
|
+
const DEFAULT_CHAT_WIDTH = 600;
|
|
17
|
+
const MAX_WIDTH = 800;
|
|
18
|
+
const MIN_WIDTH = 382; //w-96
|
|
19
|
+
const agentTransitions = useAgentTransitions();
|
|
20
|
+
|
|
15
21
|
const activeSessionId = ref<string | null>(null);
|
|
16
22
|
const currentSession = ref<IAgentSession | null>(null);
|
|
17
23
|
const sessionList = ref<ISessionsListItem[]>([]);
|
|
@@ -31,7 +37,7 @@ export const useAgentStore = defineStore('agent', () => {
|
|
|
31
37
|
const appRoot = ref<HTMLElement | null>(null);
|
|
32
38
|
const header = ref<HTMLElement | null>(null);
|
|
33
39
|
const lastSessionId = ref<string | null>(null);
|
|
34
|
-
const chatWidth = ref(
|
|
40
|
+
const chatWidth = ref(DEFAULT_CHAT_WIDTH);
|
|
35
41
|
const availableModes = ref<AgentMode[]>([]);
|
|
36
42
|
const activeModeName = ref<string | null>(null);
|
|
37
43
|
function setLocalStorageItem(key: string, value: string) {
|
|
@@ -55,7 +61,12 @@ export const useAgentStore = defineStore('agent', () => {
|
|
|
55
61
|
}
|
|
56
62
|
})
|
|
57
63
|
onMounted(() => {
|
|
58
|
-
|
|
64
|
+
const chatWidthBeforeFullScreen = parseInt(getLocalStorageItem('chatWidthBeforeFullScreen') || '0', 10);
|
|
65
|
+
if (chatWidthBeforeFullScreen) {
|
|
66
|
+
chatWidth.value = chatWidthBeforeFullScreen;
|
|
67
|
+
} else {
|
|
68
|
+
chatWidth.value = parseInt(getLocalStorageItem('chatWidth') || DEFAULT_CHAT_WIDTH.toString(), 10);
|
|
69
|
+
}
|
|
59
70
|
isTeleportedToBody.value = getLocalStorageItem('isTeleportedToBody') === 'true';
|
|
60
71
|
lastSessionId.value = getLocalStorageItem('lastSessionId');
|
|
61
72
|
if (lastSessionId.value && lastSessionId.value !== 'pre-session') {
|
|
@@ -71,29 +82,33 @@ export const useAgentStore = defineStore('agent', () => {
|
|
|
71
82
|
header.value = document.getElementById('af-header-nav');
|
|
72
83
|
if (appRoot.value && header.value) {
|
|
73
84
|
nextTick(() => {
|
|
74
|
-
|
|
75
|
-
header.value.style.transition = 'padding-right 200ms ease-in-out';
|
|
85
|
+
agentTransitions.setAppRootTransition(false);
|
|
76
86
|
});
|
|
77
87
|
}
|
|
78
88
|
})
|
|
89
|
+
|
|
79
90
|
const isFullScreen = ref(false);
|
|
80
91
|
function setFullScreen(fullScreen: boolean) {
|
|
81
92
|
isFullScreen.value = fullScreen;
|
|
82
|
-
console.log('setFullScreen', fullScreen);
|
|
83
93
|
if (fullScreen) {
|
|
84
|
-
|
|
94
|
+
setLocalStorageItem('chatWidthBeforeFullScreen', chatWidth.value.toString());
|
|
95
|
+
setLocalStorageItem('isTeleportedToBodyBeforeFullScreen', isTeleportedToBody.value ? 'true' : 'false');
|
|
96
|
+
setIsTeleportedToBody(false);
|
|
97
|
+
useAgentTransitions().setChatSurfaceTransition(false);
|
|
98
|
+
setChatWidth(window.innerWidth, false);
|
|
85
99
|
} else {
|
|
86
|
-
|
|
87
|
-
|
|
100
|
+
const lastChatWidth = parseInt(getLocalStorageItem('chatWidthBeforeFullScreen') || DEFAULT_CHAT_WIDTH.toString(), 10);
|
|
101
|
+
const isTeleportedBeforeFullScreen = getLocalStorageItem('isTeleportedToBodyBeforeFullScreen') === 'true';
|
|
102
|
+
agentTransitions.setAppRootTransition(true);
|
|
103
|
+
setIsTeleportedToBody(isTeleportedBeforeFullScreen);
|
|
104
|
+
setChatWidth(lastChatWidth, false);
|
|
105
|
+
setTimeout(() => agentTransitions.setAppRootTransition(false), agentTransitions.TRANSITION_DURATION);
|
|
88
106
|
}
|
|
89
107
|
}
|
|
90
108
|
|
|
91
|
-
function setChatWidth(width: number, blockTransition =
|
|
92
|
-
if (
|
|
93
|
-
|
|
94
|
-
appRoot.value.style.transition = '';
|
|
95
|
-
header.value.style.transition = '';
|
|
96
|
-
}
|
|
109
|
+
function setChatWidth(width: number, blockTransition = true) {
|
|
110
|
+
if (blockTransition) {
|
|
111
|
+
agentTransitions.setAppRootTransition(true);
|
|
97
112
|
}
|
|
98
113
|
chatWidth.value = width;
|
|
99
114
|
|
|
@@ -414,5 +429,8 @@ export const useAgentStore = defineStore('agent', () => {
|
|
|
414
429
|
activeModeName,
|
|
415
430
|
setAvailableModes,
|
|
416
431
|
setActiveMode,
|
|
432
|
+
DEFAULT_CHAT_WIDTH,
|
|
433
|
+
MAX_WIDTH,
|
|
434
|
+
MIN_WIDTH,
|
|
417
435
|
}
|
|
418
436
|
})
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { defineStore } from 'pinia';
|
|
2
|
+
import { ref, nextTick,onMounted } from 'vue';
|
|
3
|
+
import { useAgentStore } from './useAgentStore';
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
export const useAgentTransitions = defineStore('agentTransitions', () => {
|
|
7
|
+
const TRANSITION_DURATION = 200;
|
|
8
|
+
|
|
9
|
+
const agentStore = useAgentStore();
|
|
10
|
+
const appRoot = ref<HTMLElement | null>(null);
|
|
11
|
+
const header = ref<HTMLElement | null>(null);
|
|
12
|
+
|
|
13
|
+
const chatRoot = ref<HTMLElement | null>(null);
|
|
14
|
+
|
|
15
|
+
onMounted(() => {
|
|
16
|
+
appRoot.value = document.getElementById('app');
|
|
17
|
+
header.value = document.getElementById('af-header-nav');
|
|
18
|
+
chatRoot.value = document.getElementById('adminforth-agent-chat-surface');
|
|
19
|
+
if (appRoot.value && header.value) {
|
|
20
|
+
nextTick(() => {
|
|
21
|
+
setAppRootTransition(false);
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
if (chatRoot.value) {
|
|
25
|
+
nextTick(() => {
|
|
26
|
+
setChatSurfaceTransition(false);
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
function setAppRootTransition(blockTransition = true) {
|
|
31
|
+
if (appRoot.value && header.value) {
|
|
32
|
+
if (blockTransition) {
|
|
33
|
+
appRoot.value.style.transition = '';
|
|
34
|
+
header.value.style.transition = '';
|
|
35
|
+
} else {
|
|
36
|
+
appRoot.value.style.transition = `padding-right ${TRANSITION_DURATION}ms ease-in-out`;
|
|
37
|
+
header.value.style.transition = `padding-right ${TRANSITION_DURATION}ms ease-in-out`;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function setChatSurfaceTransition(blockTransition = true) {
|
|
42
|
+
if (chatRoot.value) {
|
|
43
|
+
if (blockTransition) {
|
|
44
|
+
chatRoot.value.style.transition = '';
|
|
45
|
+
} else {
|
|
46
|
+
chatRoot.value.style.transition = `
|
|
47
|
+
transform ${TRANSITION_DURATION}ms ease-in-out,
|
|
48
|
+
width ${TRANSITION_DURATION}ms ease-in-out
|
|
49
|
+
`;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return {
|
|
54
|
+
TRANSITION_DURATION,
|
|
55
|
+
setAppRootTransition,
|
|
56
|
+
setChatSurfaceTransition
|
|
57
|
+
}
|
|
58
|
+
});
|
|
@@ -16,132 +16,163 @@
|
|
|
16
16
|
</div>
|
|
17
17
|
</div>
|
|
18
18
|
|
|
19
|
-
<
|
|
20
|
-
ref="chatSurface"
|
|
21
|
-
class="fixed bg-lightNavbar dark:bg-darkNavbar h-screen top-0 right-0 border-x border-b border-gray-200 dark:border-gray-700
|
|
22
|
-
transition-transform duration-200 ease-in-out
|
|
23
|
-
flex flex z-10"
|
|
24
|
-
:class="[agentStore.isChatOpen ? 'translate-x-0' : 'translate-x-full', !agentStore.isTeleportedToBody ? 'shadow-2xl' : '']"
|
|
25
|
-
:style="{ width: agentStore.chatWidth + 'px' }"
|
|
26
|
-
>
|
|
19
|
+
<Teleport to="body">
|
|
27
20
|
<div
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
21
|
+
ref="chatSurface"
|
|
22
|
+
id="adminforth-agent-chat-surface"
|
|
23
|
+
class="fixed bg-lightNavbar dark:bg-darkNavbar h-screen top-0 right-0 border-x border-b border-gray-200 dark:border-gray-700
|
|
24
|
+
flex flex z-40"
|
|
25
|
+
:class="[agentStore.isChatOpen ? 'translate-x-0' : 'translate-x-full', !agentStore.isTeleportedToBody ? 'shadow-2xl' : '']"
|
|
26
|
+
:style="{ width: agentStore.chatWidth + 'px' }"
|
|
27
|
+
>
|
|
28
|
+
<div
|
|
29
|
+
v-if="!coreStore.isMobile"
|
|
30
|
+
class="w-2 cursor-ew-resize absolute left-0 top-0 h-full z-30"
|
|
31
|
+
@mousedown="startResize"
|
|
32
|
+
></div>
|
|
33
|
+
|
|
34
|
+
<div
|
|
35
|
+
class="w-full h-full flex flex-col"
|
|
36
|
+
:class="{ 'ml-4': agentStore.isFullScreen }"
|
|
37
|
+
>
|
|
38
|
+
<div class="flex items-center justify-between border-b border-gray-200 dark:border-gray-700">
|
|
39
|
+
<div
|
|
40
|
+
class="flex items-center h-[55px]"
|
|
41
|
+
>
|
|
42
|
+
<IconBarsOutline
|
|
43
|
+
class="m-2 w-8 h-8 p-1 cursor-pointer hover:scale-110 rounded transition-colors duration-200
|
|
44
|
+
text-lightNavbarIcons hover:text-lightNavbarIcons/80 hover:bg-lightNavbarIcons/20
|
|
45
|
+
dark:text-darkNavbarIcons hover:text-darkNavbarIcons/80 hover:bg-darkNavbarIcons/20 "
|
|
46
|
+
:class="agentStore.isSessionHistoryOpen ?
|
|
47
|
+
'bg-lightNavbarIcons/20 text-lightNavbarIcons/80 dark:bg-darkNavbarIcons/20 dark:text-darkNavbarIcons/80' :
|
|
48
|
+
''"
|
|
49
|
+
@click="agentStore.setSessionHistoryOpen(!agentStore.isSessionHistoryOpen)"
|
|
50
|
+
/>
|
|
51
|
+
<IconOpenSidebarSolid
|
|
52
|
+
v-if="!agentStore.isTeleportedToBody && !coreStore.isMobile && !agentStore.isFullScreen"
|
|
53
|
+
class="m-2 w-8 h-8 p-1 cursor-pointer hover:scale-110 rounded transition-colors duration-200
|
|
54
|
+
text-lightNavbarIcons hover:text-lightNavbarIcons/80 hover:bg-lightNavbarIcons/20
|
|
55
|
+
dark:text-darkNavbarIcons hover:text-darkNavbarIcons/80 hover:bg-darkNavbarIcons/20 "
|
|
56
|
+
@click="agentStore.setIsTeleportedToBody(true)"
|
|
57
|
+
/>
|
|
58
|
+
<IconCloseSidebarSolid
|
|
59
|
+
v-else-if="!coreStore.isMobile && !agentStore.isFullScreen"
|
|
60
|
+
class="m-2 w-8 h-8 p-1 cursor-pointer hover:scale-110 rounded transition-colors duration-200
|
|
61
|
+
text-lightNavbarIcons hover:text-lightNavbarIcons/80 bg-lightNavbarIcons/20
|
|
62
|
+
dark:bg-darkNavbarIcons/20 dark:text-darkNavbarIcons/80 hover:text-darkNavbarIcons/80 hover:bg-darkNavbarIcons/20 "
|
|
63
|
+
@click="agentStore.setIsTeleportedToBody(false)"
|
|
64
|
+
/>
|
|
65
|
+
<IconArrowsPointingOut
|
|
66
|
+
v-if="!agentStore.isFullScreen && !coreStore.isMobile"
|
|
67
|
+
class="m-2 w-8 h-8 p-1 cursor-pointer hover:scale-110 rounded transition-colors duration-200
|
|
68
|
+
text-lightNavbarIcons hover:text-lightNavbarIcons/80 hover:bg-lightNavbarIcons/20
|
|
69
|
+
dark:text-darkNavbarIcons hover:text-darkNavbarIcons/80 hover:bg-darkNavbarIcons/20 "
|
|
70
|
+
@click="agentStore.setFullScreen(true)"
|
|
71
|
+
/>
|
|
72
|
+
<IconArrowsPointingIn
|
|
73
|
+
v-else-if="!coreStore.isMobile"
|
|
74
|
+
class="m-2 w-8 h-8 p-1 cursor-pointer hover:scale-110 rounded transition-colors duration-200
|
|
75
|
+
text-lightNavbarIcons hover:text-lightNavbarIcons/80 hover:bg-lightNavbarIcons/20
|
|
76
|
+
dark:text-darkNavbarIcons hover:text-darkNavbarIcons/80 hover:bg-darkNavbarIcons/20 "
|
|
77
|
+
:class="agentStore.isFullScreen ?
|
|
78
|
+
'bg-lightNavbarIcons/20 text-lightNavbarIcons/80 dark:bg-darkNavbarIcons/20 dark:text-darkNavbarIcons/80' :
|
|
79
|
+
''"
|
|
80
|
+
@click="agentStore.setFullScreen(false)"
|
|
81
|
+
/>
|
|
82
|
+
</div>
|
|
83
|
+
|
|
84
|
+
<IconCloseOutline
|
|
47
85
|
class="m-2 w-8 h-8 p-1 cursor-pointer hover:scale-110 rounded transition-colors duration-200
|
|
48
86
|
text-lightNavbarIcons hover:text-lightNavbarIcons/80 hover:bg-lightNavbarIcons/20
|
|
49
87
|
dark:text-darkNavbarIcons hover:text-darkNavbarIcons/80 hover:bg-darkNavbarIcons/20 "
|
|
50
|
-
@click="agentStore.
|
|
51
|
-
/>
|
|
52
|
-
<IconCloseSidebarSolid
|
|
53
|
-
v-else-if="!coreStore.isMobile"
|
|
54
|
-
class="m-2 w-8 h-8 p-1 cursor-pointer hover:scale-110 rounded transition-colors duration-200
|
|
55
|
-
text-lightNavbarIcons hover:text-lightNavbarIcons/80 bg-lightNavbarIcons/20
|
|
56
|
-
dark:bg-darkNavbarIcons/20 dark:text-darkNavbarIcons/80 hover:text-darkNavbarIcons/80 hover:bg-darkNavbarIcons/20 "
|
|
57
|
-
@click="agentStore.setIsTeleportedToBody(false)"
|
|
88
|
+
@click="agentStore.setIsChatOpen(false)"
|
|
58
89
|
/>
|
|
59
|
-
</div>
|
|
60
|
-
|
|
61
|
-
<IconCloseOutline
|
|
62
|
-
class="m-2 w-8 h-8 p-1 cursor-pointer hover:scale-110 rounded transition-colors duration-200
|
|
63
|
-
text-lightNavbarIcons hover:text-lightNavbarIcons/80 hover:bg-lightNavbarIcons/20
|
|
64
|
-
dark:text-darkNavbarIcons hover:text-darkNavbarIcons/80 hover:bg-darkNavbarIcons/20 "
|
|
65
|
-
@click="agentStore.setIsChatOpen(false)"
|
|
66
|
-
/>
|
|
67
90
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
<div class="w-full mb-2 flex items-center justify-center px-2 bg-transparent relative">
|
|
77
|
-
<textarea
|
|
78
|
-
v-model="agentStore.userMessageInput"
|
|
79
|
-
ref="textInput"
|
|
80
|
-
@input="autoResize"
|
|
81
|
-
:class="[
|
|
82
|
-
'min-h-12 w-full resize-none overflow-hidden border 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',
|
|
83
|
-
agentStore.availableModes.length > 1 ? 'p-4 pr-12 pb-12' : 'p-4 pr-12',
|
|
84
|
-
]"
|
|
85
|
-
placeholder="Type a message..."
|
|
86
|
-
@keydown.enter.exact.prevent="sendMessage"
|
|
91
|
+
</div>
|
|
92
|
+
<div
|
|
93
|
+
class="relative flex-1 flex flex-col overflow-hidden"
|
|
94
|
+
>
|
|
95
|
+
<ConversationArea
|
|
96
|
+
v-if="agentStore.isChatOpen"
|
|
97
|
+
class="flex-1 overflow-auto"
|
|
98
|
+
:messages="agentStore.chatMessages"
|
|
87
99
|
/>
|
|
88
|
-
<div
|
|
89
|
-
v-if="agentStore.availableModes.length > 1"
|
|
90
|
-
ref="modeMenu"
|
|
91
|
-
class="absolute bottom-2 left-4"
|
|
92
|
-
>
|
|
93
|
-
<button
|
|
94
|
-
aria-label="Select mode"
|
|
95
|
-
class="flex h-8 w-8 items-center justify-center rounded-md border border-gray-200 bg-white text-lightNavbarIcons transition-colors duration-200 hover:bg-gray-100 dark:border-gray-600 dark:bg-gray-800 dark:text-darkNavbarIcons dark:hover:bg-gray-700"
|
|
96
|
-
:class="isModeMenuOpen ? 'bg-gray-100 dark:bg-gray-700' : ''"
|
|
97
|
-
:disabled="agentStore.isResponseInProgress"
|
|
98
|
-
title="Select mode"
|
|
99
|
-
type="button"
|
|
100
|
-
@click="toggleModeMenu"
|
|
101
|
-
>
|
|
102
|
-
<IconBrainOutline class="h-4 w-4" />
|
|
103
|
-
</button>
|
|
104
100
|
|
|
101
|
+
<div
|
|
102
|
+
class="w-full mb-2 flex items-center justify-center px-2 bg-transparent relative"
|
|
103
|
+
:class="agentStore.isFullScreen ? 'mx-auto' : ''"
|
|
104
|
+
:style="{ maxWidth: agentStore.isFullScreen ? agentStore.MAX_WIDTH+'px' : '100%' }"
|
|
105
|
+
>
|
|
106
|
+
<textarea
|
|
107
|
+
v-model="agentStore.userMessageInput"
|
|
108
|
+
ref="textInput"
|
|
109
|
+
@input="autoResize"
|
|
110
|
+
:class="[
|
|
111
|
+
'min-h-12 w-full resize-none overflow-hidden border 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',
|
|
112
|
+
agentStore.availableModes.length > 1 ? 'p-4 pr-12 pb-12' : 'p-4 pr-12',
|
|
113
|
+
]"
|
|
114
|
+
placeholder="Type a message..."
|
|
115
|
+
@keydown.enter.exact.prevent="sendMessage"
|
|
116
|
+
/>
|
|
105
117
|
<div
|
|
106
|
-
v-if="
|
|
107
|
-
|
|
118
|
+
v-if="agentStore.availableModes.length > 1"
|
|
119
|
+
ref="modeMenu"
|
|
120
|
+
class="absolute bottom-2 left-4"
|
|
108
121
|
>
|
|
109
122
|
<button
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
class="
|
|
113
|
-
:
|
|
123
|
+
aria-label="Select mode"
|
|
124
|
+
class="flex h-8 w-8 items-center justify-center rounded-md border border-gray-200 bg-white text-lightNavbarIcons transition-colors duration-200 hover:bg-gray-100 dark:border-gray-600 dark:bg-gray-800 dark:text-darkNavbarIcons dark:hover:bg-gray-700"
|
|
125
|
+
:class="isModeMenuOpen ? 'bg-gray-100 dark:bg-gray-700' : ''"
|
|
126
|
+
:disabled="agentStore.isResponseInProgress"
|
|
127
|
+
title="Select mode"
|
|
114
128
|
type="button"
|
|
115
|
-
@click="
|
|
129
|
+
@click="toggleModeMenu"
|
|
116
130
|
>
|
|
117
|
-
|
|
131
|
+
<IconBrainOutline class="h-4 w-4" />
|
|
118
132
|
</button>
|
|
133
|
+
|
|
134
|
+
<div
|
|
135
|
+
v-if="isModeMenuOpen"
|
|
136
|
+
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"
|
|
137
|
+
>
|
|
138
|
+
<button
|
|
139
|
+
v-for="mode in agentStore.availableModes"
|
|
140
|
+
:key="mode.name"
|
|
141
|
+
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"
|
|
142
|
+
:class="mode.name === agentStore.activeModeName ? 'bg-gray-100 dark:bg-gray-700' : ''"
|
|
143
|
+
type="button"
|
|
144
|
+
@click="selectMode(mode.name)"
|
|
145
|
+
>
|
|
146
|
+
{{ mode.name }}
|
|
147
|
+
</button>
|
|
148
|
+
</div>
|
|
119
149
|
</div>
|
|
150
|
+
<Button
|
|
151
|
+
class="absolute right-4 bottom-2 !p-0 h-[34px] w-[34px]"
|
|
152
|
+
@click="sendMessage"
|
|
153
|
+
:disabled="!agentStore.trimmedUserMessage || agentStore.isResponseInProgress"
|
|
154
|
+
>
|
|
155
|
+
<IconArrowUpOutline
|
|
156
|
+
class="w-8 h-8 p-1
|
|
157
|
+
text-white"
|
|
158
|
+
/>
|
|
159
|
+
</Button>
|
|
120
160
|
</div>
|
|
121
|
-
<Button
|
|
122
|
-
class="absolute right-4 bottom-2 !p-0 h-[34px] w-[34px]"
|
|
123
|
-
@click="sendMessage"
|
|
124
|
-
:disabled="!agentStore.trimmedUserMessage || agentStore.isResponseInProgress"
|
|
125
|
-
>
|
|
126
|
-
<IconArrowUpOutline
|
|
127
|
-
class="w-8 h-8 p-1
|
|
128
|
-
text-white"
|
|
129
|
-
/>
|
|
130
|
-
</Button>
|
|
131
161
|
</div>
|
|
132
162
|
</div>
|
|
133
163
|
</div>
|
|
134
|
-
</
|
|
164
|
+
</Teleport>
|
|
135
165
|
|
|
136
166
|
</template>
|
|
137
167
|
|
|
138
168
|
<script setup lang="ts">
|
|
139
|
-
import { IconChatBubbleLeft20Solid, IconSparklesSolid } from '@iconify-prerendered/vue-heroicons';
|
|
169
|
+
import { IconChatBubbleLeft20Solid, IconSparklesSolid, IconArrowsPointingOut, IconArrowsPointingIn } from '@iconify-prerendered/vue-heroicons';
|
|
140
170
|
import { IconCloseOutline, IconBarsOutline, IconArrowUpOutline, IconCloseSidebarSolid, IconOpenSidebarSolid, IconBrainOutline } from '@iconify-prerendered/vue-flowbite';
|
|
141
|
-
import { useTemplateRef, onMounted, ref } from 'vue';
|
|
171
|
+
import { useTemplateRef, onMounted, ref,computed } from 'vue';
|
|
142
172
|
import { onClickOutside } from '@vueuse/core'
|
|
143
173
|
import ConversationArea from './ConversationArea.vue';
|
|
144
|
-
import { useAgentStore } from './useAgentStore';
|
|
174
|
+
import { useAgentStore } from './composables/useAgentStore';
|
|
175
|
+
import { useAgentTransitions } from './composables/useAgentTransitions';
|
|
145
176
|
import { Button } from '@/afcl';
|
|
146
177
|
import { useCoreStore } from '@/stores/core';
|
|
147
178
|
|
|
@@ -159,12 +190,10 @@ const chatSurface = useTemplateRef('chatSurface');
|
|
|
159
190
|
const textInput = useTemplateRef('textInput');
|
|
160
191
|
const modeMenu = useTemplateRef('modeMenu');
|
|
161
192
|
const agentStore = useAgentStore();
|
|
193
|
+
const agentTransitions = useAgentTransitions();
|
|
162
194
|
const coreStore = useCoreStore();
|
|
163
195
|
const isModeMenuOpen = ref(false);
|
|
164
196
|
|
|
165
|
-
const MAX_WIDTH = 800;
|
|
166
|
-
const MIN_WIDTH = 382; //w-96
|
|
167
|
-
|
|
168
197
|
let startX = 0
|
|
169
198
|
let startWidth = 0
|
|
170
199
|
|
|
@@ -181,7 +210,8 @@ const startResize = (e: MouseEvent) => {
|
|
|
181
210
|
|
|
182
211
|
const onResize = (e: MouseEvent) => {
|
|
183
212
|
const dx = startX - e.clientX
|
|
184
|
-
agentStore.setChatWidth(Math.min(Math.max(startWidth + dx, MIN_WIDTH), MAX_WIDTH))
|
|
213
|
+
agentStore.setChatWidth(Math.min(Math.max(startWidth + dx, agentStore.MIN_WIDTH), agentStore.MAX_WIDTH))
|
|
214
|
+
agentTransitions.setChatSurfaceTransition(true);
|
|
185
215
|
}
|
|
186
216
|
|
|
187
217
|
const stopResize = () => {
|
|
@@ -194,8 +224,8 @@ const stopResize = () => {
|
|
|
194
224
|
const appRoot = document.getElementById('app');
|
|
195
225
|
const header = document.getElementById('af-header-nav');
|
|
196
226
|
if (appRoot && header) {
|
|
197
|
-
|
|
198
|
-
|
|
227
|
+
agentTransitions.setAppRootTransition(false);
|
|
228
|
+
agentTransitions.setChatSurfaceTransition(false);
|
|
199
229
|
}
|
|
200
230
|
}
|
|
201
231
|
|
|
@@ -1,11 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
|
|
3
|
-
<IconArrowDownOutline
|
|
4
|
-
class="absolute bottom-32 right-4 bg-lightPrimary dark:bg-darkPrimary text-white p-2 w-10 h-10 rounded-full transition-opacity duration-100 ease-in"
|
|
5
|
-
:class="showScrollToBottomButton ? 'opacity-100' : 'opacity-0 pointer-events-none'"
|
|
6
|
-
:disabled="!showScrollToBottomButton"
|
|
7
|
-
/>
|
|
8
|
-
</button>
|
|
2
|
+
|
|
9
3
|
<SessionsHistory
|
|
10
4
|
:class="agentStore.isSessionHistoryOpen ? 'translate-x-0' : '-translate-x-full'"
|
|
11
5
|
/>
|
|
@@ -18,12 +12,24 @@
|
|
|
18
12
|
</div>
|
|
19
13
|
<AutoScrollContainer
|
|
20
14
|
:enabled="!showScrollToBottomButton"
|
|
21
|
-
class="flex flex-col overflow-y-auto
|
|
15
|
+
class="relative flex flex-col overflow-y-auto"
|
|
22
16
|
ref="scrollContainer"
|
|
23
17
|
:threshold="10"
|
|
24
18
|
behavior="smooth"
|
|
19
|
+
:class="agentStore.isFullScreen ? 'mx-auto' : ''"
|
|
20
|
+
:style="{
|
|
21
|
+
maxWidth: agentStore.isFullScreen ? agentStore.MAX_WIDTH+'px' : '100%',
|
|
22
|
+
transition: `max-width ${agentTransitions.TRANSITION_DURATION}ms ease-in-out`
|
|
23
|
+
}"
|
|
25
24
|
>
|
|
26
25
|
|
|
26
|
+
<button @click="scrollContainer.scrollToBottom()">
|
|
27
|
+
<IconArrowDownOutline
|
|
28
|
+
class="fixed bottom-32 left-1/2 bg-lightPrimary dark:bg-darkPrimary text-white p-2 w-10 h-10 rounded-full transition-opacity duration-100 ease-in"
|
|
29
|
+
:class="showScrollToBottomButton ? 'opacity-100' : 'opacity-0 pointer-events-none'"
|
|
30
|
+
:disabled="!showScrollToBottomButton"
|
|
31
|
+
/>
|
|
32
|
+
</button>
|
|
27
33
|
<div
|
|
28
34
|
v-for="message in props.messages" :key="message.id"
|
|
29
35
|
class="flex flex-col w-full"
|
|
@@ -71,15 +77,16 @@ import type { IMessage, IPart } from './types';
|
|
|
71
77
|
import { useTemplateRef, ref, defineAsyncComponent, onMounted, watch, computed } from 'vue';
|
|
72
78
|
import { IconArrowDownOutline } from '@iconify-prerendered/vue-flowbite';
|
|
73
79
|
import SessionsHistory from './SessionsHistory.vue';
|
|
74
|
-
import { useAgentStore } from './useAgentStore';
|
|
75
|
-
import ToolRenderer from './ToolRenderer.vue';
|
|
80
|
+
import { useAgentStore } from './composables/useAgentStore';
|
|
76
81
|
import ToolsGroup from './ToolsGroup.vue';
|
|
82
|
+
import { useAgentTransitions } from './composables/useAgentTransitions';
|
|
77
83
|
|
|
78
84
|
const scrollContainer = useTemplateRef('scrollContainer');
|
|
79
85
|
const showScrollToBottomButton = ref(false);
|
|
80
86
|
const innerScrollContainerRef = ref(null);
|
|
81
87
|
const AutoScrollContainer = defineAsyncComponent(() => import('@incremark/vue').then(module => module.AutoScrollContainer))
|
|
82
88
|
const agentStore = useAgentStore();
|
|
89
|
+
const agentTransitions = useAgentTransitions();
|
|
83
90
|
const clicks = ref(0);
|
|
84
91
|
|
|
85
92
|
function recalculateScroll() {
|
|
@@ -49,7 +49,7 @@ import { Button, Spinner } from '@/afcl'
|
|
|
49
49
|
import { computed } from 'vue';
|
|
50
50
|
import { IconPlusOutline } from '@iconify-prerendered/vue-flowbite';
|
|
51
51
|
import type { ISessionsListItem } from './types';
|
|
52
|
-
import { useAgentStore } from './useAgentStore';
|
|
52
|
+
import { useAgentStore } from './composables/useAgentStore';
|
|
53
53
|
|
|
54
54
|
const agentStore = useAgentStore();
|
|
55
55
|
|
|
@@ -1,17 +1,23 @@
|
|
|
1
1
|
import { defineStore } from 'pinia';
|
|
2
|
-
import { IAgentSession, ISessionsListItem, IMessage } from '
|
|
2
|
+
import { IAgentSession, ISessionsListItem, IMessage } from '../types';
|
|
3
3
|
import { ref, nextTick, computed, watch, onMounted, shallowRef } from 'vue';
|
|
4
4
|
import { callAdminForthApi } from '@/utils';
|
|
5
5
|
import { useAdminforth } from '@/adminforth';
|
|
6
|
-
import { Chat } from '
|
|
7
|
-
import {
|
|
6
|
+
import { Chat } from '../chat';
|
|
7
|
+
import { DefaultChatTransport } from 'ai';
|
|
8
8
|
import { useCoreStore } from '@/stores/core';
|
|
9
|
+
import { useAgentTransitions } from './useAgentTransitions';
|
|
9
10
|
|
|
10
11
|
type AgentMode = {
|
|
11
12
|
name: string;
|
|
12
13
|
};
|
|
13
14
|
|
|
14
15
|
export const useAgentStore = defineStore('agent', () => {
|
|
16
|
+
const DEFAULT_CHAT_WIDTH = 600;
|
|
17
|
+
const MAX_WIDTH = 800;
|
|
18
|
+
const MIN_WIDTH = 382; //w-96
|
|
19
|
+
const agentTransitions = useAgentTransitions();
|
|
20
|
+
|
|
15
21
|
const activeSessionId = ref<string | null>(null);
|
|
16
22
|
const currentSession = ref<IAgentSession | null>(null);
|
|
17
23
|
const sessionList = ref<ISessionsListItem[]>([]);
|
|
@@ -31,7 +37,7 @@ export const useAgentStore = defineStore('agent', () => {
|
|
|
31
37
|
const appRoot = ref<HTMLElement | null>(null);
|
|
32
38
|
const header = ref<HTMLElement | null>(null);
|
|
33
39
|
const lastSessionId = ref<string | null>(null);
|
|
34
|
-
const chatWidth = ref(
|
|
40
|
+
const chatWidth = ref(DEFAULT_CHAT_WIDTH);
|
|
35
41
|
const availableModes = ref<AgentMode[]>([]);
|
|
36
42
|
const activeModeName = ref<string | null>(null);
|
|
37
43
|
function setLocalStorageItem(key: string, value: string) {
|
|
@@ -55,7 +61,12 @@ export const useAgentStore = defineStore('agent', () => {
|
|
|
55
61
|
}
|
|
56
62
|
})
|
|
57
63
|
onMounted(() => {
|
|
58
|
-
|
|
64
|
+
const chatWidthBeforeFullScreen = parseInt(getLocalStorageItem('chatWidthBeforeFullScreen') || '0', 10);
|
|
65
|
+
if (chatWidthBeforeFullScreen) {
|
|
66
|
+
chatWidth.value = chatWidthBeforeFullScreen;
|
|
67
|
+
} else {
|
|
68
|
+
chatWidth.value = parseInt(getLocalStorageItem('chatWidth') || DEFAULT_CHAT_WIDTH.toString(), 10);
|
|
69
|
+
}
|
|
59
70
|
isTeleportedToBody.value = getLocalStorageItem('isTeleportedToBody') === 'true';
|
|
60
71
|
lastSessionId.value = getLocalStorageItem('lastSessionId');
|
|
61
72
|
if (lastSessionId.value && lastSessionId.value !== 'pre-session') {
|
|
@@ -71,29 +82,33 @@ export const useAgentStore = defineStore('agent', () => {
|
|
|
71
82
|
header.value = document.getElementById('af-header-nav');
|
|
72
83
|
if (appRoot.value && header.value) {
|
|
73
84
|
nextTick(() => {
|
|
74
|
-
|
|
75
|
-
header.value.style.transition = 'padding-right 200ms ease-in-out';
|
|
85
|
+
agentTransitions.setAppRootTransition(false);
|
|
76
86
|
});
|
|
77
87
|
}
|
|
78
88
|
})
|
|
89
|
+
|
|
79
90
|
const isFullScreen = ref(false);
|
|
80
91
|
function setFullScreen(fullScreen: boolean) {
|
|
81
92
|
isFullScreen.value = fullScreen;
|
|
82
|
-
console.log('setFullScreen', fullScreen);
|
|
83
93
|
if (fullScreen) {
|
|
84
|
-
|
|
94
|
+
setLocalStorageItem('chatWidthBeforeFullScreen', chatWidth.value.toString());
|
|
95
|
+
setLocalStorageItem('isTeleportedToBodyBeforeFullScreen', isTeleportedToBody.value ? 'true' : 'false');
|
|
96
|
+
setIsTeleportedToBody(false);
|
|
97
|
+
useAgentTransitions().setChatSurfaceTransition(false);
|
|
98
|
+
setChatWidth(window.innerWidth, false);
|
|
85
99
|
} else {
|
|
86
|
-
|
|
87
|
-
|
|
100
|
+
const lastChatWidth = parseInt(getLocalStorageItem('chatWidthBeforeFullScreen') || DEFAULT_CHAT_WIDTH.toString(), 10);
|
|
101
|
+
const isTeleportedBeforeFullScreen = getLocalStorageItem('isTeleportedToBodyBeforeFullScreen') === 'true';
|
|
102
|
+
agentTransitions.setAppRootTransition(true);
|
|
103
|
+
setIsTeleportedToBody(isTeleportedBeforeFullScreen);
|
|
104
|
+
setChatWidth(lastChatWidth, false);
|
|
105
|
+
setTimeout(() => agentTransitions.setAppRootTransition(false), agentTransitions.TRANSITION_DURATION);
|
|
88
106
|
}
|
|
89
107
|
}
|
|
90
108
|
|
|
91
|
-
function setChatWidth(width: number, blockTransition =
|
|
92
|
-
if (
|
|
93
|
-
|
|
94
|
-
appRoot.value.style.transition = '';
|
|
95
|
-
header.value.style.transition = '';
|
|
96
|
-
}
|
|
109
|
+
function setChatWidth(width: number, blockTransition = true) {
|
|
110
|
+
if (blockTransition) {
|
|
111
|
+
agentTransitions.setAppRootTransition(true);
|
|
97
112
|
}
|
|
98
113
|
chatWidth.value = width;
|
|
99
114
|
|
|
@@ -414,5 +429,8 @@ export const useAgentStore = defineStore('agent', () => {
|
|
|
414
429
|
activeModeName,
|
|
415
430
|
setAvailableModes,
|
|
416
431
|
setActiveMode,
|
|
432
|
+
DEFAULT_CHAT_WIDTH,
|
|
433
|
+
MAX_WIDTH,
|
|
434
|
+
MIN_WIDTH,
|
|
417
435
|
}
|
|
418
436
|
})
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { defineStore } from 'pinia';
|
|
2
|
+
import { ref, nextTick,onMounted } from 'vue';
|
|
3
|
+
import { useAgentStore } from './useAgentStore';
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
export const useAgentTransitions = defineStore('agentTransitions', () => {
|
|
7
|
+
const TRANSITION_DURATION = 200;
|
|
8
|
+
|
|
9
|
+
const agentStore = useAgentStore();
|
|
10
|
+
const appRoot = ref<HTMLElement | null>(null);
|
|
11
|
+
const header = ref<HTMLElement | null>(null);
|
|
12
|
+
|
|
13
|
+
const chatRoot = ref<HTMLElement | null>(null);
|
|
14
|
+
|
|
15
|
+
onMounted(() => {
|
|
16
|
+
appRoot.value = document.getElementById('app');
|
|
17
|
+
header.value = document.getElementById('af-header-nav');
|
|
18
|
+
chatRoot.value = document.getElementById('adminforth-agent-chat-surface');
|
|
19
|
+
if (appRoot.value && header.value) {
|
|
20
|
+
nextTick(() => {
|
|
21
|
+
setAppRootTransition(false);
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
if (chatRoot.value) {
|
|
25
|
+
nextTick(() => {
|
|
26
|
+
setChatSurfaceTransition(false);
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
function setAppRootTransition(blockTransition = true) {
|
|
31
|
+
if (appRoot.value && header.value) {
|
|
32
|
+
if (blockTransition) {
|
|
33
|
+
appRoot.value.style.transition = '';
|
|
34
|
+
header.value.style.transition = '';
|
|
35
|
+
} else {
|
|
36
|
+
appRoot.value.style.transition = `padding-right ${TRANSITION_DURATION}ms ease-in-out`;
|
|
37
|
+
header.value.style.transition = `padding-right ${TRANSITION_DURATION}ms ease-in-out`;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function setChatSurfaceTransition(blockTransition = true) {
|
|
42
|
+
if (chatRoot.value) {
|
|
43
|
+
if (blockTransition) {
|
|
44
|
+
chatRoot.value.style.transition = '';
|
|
45
|
+
} else {
|
|
46
|
+
chatRoot.value.style.transition = `
|
|
47
|
+
transform ${TRANSITION_DURATION}ms ease-in-out,
|
|
48
|
+
width ${TRANSITION_DURATION}ms ease-in-out
|
|
49
|
+
`;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return {
|
|
54
|
+
TRANSITION_DURATION,
|
|
55
|
+
setAppRootTransition,
|
|
56
|
+
setChatSurfaceTransition
|
|
57
|
+
}
|
|
58
|
+
});
|