@adminforth/agent 2.10.0 → 3.0.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/dist/custom/ChatFooter.vue +47 -82
- package/dist/custom/ChatHeader.vue +57 -63
- package/dist/custom/ChatSurface.vue +11 -18
- package/dist/custom/EditActionButtons.vue +12 -8
- package/dist/custom/SessionsHistory.vue +90 -28
- package/dist/custom/SteerQueue.vue +15 -9
- package/dist/custom/composables/agentStore/useAgentSessions.ts +21 -0
- package/dist/custom/composables/useAgentStore.ts +2 -0
- package/dist/custom/conversation_area/ConversationArea.vue +21 -21
- package/dist/custom/conversation_area/MessageRenderer.vue +18 -17
- package/dist/custom/conversation_area/ProcessingTimeline.vue +11 -11
- package/dist/custom/conversation_area/ReasoningRenderer.vue +5 -6
- package/dist/custom/conversation_area/SystemMessageRenderer.vue +4 -4
- package/dist/custom/conversation_area/TextRenderer.vue +65 -27
- package/dist/custom/conversation_area/ToolApprovalRenderer.vue +34 -42
- package/dist/custom/conversation_area/ToolRenderer.vue +14 -14
- package/dist/custom/incremark_code_renderers/IncremarkShikiCodeBlock.vue +22 -61
- package/dist/custom/speech_recognition_frontend/MicrophoneButon.vue +6 -4
- package/dist/transport/http/sessionEndpoints.js +34 -0
- package/package.json +3 -3
- package/dist/custom/SteerDebugPanel.vue +0 -45
|
@@ -8,11 +8,12 @@
|
|
|
8
8
|
}"
|
|
9
9
|
>
|
|
10
10
|
<SteerQueue />
|
|
11
|
-
<!-- <SteerDebugPanel /> -->
|
|
12
11
|
<div
|
|
13
|
-
class="w-full flex flex-col rounded-
|
|
14
|
-
border-
|
|
15
|
-
|
|
12
|
+
class="af-agent-composer w-full flex flex-col rounded-xl border transition-colors duration-150
|
|
13
|
+
border-lightInputBorder bg-lightInputBackground hover:border-lightInputBorderHover
|
|
14
|
+
focus-within:border-lightInputFocusBorder focus-within:ring-[3px] focus-within:ring-lightInputFocusRing
|
|
15
|
+
dark:border-darkInputBorder dark:bg-darkInputBackground dark:hover:border-darkInputBorderHover
|
|
16
|
+
dark:focus-within:border-darkInputFocusBorder dark:focus-within:ring-darkInputFocusRing"
|
|
16
17
|
>
|
|
17
18
|
<textarea
|
|
18
19
|
v-if="!agentStore.isAudioChatMode"
|
|
@@ -21,9 +22,9 @@
|
|
|
21
22
|
rows="1"
|
|
22
23
|
@input="autoResize"
|
|
23
24
|
:class="[
|
|
24
|
-
`block w-full px-3
|
|
25
|
+
`block w-full px-3 pt-2.5 pb-1 resize-none overflow-hidden bg-transparent border-none text-sm leading-[22px]
|
|
25
26
|
focus:outline-none focus:ring-0
|
|
26
|
-
text-lightInputText placeholder:text-
|
|
27
|
+
text-lightInputText placeholder:text-lightInputPlaceholderText dark:text-darkInputText dark:placeholder:text-darkInputPlaceholderText`,
|
|
27
28
|
{ '!text-base': coreStore.isIos }
|
|
28
29
|
]"
|
|
29
30
|
:placeholder="placeholderText"
|
|
@@ -35,57 +36,38 @@
|
|
|
35
36
|
class="items-center gap-1 px-2 pb-2"
|
|
36
37
|
:class="agentStore.isAudioChatMode ? 'grid grid-cols-[1fr_auto_1fr] pt-2' : 'flex pt-1'"
|
|
37
38
|
>
|
|
38
|
-
<
|
|
39
|
+
<DropdownMenu
|
|
39
40
|
v-if="agentStore.availableModes.length > 1"
|
|
40
|
-
|
|
41
|
-
class="
|
|
41
|
+
v-model:open="isModeMenuOpen"
|
|
42
|
+
class="af-agent-mode-menu justify-self-start"
|
|
43
|
+
teleportToBody
|
|
42
44
|
>
|
|
43
|
-
<
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
>
|
|
53
|
-
<IconSparklesSolid class="w-3.5 h-3.5 text-lightPrimary dark:text-darkPrimary" />
|
|
54
|
-
{{ agentStore.activeModeName }}
|
|
55
|
-
<IconChevronDown
|
|
56
|
-
class="w-3.5 h-3.5 transition-transform duration-200"
|
|
57
|
-
:class="isModeMenuOpen ? 'rotate-180' : 'rotate-0'"
|
|
58
|
-
/>
|
|
59
|
-
</button>
|
|
60
|
-
<Transition
|
|
61
|
-
enter-active-class="transition ease-out duration-200"
|
|
62
|
-
enter-from-class="opacity-0 scale-95"
|
|
63
|
-
enter-to-class="opacity-100 scale-100"
|
|
64
|
-
leave-active-class="transition ease-in duration-150"
|
|
65
|
-
leave-from-class="opacity-100 scale-100"
|
|
66
|
-
leave-to-class="opacity-0 scale-95"
|
|
67
|
-
>
|
|
68
|
-
<div
|
|
69
|
-
v-if="isModeMenuOpen"
|
|
70
|
-
class="absolute z-20 bottom-full left-0 mb-2 min-w-44 p-1 rounded-xl border border-gray-200 bg-white shadow-lg dark:border-gray-600 dark:bg-gray-800"
|
|
45
|
+
<template #trigger="{ open }">
|
|
46
|
+
<Button
|
|
47
|
+
variant="ghost"
|
|
48
|
+
size="sm"
|
|
49
|
+
type="button"
|
|
50
|
+
:class="open && 'bg-lightSecondaryDarken text-lightInputText dark:bg-darkSecondaryLighten dark:text-darkInputText'"
|
|
51
|
+
:aria-label="$t('Select mode')"
|
|
52
|
+
:aria-expanded="open"
|
|
53
|
+
:title="$t('Select mode')"
|
|
71
54
|
>
|
|
72
|
-
<
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
</div>
|
|
55
|
+
<IconSparkles class="w-4 h-4" />
|
|
56
|
+
{{ agentStore.activeModeName }}
|
|
57
|
+
<IconChevronDown class="w-3.5 h-3.5 transition-transform duration-200" :class="open && 'rotate-180'" />
|
|
58
|
+
</Button>
|
|
59
|
+
</template>
|
|
60
|
+
<DropdownMenuItem
|
|
61
|
+
v-for="mode in agentStore.availableModes"
|
|
62
|
+
:key="mode.name"
|
|
63
|
+
@click="agentStore.setActiveMode(mode.name)"
|
|
64
|
+
>
|
|
65
|
+
{{ mode.name }}
|
|
66
|
+
<template v-if="mode.name === agentStore.activeModeName" #suffix>
|
|
67
|
+
<IconCheck class="w-4 h-4 text-lightPrimary dark:text-darkPrimary" />
|
|
68
|
+
</template>
|
|
69
|
+
</DropdownMenuItem>
|
|
70
|
+
</DropdownMenu>
|
|
89
71
|
<div
|
|
90
72
|
class="ml-auto flex items-center gap-1"
|
|
91
73
|
:class="agentStore.isAudioChatMode ? 'col-start-2' : ''"
|
|
@@ -100,27 +82,29 @@
|
|
|
100
82
|
@cancel="cancelEdit"
|
|
101
83
|
@confirm="sendMessage"
|
|
102
84
|
/>
|
|
103
|
-
<
|
|
85
|
+
<Button
|
|
104
86
|
v-else-if="!agentStore.isResponseInProgress"
|
|
105
87
|
type="button"
|
|
106
|
-
|
|
88
|
+
iconOnly
|
|
89
|
+
class="rounded-full"
|
|
107
90
|
:disabled="!agentStore.trimmedUserMessage"
|
|
108
91
|
:title="$t('Send message')"
|
|
109
92
|
:aria-label="$t('Send message')"
|
|
110
93
|
@click="sendMessage"
|
|
111
94
|
>
|
|
112
|
-
<IconArrowUp
|
|
113
|
-
</
|
|
114
|
-
<
|
|
95
|
+
<IconArrowUp />
|
|
96
|
+
</Button>
|
|
97
|
+
<Button
|
|
115
98
|
v-else
|
|
116
99
|
type="button"
|
|
117
|
-
|
|
100
|
+
iconOnly
|
|
101
|
+
class="rounded-full"
|
|
118
102
|
:title="$t('Stop')"
|
|
119
103
|
:aria-label="$t('Stop')"
|
|
120
104
|
@click="stopCurrentRequest"
|
|
121
105
|
>
|
|
122
106
|
<span class="w-2.5 h-2.5 rounded-sm bg-current" />
|
|
123
|
-
</
|
|
107
|
+
</Button>
|
|
124
108
|
</template>
|
|
125
109
|
</div>
|
|
126
110
|
</div>
|
|
@@ -129,16 +113,15 @@
|
|
|
129
113
|
</template>
|
|
130
114
|
|
|
131
115
|
<script setup lang="ts">
|
|
132
|
-
import { IconArrowUp, IconCheck, IconChevronDown,
|
|
116
|
+
import { IconArrowUp, IconCheck, IconChevronDown, IconSparkles } from '@iconify-prerendered/vue-lucide';
|
|
133
117
|
import { useTemplateRef, onMounted, ref, watch, computed } from 'vue';
|
|
134
|
-
import {
|
|
118
|
+
import { Button, DropdownMenu, DropdownMenuItem } from '@/afcl';
|
|
135
119
|
import { useAgentStore } from './composables/useAgentStore';
|
|
136
120
|
import { useAgentTransitions } from './composables/useAgentTransitions';
|
|
137
121
|
import { useCoreStore } from '@/stores/core';
|
|
138
122
|
import { remToPx } from './utils';
|
|
139
123
|
import MicrophoneButton from './speech_recognition_frontend/MicrophoneButon.vue';
|
|
140
124
|
import SteerQueue from './SteerQueue.vue';
|
|
141
|
-
import SteerDebugPanel from './SteerDebugPanel.vue';
|
|
142
125
|
import EditActionButtons from './EditActionButtons.vue';
|
|
143
126
|
|
|
144
127
|
const props = defineProps<{
|
|
@@ -156,14 +139,7 @@ const props = defineProps<{
|
|
|
156
139
|
conversationAreaRef: any
|
|
157
140
|
}>();
|
|
158
141
|
|
|
159
|
-
const ROUND_PRIMARY_BUTTON_CLASSES = `flex items-center justify-center w-8 h-8 shrink-0 rounded-full transition-colors duration-150
|
|
160
|
-
bg-lightPrimary text-lightPrimaryContrast hover:opacity-90
|
|
161
|
-
dark:bg-darkPrimary dark:text-darkPrimaryContrast
|
|
162
|
-
disabled:bg-gray-200 disabled:text-gray-400 disabled:hover:opacity-100 disabled:cursor-default
|
|
163
|
-
dark:disabled:bg-gray-700 dark:disabled:text-gray-500`;
|
|
164
|
-
|
|
165
142
|
const textInput = useTemplateRef('textInput');
|
|
166
|
-
const modeMenu = useTemplateRef('modeMenu');
|
|
167
143
|
const agentStore = useAgentStore();
|
|
168
144
|
const agentTransitions = useAgentTransitions();
|
|
169
145
|
const coreStore = useCoreStore();
|
|
@@ -176,8 +152,6 @@ const placeholderText = computed(() => {
|
|
|
176
152
|
return agentStore.userMessagePlaceholder;
|
|
177
153
|
});
|
|
178
154
|
|
|
179
|
-
onClickOutside(modeMenu as any, () => { isModeMenuOpen.value = false; });
|
|
180
|
-
|
|
181
155
|
onMounted(async () => {
|
|
182
156
|
agentStore.setAvailableModes(props.meta.modes, props.meta.defaultModeName);
|
|
183
157
|
agentStore.setCurrentGenerationModeFromLocalStorage();
|
|
@@ -210,15 +184,6 @@ function autoResize() {
|
|
|
210
184
|
}
|
|
211
185
|
}
|
|
212
186
|
|
|
213
|
-
function toggleModeMenu() {
|
|
214
|
-
isModeMenuOpen.value = !isModeMenuOpen.value;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
function selectMode(modeName: string) {
|
|
218
|
-
agentStore.setActiveMode(modeName);
|
|
219
|
-
isModeMenuOpen.value = false;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
187
|
async function sendMessage() {
|
|
223
188
|
if (agentStore.isAudioChatMode) return;
|
|
224
189
|
isModeMenuOpen.value = false;
|
|
@@ -1,83 +1,77 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div
|
|
2
|
+
<div
|
|
3
3
|
ref="headerRef"
|
|
4
|
-
class="flex items-center
|
|
4
|
+
class="af-agent-header flex items-center gap-0.5 h-12 px-2 border-b border-lightNavbarBorder dark:border-darkNavbarBorder"
|
|
5
5
|
>
|
|
6
|
-
<
|
|
7
|
-
|
|
6
|
+
<Button
|
|
7
|
+
variant="ghost"
|
|
8
|
+
iconOnly
|
|
9
|
+
:class="agentStore.isSessionHistoryOpen && PRESSED_CLASSES"
|
|
10
|
+
:aria-pressed="agentStore.isSessionHistoryOpen"
|
|
11
|
+
:aria-label="$t('Chat history')"
|
|
12
|
+
:title="$t('Chat history')"
|
|
13
|
+
@click="agentStore.setSessionHistoryOpen(!agentStore.isSessionHistoryOpen)"
|
|
8
14
|
>
|
|
9
|
-
<
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
/>
|
|
32
|
-
<
|
|
33
|
-
|
|
34
|
-
|
|
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
|
-
<p class="flex-1 min-w-0 truncate px-1 text-sm font-semibold text-lightNavbarText dark:text-darkNavbarText">
|
|
15
|
+
<IconHistory />
|
|
16
|
+
</Button>
|
|
17
|
+
<Button
|
|
18
|
+
v-if="!coreStore.isMobile && !agentStore.isFullScreen"
|
|
19
|
+
variant="ghost"
|
|
20
|
+
iconOnly
|
|
21
|
+
:class="agentStore.isTeleportedToBody && PRESSED_CLASSES"
|
|
22
|
+
:aria-pressed="agentStore.isTeleportedToBody"
|
|
23
|
+
:aria-label="$t('Dock to the side')"
|
|
24
|
+
:title="$t('Dock to the side')"
|
|
25
|
+
@click="agentStore.setIsTeleportedToBody(!agentStore.isTeleportedToBody)"
|
|
26
|
+
>
|
|
27
|
+
<IconPanelRight />
|
|
28
|
+
</Button>
|
|
29
|
+
<Button
|
|
30
|
+
v-if="!coreStore.isMobile"
|
|
31
|
+
variant="ghost"
|
|
32
|
+
iconOnly
|
|
33
|
+
:aria-label="agentStore.isFullScreen ? $t('Exit full screen') : $t('Full screen')"
|
|
34
|
+
:title="agentStore.isFullScreen ? $t('Exit full screen') : $t('Full screen')"
|
|
35
|
+
@click="agentStore.setFullScreen(!agentStore.isFullScreen)"
|
|
36
|
+
>
|
|
37
|
+
<IconMinimize2 v-if="agentStore.isFullScreen" />
|
|
38
|
+
<IconMaximize2 v-else />
|
|
39
|
+
</Button>
|
|
40
|
+
<p class="flex-1 min-w-0 truncate px-1 text-[13px] font-semibold text-lightNavbarText dark:text-darkNavbarText">
|
|
51
41
|
{{ isNewChat ? $t('New chat') : agentStore.currentSession.title }}
|
|
52
42
|
</p>
|
|
53
|
-
<
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
43
|
+
<Button
|
|
44
|
+
variant="secondary"
|
|
45
|
+
class="shrink-0"
|
|
46
|
+
@click="agentStore.createPreSession(); agentStore.setSessionHistoryOpen(false); agentStore.focusTextInput();"
|
|
47
|
+
>
|
|
48
|
+
<IconPlus class="w-4 h-4" />
|
|
49
|
+
{{ $t('New chat') }}
|
|
50
|
+
</Button>
|
|
51
|
+
<Button
|
|
52
|
+
variant="ghost"
|
|
53
|
+
iconOnly
|
|
54
|
+
:aria-label="$t('Close')"
|
|
55
|
+
:title="$t('Close')"
|
|
56
|
+
@click="agentStore.setIsChatOpen(false)"
|
|
57
|
+
>
|
|
58
|
+
<IconX />
|
|
59
|
+
</Button>
|
|
68
60
|
</div>
|
|
69
61
|
</template>
|
|
70
62
|
|
|
71
63
|
|
|
72
64
|
<script setup>
|
|
73
65
|
import { computed } from 'vue';
|
|
74
|
-
import {
|
|
75
|
-
import { IconCloseOutline, IconBarsOutline, IconCloseSidebarSolid, IconOpenSidebarSolid, IconPlusOutline } from '@iconify-prerendered/vue-flowbite';
|
|
66
|
+
import { IconHistory, IconMaximize2, IconMinimize2, IconPanelRight, IconPlus, IconX } from '@iconify-prerendered/vue-lucide';
|
|
76
67
|
import { useAgentStore } from './composables/useAgentStore';
|
|
77
68
|
import { useCoreStore } from '@/stores/core';
|
|
78
69
|
import { Button } from '@/afcl';
|
|
79
70
|
import { PRE_SESSION_ID } from './composables/agentStore/constants';
|
|
80
71
|
|
|
72
|
+
// a toggle in the on state, same fill as the hover of a ghost button
|
|
73
|
+
const PRESSED_CLASSES = 'bg-lightSecondaryDarken text-lightInputText dark:bg-darkSecondaryLighten dark:text-darkInputText';
|
|
74
|
+
|
|
81
75
|
const agentStore = useAgentStore();
|
|
82
76
|
const coreStore = useCoreStore();
|
|
83
77
|
|
|
@@ -1,28 +1,20 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
hover:scale-110 transition-colors duration-200"
|
|
2
|
+
<Button
|
|
3
|
+
variant="secondary"
|
|
4
|
+
:class="{ 'bg-lightButtonGroupActiveBackground hover:bg-lightButtonGroupActiveBackground dark:bg-darkButtonGroupActiveBackground dark:hover:bg-darkButtonGroupActiveBackground': agentStore.isChatOpen }"
|
|
5
|
+
:aria-expanded="agentStore.isChatOpen"
|
|
7
6
|
@click="agentStore.setIsChatOpen(!agentStore.isChatOpen)"
|
|
8
7
|
>
|
|
9
|
-
<
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
<div class="absolute w-4 h-4 bg-lightNavbar dark:bg-darkNavbar rounded-full -top-1 -right-2">
|
|
13
|
-
<IconSparklesSolid
|
|
14
|
-
class="w-4 h-4"
|
|
15
|
-
/>
|
|
16
|
-
</div>
|
|
17
|
-
</div>
|
|
8
|
+
<IconBot class="w-4 h-4" />
|
|
9
|
+
{{ $t('Agent') }}
|
|
10
|
+
</Button>
|
|
18
11
|
|
|
19
12
|
<Teleport to="body">
|
|
20
13
|
<div
|
|
21
14
|
ref="chatSurface"
|
|
22
15
|
id="adminforth-agent-chat-surface"
|
|
23
|
-
class="fixed
|
|
24
|
-
|
|
25
|
-
:class="[agentStore.isChatOpen ? 'translate-x-0' : 'translate-x-full', !agentStore.isTeleportedToBody ? 'shadow-2xl' : '']"
|
|
16
|
+
class="fixed top-0 right-0 z-40 flex h-screen border-l border-lightNavbarBorder bg-lightNavbar dark:border-darkNavbarBorder dark:bg-darkNavbar"
|
|
17
|
+
:class="[agentStore.isChatOpen ? 'translate-x-0' : 'translate-x-full', !agentStore.isTeleportedToBody ? 'shadow-popover dark:shadow-darkPopover' : '']"
|
|
26
18
|
:style="{ width: agentStore.chatWidth + 'rem' }"
|
|
27
19
|
>
|
|
28
20
|
<div
|
|
@@ -58,7 +50,8 @@
|
|
|
58
50
|
</template>
|
|
59
51
|
|
|
60
52
|
<script setup lang="ts">
|
|
61
|
-
import {
|
|
53
|
+
import { IconBot } from '@iconify-prerendered/vue-lucide';
|
|
54
|
+
import { Button } from '@/afcl';
|
|
62
55
|
import { useTemplateRef, onMounted, ref, onUnmounted } from 'vue';
|
|
63
56
|
import { onClickOutside } from '@vueuse/core'
|
|
64
57
|
import ConversationArea from './conversation_area/ConversationArea.vue';
|
|
@@ -1,26 +1,30 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div class="flex items-center gap-
|
|
2
|
+
<div class="flex items-center gap-2">
|
|
3
3
|
<Button
|
|
4
4
|
variant="secondary"
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
size="sm"
|
|
6
|
+
iconOnly
|
|
7
|
+
:title="$t('Cancel edit')"
|
|
8
|
+
:aria-label="$t('Cancel edit')"
|
|
7
9
|
@click="emit('cancel')"
|
|
8
10
|
>
|
|
9
|
-
<
|
|
11
|
+
<IconX />
|
|
10
12
|
</Button>
|
|
11
13
|
<Button
|
|
12
|
-
|
|
13
|
-
|
|
14
|
+
size="sm"
|
|
15
|
+
iconOnly
|
|
16
|
+
:title="$t('Send edited message')"
|
|
17
|
+
:aria-label="$t('Send edited message')"
|
|
14
18
|
:disabled="confirmDisabled"
|
|
15
19
|
@click="emit('confirm')"
|
|
16
20
|
>
|
|
17
|
-
<
|
|
21
|
+
<IconCheck />
|
|
18
22
|
</Button>
|
|
19
23
|
</div>
|
|
20
24
|
</template>
|
|
21
25
|
|
|
22
26
|
<script setup lang="ts">
|
|
23
|
-
import {
|
|
27
|
+
import { IconCheck, IconX } from '@iconify-prerendered/vue-lucide';
|
|
24
28
|
import { Button } from '@/afcl';
|
|
25
29
|
|
|
26
30
|
defineProps<{
|
|
@@ -1,37 +1,73 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
"
|
|
2
|
+
<div
|
|
3
|
+
class="af-agent-history absolute top-0 left-0 z-20 flex h-full w-96 max-w-full flex-col overflow-y-auto overflow-x-hidden
|
|
4
|
+
border-r border-lightNavbarBorder bg-lightNavbar transition-transform duration-200 ease-in-out
|
|
5
|
+
dark:border-darkNavbarBorder dark:bg-darkNavbar"
|
|
7
6
|
>
|
|
8
|
-
<h3
|
|
9
|
-
<div
|
|
10
|
-
|
|
11
|
-
<div class="w-full border-b border-gray-200 dark:border-gray-700"/>
|
|
12
|
-
<div v-for="group in groupedSessions" :key="group.dayKey" class="w-full py-2">
|
|
13
|
-
<div class="px-4 pb-2 text-xs font-semibold uppercase tracking-[0.2em] text-gray-500 dark:text-gray-400">
|
|
7
|
+
<h3 class="px-4 pt-4 pb-2 text-sm font-semibold text-lightNavbarText dark:text-darkNavbarText">{{ $t('Chat history') }}</h3>
|
|
8
|
+
<div v-for="group in groupedSessions" :key="group.dayKey" class="px-2 py-2">
|
|
9
|
+
<div class="px-2.5 pb-1 text-xs font-medium text-lightInputIconColor dark:text-darkInputIconColor">
|
|
14
10
|
{{ group.label }}
|
|
15
11
|
</div>
|
|
16
|
-
|
|
17
|
-
<
|
|
12
|
+
<!-- enter and esc are handled on the wrapper so they fire once: Input spreads attrs on both its root and the input -->
|
|
13
|
+
<div
|
|
18
14
|
v-for="session in group.sessions"
|
|
19
15
|
:key="session.sessionId"
|
|
20
|
-
class=" flex items-center
|
|
21
|
-
:class="
|
|
22
|
-
'
|
|
23
|
-
|
|
24
|
-
@
|
|
16
|
+
class="af-agent-history-row group flex items-center gap-0.5 rounded-default text-sm transition-colors"
|
|
17
|
+
:class="agentStore.activeSessionId === session.sessionId
|
|
18
|
+
? 'font-medium bg-lightVerticalTabsBackgroundActive text-lightVerticalTabsTextActive dark:bg-darkVerticalTabsBackgroundActive dark:text-darkVerticalTabsTextActive'
|
|
19
|
+
: 'text-lightVerticalTabsText hover:bg-lightVerticalTabsBackgroundHover hover:text-lightVerticalTabsTextHover dark:text-darkVerticalTabsText dark:hover:bg-darkVerticalTabsBackgroundHover dark:hover:text-darkVerticalTabsTextHover'"
|
|
20
|
+
@keydown.enter.prevent="renamingSessionId === session.sessionId && commitRename(session)"
|
|
21
|
+
@keydown.esc.prevent="renamingSessionId = null"
|
|
25
22
|
>
|
|
26
|
-
<
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
23
|
+
<Input
|
|
24
|
+
v-if="renamingSessionId === session.sessionId"
|
|
25
|
+
ref="renameInput"
|
|
26
|
+
v-model="renameTitle"
|
|
27
|
+
type="text"
|
|
28
|
+
class="min-w-0 flex-1"
|
|
29
|
+
fullWidth
|
|
30
|
+
maxlength="100"
|
|
31
|
+
:aria-label="$t('Chat name')"
|
|
32
|
+
@blur="commitRename(session)"
|
|
33
|
+
/>
|
|
34
|
+
<template v-else>
|
|
35
|
+
<button
|
|
36
|
+
type="button"
|
|
37
|
+
class="min-w-0 flex-1 truncate rounded-default px-2.5 py-1.5 text-left focus:outline-none focus-visible:ring-[3px] focus-visible:ring-lightFocusRing dark:focus-visible:ring-darkFocusRing"
|
|
38
|
+
@click="agentStore.setActiveSession(session.sessionId); agentStore.setSessionHistoryOpen(false);"
|
|
39
|
+
>
|
|
40
|
+
{{ session.title || session.sessionId }}
|
|
41
|
+
</button>
|
|
42
|
+
<Button
|
|
43
|
+
v-if="session.sessionId !== PRE_SESSION_ID"
|
|
44
|
+
variant="ghost"
|
|
45
|
+
size="sm"
|
|
46
|
+
iconOnly
|
|
47
|
+
:class="ROW_ACTION_CLASSES"
|
|
48
|
+
:aria-label="$t('Rename chat')"
|
|
49
|
+
:title="$t('Rename chat')"
|
|
50
|
+
@click="startRename(session)"
|
|
51
|
+
>
|
|
52
|
+
<IconPencil />
|
|
53
|
+
</Button>
|
|
54
|
+
<Button
|
|
55
|
+
variant="ghost"
|
|
56
|
+
size="sm"
|
|
57
|
+
iconOnly
|
|
58
|
+
:class="ROW_ACTION_CLASSES"
|
|
59
|
+
:aria-label="$t('Delete chat')"
|
|
60
|
+
:title="$t('Delete chat')"
|
|
61
|
+
@click="agentStore.deleteSession(session.sessionId)"
|
|
62
|
+
>
|
|
63
|
+
<IconTrash2 />
|
|
64
|
+
</Button>
|
|
65
|
+
</template>
|
|
66
|
+
</div>
|
|
31
67
|
</div>
|
|
32
68
|
<p
|
|
33
69
|
v-if="!groupedSessions || groupedSessions.length === 0"
|
|
34
|
-
class="
|
|
70
|
+
class="flex flex-1 items-center justify-center px-4 text-sm text-lightInputIconColor dark:text-darkInputIconColor"
|
|
35
71
|
>
|
|
36
72
|
{{ $t('There are no previous chat sessions') }}
|
|
37
73
|
</p>
|
|
@@ -40,15 +76,41 @@
|
|
|
40
76
|
|
|
41
77
|
|
|
42
78
|
<script setup lang="ts">
|
|
43
|
-
import { Button,
|
|
44
|
-
import { computed } from 'vue';
|
|
45
|
-
import {
|
|
79
|
+
import { Button, Input } from '@/afcl'
|
|
80
|
+
import { computed, nextTick, ref, useTemplateRef } from 'vue';
|
|
81
|
+
import { IconPencil, IconTrash2 } from '@iconify-prerendered/vue-lucide';
|
|
46
82
|
import type { ISessionsListItem } from './types';
|
|
47
83
|
import { useAgentStore } from './composables/useAgentStore';
|
|
84
|
+
import { PRE_SESSION_ID } from './composables/agentStore/constants';
|
|
85
|
+
|
|
86
|
+
// row actions show on hover or keyboard focus of the row, and always on touch screens which have no hover
|
|
87
|
+
const ROW_ACTION_CLASSES = 'hidden group-hover:flex group-focus-within:flex [@media(hover:none)]:flex';
|
|
48
88
|
|
|
49
89
|
const agentStore = useAgentStore();
|
|
90
|
+
const renamingSessionId = ref<string | null>(null);
|
|
91
|
+
const renameTitle = ref('');
|
|
92
|
+
// the input is rendered inside v-for, so the template ref is an array with the one open input
|
|
93
|
+
const renameInputs = useTemplateRef<Array<{ focus: () => void }>>('renameInput');
|
|
94
|
+
|
|
95
|
+
async function startRename(session: ISessionsListItem) {
|
|
96
|
+
renamingSessionId.value = session.sessionId;
|
|
97
|
+
renameTitle.value = session.title;
|
|
98
|
+
await nextTick();
|
|
99
|
+
renameInputs.value![0].focus();
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// called by both enter and the blur that follows when the input goes away, only the first one saves
|
|
103
|
+
async function commitRename(session: ISessionsListItem) {
|
|
104
|
+
if (renamingSessionId.value !== session.sessionId) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
renamingSessionId.value = null;
|
|
108
|
+
const title = renameTitle.value.trim();
|
|
109
|
+
if (title && title !== session.title) {
|
|
110
|
+
await agentStore.renameSession(session.sessionId, title);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
50
113
|
|
|
51
|
-
const h3Style = "text-gray-800 dark:text-gray-200 font-medium text-xl tracking-widest my-2"
|
|
52
114
|
const dayLabelFormatter = new Intl.DateTimeFormat(undefined, { month: 'short', day: 'numeric' });
|
|
53
115
|
const dayLabelWithYearFormatter = new Intl.DateTimeFormat(undefined, { month: 'short', day: 'numeric', year: 'numeric' });
|
|
54
116
|
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
<div
|
|
16
16
|
v-for="item in agentStore.steerQueue"
|
|
17
17
|
:key="item.id"
|
|
18
|
-
class="flex items-center gap-2 rounded-lg border border-
|
|
18
|
+
class="flex items-center gap-2 rounded-lg border border-lightCardBorder bg-lightCardBackground py-1.5 pl-3 pr-1.5 shadow-popover dark:border-darkCardBorder dark:bg-darkCardBackground dark:shadow-darkPopover"
|
|
19
19
|
>
|
|
20
20
|
<span
|
|
21
21
|
class="flex-1 min-w-0 truncate text-sm text-lightInputText dark:text-darkInputText"
|
|
@@ -23,12 +23,13 @@
|
|
|
23
23
|
>
|
|
24
24
|
{{ item.text }}
|
|
25
25
|
</span>
|
|
26
|
-
<span class="shrink-0 text-xs text-
|
|
26
|
+
<span class="shrink-0 text-xs text-lightInputPlaceholderText dark:text-darkInputPlaceholderText">
|
|
27
27
|
{{ $t('queued') }}
|
|
28
28
|
</span>
|
|
29
|
-
<
|
|
29
|
+
<Button
|
|
30
|
+
variant="secondary"
|
|
31
|
+
size="sm"
|
|
30
32
|
type="button"
|
|
31
|
-
class="shrink-0 rounded-md px-2 py-1 text-xs font-medium text-lightPrimary transition-colors hover:bg-lightPrimary/10 disabled:cursor-not-allowed disabled:opacity-40 disabled:hover:bg-transparent dark:text-darkPrimary dark:hover:bg-darkPrimary/10 dark:disabled:hover:bg-transparent"
|
|
32
33
|
:disabled="agentStore.isFinalResponseStreaming"
|
|
33
34
|
:title="agentStore.isFinalResponseStreaming
|
|
34
35
|
? $t('The final response is streaming; this message will be sent after it finishes')
|
|
@@ -36,21 +37,26 @@
|
|
|
36
37
|
@click="agentStore.steerQueuedMessage(item.id)"
|
|
37
38
|
>
|
|
38
39
|
{{ $t('Steer') }}
|
|
39
|
-
</
|
|
40
|
-
<
|
|
40
|
+
</Button>
|
|
41
|
+
<Button
|
|
42
|
+
variant="ghost"
|
|
43
|
+
size="sm"
|
|
44
|
+
iconOnly
|
|
41
45
|
type="button"
|
|
42
|
-
class="shrink-0 flex h-6 w-6 items-center justify-center rounded-md text-lg leading-none text-gray-400 transition-colors hover:bg-gray-200 hover:text-gray-600 dark:hover:bg-gray-700 dark:hover:text-gray-300"
|
|
43
46
|
:title="$t('Remove from queue')"
|
|
47
|
+
:aria-label="$t('Remove from queue')"
|
|
44
48
|
@click="agentStore.removeQueuedMessage(item.id)"
|
|
45
49
|
>
|
|
46
|
-
|
|
47
|
-
</
|
|
50
|
+
<IconX />
|
|
51
|
+
</Button>
|
|
48
52
|
</div>
|
|
49
53
|
</TransitionGroup>
|
|
50
54
|
</div>
|
|
51
55
|
</template>
|
|
52
56
|
|
|
53
57
|
<script setup lang="ts">
|
|
58
|
+
import { IconX } from '@iconify-prerendered/vue-lucide';
|
|
59
|
+
import { Button } from '@/afcl';
|
|
54
60
|
import { useAgentStore } from './composables/useAgentStore';
|
|
55
61
|
|
|
56
62
|
const agentStore = useAgentStore();
|