@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
|
@@ -203,6 +203,26 @@ export function createAgentSessionManager({
|
|
|
203
203
|
setCurrentChat(PRE_SESSION_ID);
|
|
204
204
|
}
|
|
205
205
|
|
|
206
|
+
async function renameSession(sessionId: string, title: string) {
|
|
207
|
+
const res = await callAdminForthApi({
|
|
208
|
+
method: 'POST',
|
|
209
|
+
path: '/agent/rename-session',
|
|
210
|
+
body: { sessionId, title },
|
|
211
|
+
});
|
|
212
|
+
if (res.error) {
|
|
213
|
+
console.error('Error renaming session:', res.error);
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
sessionList.value.find((s: ISessionsListItem) => s.sessionId === sessionId)!.title = res.title;
|
|
217
|
+
// a loaded session is cached apart from the list, and the open one is shown in the header
|
|
218
|
+
if (sessions.value[sessionId]) {
|
|
219
|
+
sessions.value[sessionId].title = res.title;
|
|
220
|
+
}
|
|
221
|
+
if (currentSession.value?.sessionId === sessionId) {
|
|
222
|
+
currentSession.value.title = res.title;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
206
226
|
async function deleteSession(sessionId: string) {
|
|
207
227
|
if (sessionId === PRE_SESSION_ID) {
|
|
208
228
|
deletePreSession();
|
|
@@ -370,6 +390,7 @@ export function createAgentSessionManager({
|
|
|
370
390
|
createPreSession,
|
|
371
391
|
setActiveSession,
|
|
372
392
|
fetchSessionsList,
|
|
393
|
+
renameSession,
|
|
373
394
|
deleteSession,
|
|
374
395
|
addDebugMessage,
|
|
375
396
|
addSystemMessage,
|
|
@@ -198,6 +198,7 @@ export const useAgentStore = defineStore('agent', () => {
|
|
|
198
198
|
createPreSession,
|
|
199
199
|
setActiveSession,
|
|
200
200
|
fetchSessionsList,
|
|
201
|
+
renameSession,
|
|
201
202
|
deleteSession,
|
|
202
203
|
addDebugMessage,
|
|
203
204
|
addSystemMessage,
|
|
@@ -590,6 +591,7 @@ export const useAgentStore = defineStore('agent', () => {
|
|
|
590
591
|
sessionList,
|
|
591
592
|
setActiveSession,
|
|
592
593
|
fetchSessionsList,
|
|
594
|
+
renameSession,
|
|
593
595
|
deleteSession,
|
|
594
596
|
createPreSession,
|
|
595
597
|
//____________________________________________
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<div
|
|
6
6
|
v-if="agentStore.isSessionHistoryOpen"
|
|
7
7
|
@click="agentStore.setSessionHistoryOpen(false)"
|
|
8
|
-
class="absolute
|
|
8
|
+
class="absolute z-10 h-full w-full bg-lightScrim dark:bg-darkScrim"
|
|
9
9
|
>
|
|
10
10
|
</div>
|
|
11
11
|
<div class="relative flex-1 min-h-0 overflow-hidden [container-type:size]" @click="recalculateScroll()">
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
|
|
37
37
|
<div
|
|
38
38
|
v-for="(message, index) in props.messages" :key="index"
|
|
39
|
-
class="flex flex-col w-full
|
|
39
|
+
class="flex flex-col w-full pt-5 first:pt-4"
|
|
40
40
|
:class="message.role === 'user' ? 'self-end' : 'self-start'"
|
|
41
41
|
ref="messagesRefs"
|
|
42
42
|
>
|
|
@@ -50,25 +50,27 @@
|
|
|
50
50
|
<div class="flex items-center justify-center w-11 h-11 rounded-xl bg-lightPrimary/10 text-lightPrimary dark:bg-darkPrimary/15 dark:text-darkPrimary">
|
|
51
51
|
<IconSparkles class="w-6 h-6" />
|
|
52
52
|
</div>
|
|
53
|
-
<h2 class="mt-4 text-
|
|
53
|
+
<h2 class="mt-4 text-lg font-semibold tracking-tight text-lightNavbarText dark:text-darkNavbarText">
|
|
54
54
|
{{ $t('How can I help?') }}
|
|
55
55
|
</h2>
|
|
56
|
-
<p class="mt-1.5 max-w-xs text-sm leading-relaxed text-
|
|
56
|
+
<p class="mt-1.5 max-w-xs text-sm leading-relaxed text-lightInputIconColor dark:text-darkInputIconColor">
|
|
57
57
|
{{ $t('Ask about your data, build charts or update records.') }}
|
|
58
58
|
</p>
|
|
59
59
|
<div v-if="suggestions.length" class="mt-7 w-full max-w-sm flex flex-col gap-2 text-left">
|
|
60
|
-
<p class="pl-0.5 text-xs font-medium text-
|
|
60
|
+
<p class="pl-0.5 text-xs font-medium text-lightInputIconColor dark:text-darkInputIconColor">{{ $t('Try asking') }}</p>
|
|
61
61
|
<button
|
|
62
62
|
v-for="suggestion in suggestions"
|
|
63
63
|
:key="suggestion"
|
|
64
64
|
type="button"
|
|
65
|
-
class="group flex items-center gap-2.5 w-full px-3 py-2.5 rounded-
|
|
66
|
-
border-
|
|
67
|
-
|
|
65
|
+
class="group flex items-center gap-2.5 w-full px-3 py-2.5 rounded-lg border text-sm text-left transition-colors duration-150
|
|
66
|
+
border-lightCardBorder bg-lightCardBackground text-lightNavbarText hover:bg-lightCardBackgroundHover
|
|
67
|
+
focus:outline-none focus-visible:ring-[3px] focus-visible:ring-lightFocusRing
|
|
68
|
+
dark:border-darkCardBorder dark:bg-darkCardBackground dark:text-darkNavbarText dark:hover:bg-darkCardBackgroundHover
|
|
69
|
+
dark:focus-visible:ring-darkFocusRing"
|
|
68
70
|
@click="applySuggestion(suggestion)"
|
|
69
71
|
>
|
|
70
72
|
<span class="flex-1 min-w-0">{{ suggestion }}</span>
|
|
71
|
-
<IconArrowRight class="w-4 h-4 shrink-0 text-
|
|
73
|
+
<IconArrowRight class="w-4 h-4 shrink-0 text-lightInputIconColor transition-transform duration-150 group-hover:translate-x-0.5 dark:text-darkInputIconColor" />
|
|
72
74
|
</button>
|
|
73
75
|
</div>
|
|
74
76
|
</div>
|
|
@@ -84,18 +86,17 @@
|
|
|
84
86
|
<button
|
|
85
87
|
v-if="showScrollToBottomButton"
|
|
86
88
|
@click="scrollContainer.scrollToBottom();"
|
|
87
|
-
class="absolute z-10 bottom-8 left-1/2 -translate-x-1/2
|
|
88
|
-
bg-
|
|
89
|
-
|
|
90
|
-
text-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
:
|
|
89
|
+
class="absolute z-10 bottom-8 left-1/2 -translate-x-1/2 flex items-center justify-center gap-0.5 w-[2.125rem] h-[2.125rem] rounded-full border
|
|
90
|
+
border-lightInputBorder bg-lightInputBackground text-lightInputIconColor shadow-popover hover:text-lightInputText
|
|
91
|
+
focus:outline-none focus-visible:ring-[3px] focus-visible:ring-lightFocusRing
|
|
92
|
+
dark:border-darkInputBorder dark:bg-darkInputBackground dark:text-darkInputIconColor dark:shadow-darkPopover dark:hover:text-darkInputText
|
|
93
|
+
dark:focus-visible:ring-darkFocusRing"
|
|
94
|
+
:class="{ 'w-12': showAnimationInScrollToBottomButton }"
|
|
95
|
+
:aria-label="$t('Scroll to the latest message')"
|
|
94
96
|
>
|
|
95
|
-
<
|
|
97
|
+
<IconArrowDown
|
|
96
98
|
v-if="!showAnimationInScrollToBottomButton"
|
|
97
|
-
class="
|
|
98
|
-
:disabled="!showScrollToBottomButton"
|
|
99
|
+
class="w-4 h-4"
|
|
99
100
|
/>
|
|
100
101
|
<ThreeDotsAnimation
|
|
101
102
|
v-else
|
|
@@ -110,8 +111,7 @@
|
|
|
110
111
|
<script setup lang="ts">
|
|
111
112
|
import type { IMessage } from '../types';
|
|
112
113
|
import { useTemplateRef, ref, onMounted, onUnmounted, watch, nextTick, defineAsyncComponent, computed } from 'vue';
|
|
113
|
-
import {
|
|
114
|
-
import { IconArrowRight, IconSparkles } from '@iconify-prerendered/vue-heroicons';
|
|
114
|
+
import { IconArrowDown, IconArrowRight, IconSparkles } from '@iconify-prerendered/vue-lucide';
|
|
115
115
|
import SessionsHistory from '../SessionsHistory.vue';
|
|
116
116
|
import { useAgentStore } from '../composables/useAgentStore';
|
|
117
117
|
import { useAgentTransitions } from '../composables/useAgentTransitions';
|
|
@@ -18,34 +18,38 @@
|
|
|
18
18
|
>
|
|
19
19
|
<div
|
|
20
20
|
v-if="showActionsBar && showEditButton"
|
|
21
|
-
class="absolute flex -
|
|
21
|
+
class="absolute flex top-full -mt-1 p-1 z-20"
|
|
22
22
|
:class="isUserMessage ? 'right-0 self-end mr-4' : 'left-0 self-start ml-4'"
|
|
23
23
|
@mouseenter="showEditButtonOnHover"
|
|
24
24
|
@mouseleave="hideEditButtonOnHover"
|
|
25
25
|
>
|
|
26
|
-
<
|
|
26
|
+
<Button
|
|
27
27
|
v-if="canCopy"
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
variant="ghost"
|
|
29
|
+
size="sm"
|
|
30
|
+
iconOnly
|
|
30
31
|
:title="$t('Copy message')"
|
|
32
|
+
:aria-label="$t('Copy message')"
|
|
31
33
|
@click="copyMessageToClipboard"
|
|
32
34
|
>
|
|
33
|
-
<
|
|
34
|
-
</
|
|
35
|
-
<
|
|
35
|
+
<IconCopy />
|
|
36
|
+
</Button>
|
|
37
|
+
<Button
|
|
36
38
|
v-if="canEdit"
|
|
37
|
-
|
|
38
|
-
|
|
39
|
+
variant="ghost"
|
|
40
|
+
size="sm"
|
|
41
|
+
iconOnly
|
|
39
42
|
:title="$t('Edit message')"
|
|
43
|
+
:aria-label="$t('Edit message')"
|
|
40
44
|
@click="agentStore.startEditMessage(message)"
|
|
41
45
|
>
|
|
42
|
-
<
|
|
43
|
-
</
|
|
46
|
+
<IconPencil />
|
|
47
|
+
</Button>
|
|
44
48
|
</div>
|
|
45
49
|
</Transition>
|
|
46
50
|
<div
|
|
47
51
|
v-if="message.role === 'user' && message.metadata?.steer && !isBeingEditedTurn"
|
|
48
|
-
class="self-end mr-4 mb-0.5 text-xs italic text-
|
|
52
|
+
class="self-end mr-4 mb-0.5 text-xs italic text-lightInputPlaceholderText dark:text-darkInputPlaceholderText"
|
|
49
53
|
>
|
|
50
54
|
{{ $t('Steering instruction') }}
|
|
51
55
|
</div>
|
|
@@ -83,7 +87,8 @@
|
|
|
83
87
|
|
|
84
88
|
<script setup lang="ts">
|
|
85
89
|
import { computed, ref } from 'vue';
|
|
86
|
-
import {
|
|
90
|
+
import { IconCopy, IconPencil } from '@iconify-prerendered/vue-lucide';
|
|
91
|
+
import { Button } from '@/afcl';
|
|
87
92
|
import TextRenderer from './TextRenderer.vue';
|
|
88
93
|
import type { IMessage } from '../types';
|
|
89
94
|
import { getMessageParts } from '../utils';
|
|
@@ -99,10 +104,6 @@
|
|
|
99
104
|
isLastMessageInChat: boolean
|
|
100
105
|
}>();
|
|
101
106
|
|
|
102
|
-
const ACTION_BUTTON_CLASSES = `flex items-center justify-center w-7 h-7 rounded-md transition-colors duration-150
|
|
103
|
-
text-gray-500 hover:bg-gray-100 hover:text-gray-800
|
|
104
|
-
dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-gray-200`;
|
|
105
|
-
|
|
106
107
|
const agentStore = useAgentStore();
|
|
107
108
|
const { alert } = useAdminforth();
|
|
108
109
|
const showEditButton = ref(false);
|
|
@@ -2,21 +2,21 @@
|
|
|
2
2
|
<template v-if="timelineParts.length > 0 || inProgress">
|
|
3
3
|
<button
|
|
4
4
|
type="button"
|
|
5
|
-
class="z-20 self-start flex items-center gap-1
|
|
6
|
-
text-
|
|
7
|
-
:class="timelineParts.length > 0 ? 'hover:text-
|
|
5
|
+
class="z-20 self-start flex items-center gap-1 mx-4 mb-2 select-none text-[13px] font-medium transition-colors duration-150
|
|
6
|
+
text-lightInputIconColor dark:text-darkInputIconColor"
|
|
7
|
+
:class="timelineParts.length > 0 ? 'hover:text-lightInputText dark:hover:text-darkInputText' : 'cursor-default'"
|
|
8
8
|
@click="isExpanded = !isExpanded"
|
|
9
9
|
>
|
|
10
10
|
<IconChevronRight
|
|
11
11
|
v-if="timelineParts.length > 0"
|
|
12
|
-
class="w-
|
|
12
|
+
class="w-3.5 h-3.5 transition-transform duration-200"
|
|
13
13
|
:class="isExpanded ? 'rotate-90' : 'rotate-0'"
|
|
14
14
|
/>
|
|
15
15
|
<template v-if="inProgress">
|
|
16
16
|
<span
|
|
17
|
-
class="shine-text
|
|
18
|
-
bg-[linear-gradient(-65deg,currentColor,
|
|
19
|
-
dark:bg-[linear-gradient(-65deg,currentColor,
|
|
17
|
+
class="shine-text
|
|
18
|
+
bg-[linear-gradient(-65deg,currentColor,theme(colors.lightInputPlaceholderText)_50%,currentColor)]
|
|
19
|
+
dark:bg-[linear-gradient(-65deg,currentColor,theme(colors.darkInputText)_70%,currentColor)]"
|
|
20
20
|
>
|
|
21
21
|
{{ $t('Thinking') }}
|
|
22
22
|
</span>
|
|
@@ -34,8 +34,8 @@
|
|
|
34
34
|
<div
|
|
35
35
|
v-if="timelineParts.length > 0"
|
|
36
36
|
v-show="isExpanded"
|
|
37
|
-
class="z-20 mx-4
|
|
38
|
-
border-
|
|
37
|
+
class="z-20 mx-4 mb-3 overflow-hidden rounded-lg border
|
|
38
|
+
border-lightCardBorder bg-lightCardBackground dark:border-darkCardBorder dark:bg-darkCardBackground"
|
|
39
39
|
>
|
|
40
40
|
<CustomAutoScrollContainer
|
|
41
41
|
ref="scrollContainerRef"
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
<ToolRenderer v-else-if="part.type === 'data-tool-call'" :data="formatToolCallPart(part)" />
|
|
53
53
|
<li
|
|
54
54
|
v-else-if="part.type === 'data-rendering'"
|
|
55
|
-
class="flex items-center gap-2 py-0.5 text-[0.8125rem] text-
|
|
55
|
+
class="flex items-center gap-2 py-0.5 text-[0.8125rem] text-lightTableText dark:text-darkTableText"
|
|
56
56
|
>
|
|
57
57
|
<span class="flex items-center justify-center w-4 h-4 shrink-0">
|
|
58
58
|
<span class="w-2 h-2 rounded-full bg-current animate-pulse" />
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
<script setup lang="ts">
|
|
74
74
|
import type { IFormattedToolCallPart, IMessage, IPart } from '../types';
|
|
75
75
|
import { ref, computed, watch, onUnmounted, onMounted, defineAsyncComponent } from 'vue';
|
|
76
|
-
import { IconChevronRight } from '@iconify-prerendered/vue-
|
|
76
|
+
import { IconChevronRight } from '@iconify-prerendered/vue-lucide';
|
|
77
77
|
import ReasoningRenderer from './ReasoningRenderer.vue';
|
|
78
78
|
import ToolRenderer from './ToolRenderer.vue';
|
|
79
79
|
import ThreeDotsAnimation from './ThreeDotsAnimation.vue';
|
|
@@ -5,19 +5,19 @@
|
|
|
5
5
|
class="flex items-center gap-2 w-full py-0.5 text-left text-[0.8125rem] leading-5 select-none"
|
|
6
6
|
@click="isExpanded = !isExpanded"
|
|
7
7
|
>
|
|
8
|
-
<
|
|
9
|
-
<span class="min-w-0 truncate font-medium text-
|
|
8
|
+
<IconBrain class="w-4 h-4 shrink-0 text-lightInputIconColor dark:text-darkInputIconColor" />
|
|
9
|
+
<span class="min-w-0 truncate font-medium text-lightTableText dark:text-darkTableText">{{ reasoningTitle || $t('Reasoning') }}</span>
|
|
10
10
|
<ThreeDotsAnimation v-if="isStreaming"/>
|
|
11
11
|
<IconChevronDown
|
|
12
12
|
:class="isExpanded ? 'rotate-180' : 'rotate-0'"
|
|
13
|
-
class="ml-auto w-3.5 h-3.5 shrink-0 text-
|
|
13
|
+
class="ml-auto w-3.5 h-3.5 shrink-0 text-lightInputIconColor transition-transform duration-200 dark:text-darkInputIconColor"
|
|
14
14
|
/>
|
|
15
15
|
</button>
|
|
16
16
|
<transition name="expand">
|
|
17
17
|
<IncremarkContent
|
|
18
18
|
v-if="isExpanded"
|
|
19
19
|
v-show="isExpanded"
|
|
20
|
-
class="ml-6 mb-1 text-sm text-
|
|
20
|
+
class="ml-6 mb-1 text-sm text-lightInputIconColor dark:text-darkInputIconColor"
|
|
21
21
|
:content="reasoningText"
|
|
22
22
|
/>
|
|
23
23
|
</transition>
|
|
@@ -27,8 +27,7 @@
|
|
|
27
27
|
|
|
28
28
|
|
|
29
29
|
<script setup lang="ts">
|
|
30
|
-
import {
|
|
31
|
-
import { IconChevronDown } from '@iconify-prerendered/vue-heroicons';
|
|
30
|
+
import { IconBrain, IconChevronDown } from '@iconify-prerendered/vue-lucide';
|
|
32
31
|
import type { IPart } from '../types';
|
|
33
32
|
import { ref, computed, watch, defineAsyncComponent } from 'vue';
|
|
34
33
|
import ThreeDotsAnimation from './ThreeDotsAnimation.vue';
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<p :class="`${TEXT_CLASSES}`" v-if="message === RESERVED_SYSTEM_MESSAGE_CONTENT.START_AUDIO_CHAT">
|
|
3
|
-
<
|
|
3
|
+
<IconPhone class="inline-block w-4 h-4 mr-1" />
|
|
4
4
|
{{$t('Audio chat started')}}
|
|
5
5
|
</p>
|
|
6
6
|
<p :class="`${TEXT_CLASSES}`" v-else-if="message === RESERVED_SYSTEM_MESSAGE_CONTENT.END_AUDIO_CHAT">
|
|
7
|
-
<
|
|
7
|
+
<IconPhoneOff class="inline-block w-4 h-4 mr-1" />
|
|
8
8
|
{{$t('Audio chat ended')}}
|
|
9
9
|
</p>
|
|
10
10
|
<p :class="`${TEXT_CLASSES}`" v-else-if="message === RESERVED_SYSTEM_MESSAGE_CONTENT.AGENT_RESPONSE_ABORTED">
|
|
@@ -17,9 +17,9 @@
|
|
|
17
17
|
|
|
18
18
|
<script setup lang="ts">
|
|
19
19
|
import { RESERVED_SYSTEM_MESSAGE_CONTENT } from '../composables/agentStore/constants';
|
|
20
|
-
import {
|
|
20
|
+
import { IconPhone, IconPhoneOff } from '@iconify-prerendered/vue-lucide';
|
|
21
21
|
|
|
22
|
-
const TEXT_CLASSES = '
|
|
22
|
+
const TEXT_CLASSES = 'flex items-center justify-center text-center text-sm italic text-lightInputIconColor dark:text-darkInputIconColor';
|
|
23
23
|
|
|
24
24
|
const props = defineProps<{
|
|
25
25
|
message: string
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div
|
|
3
|
-
class="flex items-center justify-center min-w-0 transition-shadow duration-150 z-20"
|
|
3
|
+
class="flex items-center justify-center min-w-0 text-sm transition-shadow duration-150 z-20"
|
|
4
4
|
@click="handleMarkdownLinkClick"
|
|
5
5
|
:class="[
|
|
6
|
-
hasVegaLite
|
|
6
|
+
hasVegaLite && 'w-full',
|
|
7
7
|
props.role === 'user'
|
|
8
|
-
? 'self-end max-w-[82%] mr-4 px-3
|
|
8
|
+
? 'self-end max-w-[82%] mr-4 px-3 py-2 rounded-xl rounded-br [&_.incremark-paragraph]:leading-5 bg-lightSecondaryDarken dark:bg-darkSecondaryLighten'
|
|
9
9
|
: 'self-start max-w-full px-4',
|
|
10
|
-
props.editMode ?
|
|
10
|
+
props.editMode ? `w-full max-w-[calc(100%-2rem)] flex-col items-stretch py-3 border bg-lightInputBackground border-lightInputFocusBorder ring-[3px] ring-lightInputFocusRing
|
|
11
|
+
dark:bg-darkInputBackground dark:border-darkInputFocusBorder dark:ring-darkInputFocusRing` : ''
|
|
11
12
|
]"
|
|
12
13
|
>
|
|
13
14
|
<IncremarkContent
|
|
@@ -19,13 +20,10 @@
|
|
|
19
20
|
:components="incremarkComponents"
|
|
20
21
|
:incremark-options="incremarkOptions"
|
|
21
22
|
/>
|
|
22
|
-
<!-- <p v-else class="text-red-500 py-2">
|
|
23
|
-
{{ $t('No content to render') }}
|
|
24
|
-
</p> -->
|
|
25
23
|
<template v-if="props.editMode">
|
|
26
24
|
<textarea
|
|
27
25
|
v-model="agentStore.userMessageInput"
|
|
28
|
-
class="w-full min-h-24 resize-y bg-transparent text-sm text-
|
|
26
|
+
class="w-full min-h-24 resize-y bg-transparent text-sm text-lightInputText dark:text-darkInputText border-none focus:outline-none focus:ring-0 p-0"
|
|
29
27
|
@keydown.esc.prevent="agentStore.cancelEditMessage"
|
|
30
28
|
/>
|
|
31
29
|
<EditActionButtons
|
|
@@ -155,31 +153,55 @@
|
|
|
155
153
|
</script>
|
|
156
154
|
|
|
157
155
|
<style lang="scss">
|
|
156
|
+
// the incremark theme ships its own fonts and palette, point them to the design tokens
|
|
157
|
+
.incremark {
|
|
158
|
+
--incremark-typography-font-family-base: theme('fontFamily.sans');
|
|
159
|
+
--incremark-typography-font-family-mono: theme('fontFamily.mono');
|
|
160
|
+
--incremark-color-text-primary: theme('colors.lightListTableText');
|
|
161
|
+
--incremark-color-text-secondary: theme('colors.lightInputIconColor');
|
|
162
|
+
--incremark-color-text-tertiary: theme('colors.lightInputPlaceholderText');
|
|
163
|
+
--incremark-color-border-default: theme('colors.lightCardBorder');
|
|
164
|
+
--incremark-color-border-strong: theme('colors.lightCardBorder');
|
|
165
|
+
--incremark-color-border-subtle: theme('colors.lightCardBorder');
|
|
166
|
+
--incremark-color-background-base: theme('colors.lightCardBackground');
|
|
167
|
+
--incremark-color-background-elevated: theme('colors.lightCardBackgroundHover');
|
|
168
|
+
--incremark-color-code-inline-background: theme('colors.lightSecondaryDarken');
|
|
169
|
+
--incremark-color-code-inline-text: theme('colors.lightInputText');
|
|
170
|
+
--incremark-color-brand-primary: theme('colors.lightPrimary');
|
|
171
|
+
--incremark-color-interactive-link: theme('colors.lightPrimary');
|
|
172
|
+
--incremark-color-interactive-link-hover: theme('colors.lightPrimary');
|
|
173
|
+
--incremark-color-interactive-link-visited: theme('colors.lightPrimary');
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
.dark .incremark {
|
|
177
|
+
--incremark-color-text-primary: theme('colors.darkListTableText');
|
|
178
|
+
--incremark-color-text-secondary: theme('colors.darkInputIconColor');
|
|
179
|
+
--incremark-color-text-tertiary: theme('colors.darkInputPlaceholderText');
|
|
180
|
+
--incremark-color-border-default: theme('colors.darkCardBorder');
|
|
181
|
+
--incremark-color-border-strong: theme('colors.darkCardBorder');
|
|
182
|
+
--incremark-color-border-subtle: theme('colors.darkCardBorder');
|
|
183
|
+
--incremark-color-background-base: theme('colors.darkCardBackground');
|
|
184
|
+
--incremark-color-background-elevated: theme('colors.darkCardBackgroundHover');
|
|
185
|
+
--incremark-color-code-inline-background: theme('colors.darkSecondaryLighten');
|
|
186
|
+
--incremark-color-code-inline-text: theme('colors.darkInputText');
|
|
187
|
+
--incremark-color-brand-primary: theme('colors.darkPrimary');
|
|
188
|
+
--incremark-color-interactive-link: theme('colors.darkPrimary');
|
|
189
|
+
--incremark-color-interactive-link-hover: theme('colors.darkPrimary');
|
|
190
|
+
--incremark-color-interactive-link-visited: theme('colors.darkPrimary');
|
|
191
|
+
}
|
|
192
|
+
|
|
158
193
|
.incremark a.incremark-link,
|
|
159
194
|
.incremark a.incremark-link:visited {
|
|
195
|
+
@apply text-lightPrimary dark:text-darkPrimary;
|
|
160
196
|
display: inline-block;
|
|
161
197
|
text-decoration: underline;
|
|
162
198
|
text-underline-offset: 4px;
|
|
163
|
-
text-decoration-style:dotted;
|
|
164
|
-
color: rgb(0, 0, 0);
|
|
165
|
-
transition: color 0.2s ease;
|
|
199
|
+
text-decoration-style: dotted;
|
|
166
200
|
word-break: break-all;
|
|
167
201
|
}
|
|
168
202
|
|
|
169
203
|
.incremark a.incremark-link:hover {
|
|
170
|
-
|
|
171
|
-
text-decoration: underline;
|
|
172
|
-
text-underline-offset: 4px;
|
|
173
|
-
text-decoration-style:dotted;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
html[data-theme="dark"] .incremark a.incremark-link,
|
|
177
|
-
html[data-theme="dark"] .incremark a.incremark-link:visited {
|
|
178
|
-
color: rgb(220, 220, 220);
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
html[data-theme="dark"] .incremark a.incremark-link:hover {
|
|
182
|
-
color: rgb(147, 147, 255);
|
|
204
|
+
text-decoration-style: solid;
|
|
183
205
|
}
|
|
184
206
|
|
|
185
207
|
a.incremark-link::after {
|
|
@@ -198,11 +220,27 @@ a.incremark-link::after {
|
|
|
198
220
|
word-break: break-word;
|
|
199
221
|
}
|
|
200
222
|
|
|
201
|
-
.incremark
|
|
223
|
+
// scoped under .incremark: the incremark theme is imported lazily and would win an equal selector
|
|
224
|
+
.incremark .incremark-list {
|
|
202
225
|
list-style: disc;
|
|
226
|
+
margin: 0 0 8px;
|
|
227
|
+
padding-left: 18px;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
.incremark .incremark-list-item {
|
|
231
|
+
margin: 2px 0;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
.incremark .incremark-paragraph {
|
|
235
|
+
margin: 0 0 8px;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
.incremark .incremark-list-item > .incremark-paragraph {
|
|
239
|
+
margin: 0;
|
|
203
240
|
}
|
|
204
241
|
|
|
205
|
-
|
|
206
|
-
|
|
242
|
+
// the gap between messages comes from the conversation, not from the last block of a message
|
|
243
|
+
.incremark > .incremark-block:last-child > :last-child {
|
|
244
|
+
margin-bottom: 0;
|
|
207
245
|
}
|
|
208
246
|
</style>
|
|
@@ -1,29 +1,26 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div
|
|
3
|
-
class="mx-4 my-2 max-w-[min(34rem,calc(100%-2rem))] z-20 rounded-
|
|
3
|
+
class="af-agent-approval mx-4 my-2 max-w-[min(34rem,calc(100%-2rem))] z-20 rounded-lg border p-3.5"
|
|
4
4
|
:class="isAwaitingDecision
|
|
5
|
-
? 'border-
|
|
6
|
-
: 'border-
|
|
5
|
+
? 'border-lightWarningBorder bg-lightWarningSoft dark:border-darkWarningBorder dark:bg-darkWarningSoft'
|
|
6
|
+
: 'border-lightCardBorder bg-lightCardBackground dark:border-darkCardBorder dark:bg-darkCardBackground'"
|
|
7
7
|
>
|
|
8
8
|
<div class="flex items-start gap-2.5">
|
|
9
|
-
<
|
|
10
|
-
class="
|
|
11
|
-
:class="isAwaitingDecision
|
|
12
|
-
|
|
13
|
-
: 'bg-gray-200 text-gray-600 dark:bg-gray-700 dark:text-gray-300'"
|
|
14
|
-
>
|
|
15
|
-
<IconShieldExclamation class="h-4 w-4" />
|
|
16
|
-
</span>
|
|
9
|
+
<IconShieldAlert
|
|
10
|
+
class="mt-0.5 h-4 w-4 shrink-0"
|
|
11
|
+
:class="isAwaitingDecision ? 'text-lightWarning dark:text-darkWarning' : 'text-lightInputIconColor dark:text-darkInputIconColor'"
|
|
12
|
+
/>
|
|
17
13
|
<div class="min-w-0 flex-1">
|
|
18
|
-
<h3 class="text-sm font-semibold leading-5 text-
|
|
19
|
-
<p class="text-[0.8125rem] leading-5 text-
|
|
14
|
+
<h3 class="text-sm font-semibold leading-5 text-lightCardTitle dark:text-darkCardTitle">{{ $t('Approval needed') }}</h3>
|
|
15
|
+
<p class="text-[0.8125rem] leading-5 text-lightCardDescription dark:text-darkCardDescription">
|
|
20
16
|
{{ $t('Review what the agent is about to do before continuing.') }}
|
|
21
17
|
</p>
|
|
22
18
|
</div>
|
|
23
19
|
</div>
|
|
24
20
|
<ul
|
|
25
|
-
class="mt-3 divide-y rounded-
|
|
26
|
-
divide-
|
|
21
|
+
class="mt-3 divide-y rounded-default border text-[0.8125rem] leading-5
|
|
22
|
+
divide-lightCardBorder border-lightCardBorder bg-lightCardBackground text-lightTableText
|
|
23
|
+
dark:divide-darkCardBorder dark:border-darkCardBorder dark:bg-darkCardBackground dark:text-darkTableText"
|
|
27
24
|
>
|
|
28
25
|
<li
|
|
29
26
|
v-for="(approval, approvalIndex) in data.approvals"
|
|
@@ -36,19 +33,19 @@
|
|
|
36
33
|
class="mt-2 grid grid-cols-[minmax(0,auto)_minmax(0,1fr)] gap-x-4 gap-y-1.5"
|
|
37
34
|
>
|
|
38
35
|
<template v-for="(field, fieldIndex) in approval.fields" :key="fieldIndex">
|
|
39
|
-
<dt class="text-
|
|
36
|
+
<dt class="text-lightInputIconColor dark:text-darkInputIconColor [overflow-wrap:anywhere]">{{ field.label }}</dt>
|
|
40
37
|
<dd class="flex min-w-0 flex-wrap items-center gap-1.5">
|
|
41
38
|
<template v-if="field.from !== undefined && field.from !== field.to">
|
|
42
39
|
<span
|
|
43
40
|
class="min-w-0 rounded px-1.5 line-through [overflow-wrap:anywhere]
|
|
44
|
-
bg-
|
|
41
|
+
bg-lightDangerSoft text-lightDanger decoration-lightDanger/50 dark:bg-darkDangerSoft dark:text-darkDanger dark:decoration-darkDanger/50"
|
|
45
42
|
>
|
|
46
43
|
{{ field.from }}
|
|
47
44
|
</span>
|
|
48
|
-
<IconArrowRight class="h-3.5 w-3.5 shrink-0 text-
|
|
45
|
+
<IconArrowRight class="h-3.5 w-3.5 shrink-0 text-lightInputIconColor dark:text-darkInputIconColor" />
|
|
49
46
|
<span
|
|
50
47
|
class="min-w-0 rounded px-1.5 font-medium [overflow-wrap:anywhere]
|
|
51
|
-
bg-
|
|
48
|
+
bg-lightSuccessSoft text-lightSuccess dark:bg-darkSuccessSoft dark:text-darkSuccess"
|
|
52
49
|
>
|
|
53
50
|
{{ field.to }}
|
|
54
51
|
</span>
|
|
@@ -61,39 +58,28 @@
|
|
|
61
58
|
</ul>
|
|
62
59
|
<div class="mt-3 flex flex-wrap gap-2">
|
|
63
60
|
<template v-if="data.status === 'pending'">
|
|
64
|
-
<button
|
|
65
|
-
type="button"
|
|
66
|
-
class="inline-flex h-8 items-center gap-1.5 rounded-lg px-3 text-[0.8125rem] font-medium transition-opacity duration-150
|
|
67
|
-
bg-lightPrimary text-lightPrimaryContrast hover:opacity-90 dark:bg-darkPrimary dark:text-darkPrimaryContrast"
|
|
68
|
-
@click="submit('approve')"
|
|
69
|
-
>
|
|
61
|
+
<Button size="sm" type="button" @click="submit('approve')">
|
|
70
62
|
<IconCheck class="h-4 w-4" />
|
|
71
63
|
{{ $t('Approve') }}
|
|
72
|
-
</
|
|
73
|
-
<button
|
|
74
|
-
type="button"
|
|
75
|
-
class="inline-flex h-8 items-center rounded-lg border px-3 text-[0.8125rem] font-medium transition-colors duration-150
|
|
76
|
-
border-gray-300 bg-white text-gray-700 hover:bg-gray-50
|
|
77
|
-
dark:border-gray-600 dark:bg-transparent dark:text-gray-200 dark:hover:bg-gray-700/50"
|
|
78
|
-
@click="submit('reject')"
|
|
79
|
-
>
|
|
64
|
+
</Button>
|
|
65
|
+
<Button variant="secondary" size="sm" type="button" @click="submit('reject')">
|
|
80
66
|
{{ $t('Reject') }}
|
|
81
|
-
</
|
|
67
|
+
</Button>
|
|
82
68
|
</template>
|
|
83
69
|
<span
|
|
84
70
|
v-else
|
|
85
|
-
class="inline-flex h-
|
|
86
|
-
|
|
71
|
+
class="inline-flex h-6 items-center gap-1.5 rounded-full border px-2 text-xs font-medium"
|
|
72
|
+
:class="STATUS_BADGE_CLASSES[data.status]"
|
|
87
73
|
>
|
|
88
|
-
<Spinner v-if="data.status === 'processing'" class="h-
|
|
89
|
-
<IconCheck v-else-if="data.status === 'approved'" class="h-
|
|
90
|
-
<
|
|
74
|
+
<Spinner v-if="data.status === 'processing'" class="h-3.5 w-3.5" />
|
|
75
|
+
<IconCheck v-else-if="data.status === 'approved'" class="h-3.5 w-3.5" />
|
|
76
|
+
<IconX v-else class="h-3.5 w-3.5" />
|
|
91
77
|
{{ data.status === 'processing' ? $t('Processing') : data.status === 'approved' ? $t('Approved') : $t('Rejected') }}
|
|
92
78
|
</span>
|
|
93
79
|
</div>
|
|
94
80
|
<p
|
|
95
81
|
v-if="data.error"
|
|
96
|
-
class="mt-3 text-sm leading-5 text-
|
|
82
|
+
class="mt-3 text-sm leading-5 text-lightDanger dark:text-darkDanger"
|
|
97
83
|
>
|
|
98
84
|
{{ data.error }}
|
|
99
85
|
</p>
|
|
@@ -104,13 +90,19 @@
|
|
|
104
90
|
import { computed } from 'vue';
|
|
105
91
|
import type { IPartData } from '../types';
|
|
106
92
|
import { useAgentStore } from '../composables/useAgentStore';
|
|
107
|
-
import { Spinner } from '@/afcl';
|
|
108
|
-
import { IconArrowRight, IconCheck,
|
|
93
|
+
import { Button, Spinner } from '@/afcl';
|
|
94
|
+
import { IconArrowRight, IconCheck, IconShieldAlert, IconX } from '@iconify-prerendered/vue-lucide';
|
|
109
95
|
|
|
110
96
|
const props = defineProps<{
|
|
111
97
|
data: IPartData;
|
|
112
98
|
}>();
|
|
113
99
|
|
|
100
|
+
const STATUS_BADGE_CLASSES: Record<string, string> = {
|
|
101
|
+
processing: 'border-transparent bg-lightSecondaryDarken text-lightInputIconColor dark:bg-darkSecondaryLighten dark:text-darkInputIconColor',
|
|
102
|
+
approved: 'border-lightSuccessBorder bg-lightSuccessSoft text-lightSuccess dark:border-darkSuccessBorder dark:bg-darkSuccessSoft dark:text-darkSuccess',
|
|
103
|
+
rejected: 'border-lightDangerBorder bg-lightDangerSoft text-lightDanger dark:border-darkDangerBorder dark:bg-darkDangerSoft dark:text-darkDanger',
|
|
104
|
+
};
|
|
105
|
+
|
|
114
106
|
const agentStore = useAgentStore();
|
|
115
107
|
|
|
116
108
|
const isAwaitingDecision = computed(() => props.data.status === 'pending' || props.data.status === 'processing');
|