@adminforth/agent 2.0.10 → 2.2.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/README.md +333 -10
- package/dist/application/ports.d.ts +66 -0
- package/dist/application/runTurnUseCase.d.ts +71 -0
- package/dist/application/runTurnUseCase.js +364 -0
- package/dist/custom/ChatFooter.vue +58 -30
- package/dist/custom/SteerDebugPanel.vue +45 -0
- package/dist/custom/SteerQueue.vue +54 -0
- package/dist/custom/composables/agentStore/constants.ts +1 -0
- package/dist/custom/composables/agentStore/useAgentSessions.ts +35 -16
- package/dist/custom/composables/agentStore/useAgentSteerQueue.ts +114 -0
- package/dist/custom/composables/useAgentStore.ts +46 -1
- package/dist/custom/conversation_area/MessageRenderer.vue +7 -1
- package/dist/custom/speech_recognition_frontend/AudioLines.vue +1 -1
- package/dist/custom/speech_recognition_frontend/MicrophoneButon.vue +3 -3
- package/dist/{agentEvents.d.ts → domain/agentEvents.d.ts} +5 -1
- package/dist/domain/steerBuffer.d.ts +33 -0
- package/dist/domain/steerBuffer.js +51 -0
- package/dist/{agent → domain}/systemPrompt.js +1 -1
- package/dist/{agent/turn → domain}/turnTypes.d.ts +41 -37
- package/dist/{agent/turn/VegaLiteStreamBuffer.d.ts → domain/vegaLiteStreamBuffer.d.ts} +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.js +49 -35
- package/dist/{agent/runtime/AgentContext.d.ts → llm/agentContext.d.ts} +3 -3
- package/dist/{agent/simpleAgent.d.ts → llm/agentModels.d.ts} +18 -16
- package/dist/llm/agentModels.js +65 -0
- package/dist/{agent/runtime/AgentRuntime.d.ts → llm/agentRuntime.d.ts} +14 -2
- package/dist/{agent/runtime/AgentRuntime.js → llm/agentRuntime.js} +41 -6
- package/dist/llm/langGraphLlm.d.ts +25 -0
- package/dist/llm/langGraphLlm.js +54 -0
- package/dist/{agent/middleware/apiBasedTools.d.ts → llm/middleware/apiToolsMiddleware.d.ts} +1 -1
- package/dist/{agent/middleware/apiBasedTools.js → llm/middleware/apiToolsMiddleware.js} +8 -14
- package/dist/{agent → llm}/middleware/sequenceDebug.d.ts +1 -1
- package/dist/llm/middleware/steerMiddleware.d.ts +21 -0
- package/dist/llm/middleware/steerMiddleware.js +50 -0
- package/dist/{agent/models/AgentModelFactory.d.ts → llm/modelFactory.d.ts} +1 -1
- package/dist/{agent/models/AgentModelFactory.js → llm/modelFactory.js} +1 -1
- package/dist/llm/streamAdapter.d.ts +18 -0
- package/dist/llm/streamAdapter.js +117 -0
- package/dist/{sessionStore.d.ts → persistence/sessionStore.d.ts} +19 -2
- package/dist/{sessionStore.js → persistence/sessionStore.js} +53 -0
- package/dist/{agent/tools/AgentToolProvider.d.ts → tools/agentToolProvider.d.ts} +1 -1
- package/dist/{agent/tools/AgentToolProvider.js → tools/agentToolProvider.js} +1 -1
- package/dist/{apiBasedTools.js → tools/apiBasedTools.js} +0 -3
- package/dist/{agent/tools → tools}/apiTool.d.ts +1 -1
- package/dist/{agent/tools → tools}/fetchSkill.js +1 -1
- package/dist/{agent/tools → tools}/fetchToolSchema.d.ts +1 -1
- package/dist/{agent/tools → tools}/index.d.ts +1 -1
- package/dist/{agent/tools → tools}/navigateUser.d.ts +3 -3
- package/dist/{endpoints → transport/http}/context.d.ts +12 -4
- package/dist/transport/http/context.js +1 -0
- package/dist/{endpoints/core.js → transport/http/coreEndpoints.js} +23 -3
- package/dist/{endpoints/sessions.js → transport/http/sessionEndpoints.js} +36 -1
- package/dist/transport/sse/sseWriter.d.ts +29 -0
- package/dist/{surfaces/web-sse/createSseEventEmitter.js → transport/sse/sseWriter.js} +27 -0
- package/dist/{chatSurfaceService.d.ts → transport/surfaces/chatSurfaceService.d.ts} +3 -3
- package/dist/{chatSurfaceService.js → transport/surfaces/chatSurfaceService.js} +7 -5
- package/dist/{agent/speech/SpeechTurnService.d.ts → transport/surfaces/speechTurnService.d.ts} +1 -1
- package/dist/{agent/speech/SpeechTurnService.js → transport/surfaces/speechTurnService.js} +2 -2
- package/dist/types.d.ts +10 -4
- package/package.json +11 -2
- package/.woodpecker/buildRelease.sh +0 -13
- package/.woodpecker/buildSlackNotify.sh +0 -46
- package/.woodpecker/release.yml +0 -57
- package/agent/checkpointer.ts +0 -329
- package/agent/languageDetect.ts +0 -92
- package/agent/middleware/apiBasedTools.ts +0 -174
- package/agent/middleware/openAiResponsesContinuation.ts +0 -92
- package/agent/middleware/sequenceDebug.ts +0 -429
- package/agent/models/AgentModeResolver.ts +0 -9
- package/agent/models/AgentModelFactory.ts +0 -28
- package/agent/runtime/AgentContext.ts +0 -30
- package/agent/runtime/AgentRuntime.ts +0 -89
- package/agent/simpleAgent.ts +0 -199
- package/agent/skills/registry.ts +0 -162
- package/agent/speech/SpeechTurnService.ts +0 -179
- package/agent/systemPrompt.ts +0 -133
- package/agent/toolCallEvents.ts +0 -135
- package/agent/tools/AgentToolProvider.ts +0 -28
- package/agent/tools/apiTool.ts +0 -66
- package/agent/tools/fetchSkill.ts +0 -68
- package/agent/tools/fetchToolSchema.ts +0 -47
- package/agent/tools/getUserLocation.ts +0 -45
- package/agent/tools/index.ts +0 -31
- package/agent/tools/navigateUser.ts +0 -210
- package/agent/turn/TurnContextBuilder.ts +0 -36
- package/agent/turn/TurnLifecycleService.ts +0 -47
- package/agent/turn/TurnPersistenceService.ts +0 -33
- package/agent/turn/TurnPromptBuilder.ts +0 -51
- package/agent/turn/TurnStreamConsumer.ts +0 -69
- package/agent/turn/VegaLiteStreamBuffer.ts +0 -90
- package/agent/turn/turnTypes.ts +0 -100
- package/agentEvents.ts +0 -75
- package/agentTurnService.ts +0 -314
- package/apiBasedTools.ts +0 -712
- package/build.log +0 -67
- package/chatSurfaceService.ts +0 -319
- package/custom/ChatFooter.vue +0 -173
- package/custom/ChatHeader.vue +0 -78
- package/custom/ChatSurface.vue +0 -160
- package/custom/CustomAutoScrollContainer.vue +0 -164
- package/custom/SessionsHistory.vue +0 -88
- package/custom/chat.ts +0 -82
- package/custom/composables/agentAudio/utils.ts +0 -220
- package/custom/composables/agentStore/constants.ts +0 -19
- package/custom/composables/agentStore/pageContext.ts +0 -8
- package/custom/composables/agentStore/useAgentChat.ts +0 -241
- package/custom/composables/agentStore/useAgentPlaceholder.ts +0 -142
- package/custom/composables/agentStore/useAgentSessions.ts +0 -350
- package/custom/composables/useAgentAudio.ts +0 -272
- package/custom/composables/useAgentStore.ts +0 -510
- package/custom/composables/useAgentTransitions.ts +0 -58
- package/custom/conversation_area/ConversationArea.vue +0 -301
- package/custom/conversation_area/MessageRenderer.vue +0 -49
- package/custom/conversation_area/ProcessingTimeline.vue +0 -286
- package/custom/conversation_area/ReasoningRenderer.vue +0 -79
- package/custom/conversation_area/SystemMessageRenderer.vue +0 -28
- package/custom/conversation_area/TextRenderer.vue +0 -182
- package/custom/conversation_area/ThreeDotsAnimation.vue +0 -35
- package/custom/conversation_area/ToolApprovalRenderer.vue +0 -98
- package/custom/conversation_area/ToolRenderer.vue +0 -178
- package/custom/conversation_area/ToolsGroup.vue +0 -59
- package/custom/env.d.ts +0 -7
- package/custom/incremark_code_renderers/IncremarkShikiCodeBlock.vue +0 -393
- package/custom/incremark_code_renderers/incremarkCodeHighlight.ts +0 -285
- package/custom/incremark_code_renderers/incremarkRenderer.ts +0 -653
- package/custom/incremark_code_renderers/renderIncremarkMarkdown.ts +0 -118
- package/custom/package.json +0 -30
- package/custom/pnpm-lock.yaml +0 -2394
- package/custom/public/agentAudio/agent-processing.mp3 +0 -0
- package/custom/skills/analyze_data/SKILL.md +0 -25
- package/custom/skills/charts/SKILL.md +0 -193
- package/custom/skills/fetch_data/SKILL.md +0 -31
- package/custom/skills/mutate_data/SKILL.md +0 -121
- package/custom/speech_recognition_frontend/AudioLines.vue +0 -43
- package/custom/speech_recognition_frontend/MicrophoneButon.vue +0 -185
- package/custom/speech_recognition_frontend/types/voice-activity-detection.d.ts +0 -22
- package/custom/speech_recognition_frontend/voiceActivityDetection.ts +0 -62
- package/custom/tsconfig.json +0 -15
- package/custom/types.ts +0 -108
- package/custom/utils.ts +0 -38
- package/dist/agent/middleware/openAiResponsesContinuation.d.ts +0 -1
- package/dist/agent/middleware/openAiResponsesContinuation.js +0 -66
- package/dist/agent/models/AgentModeResolver.d.ts +0 -9
- package/dist/agent/models/AgentModeResolver.js +0 -9
- package/dist/agent/simpleAgent.js +0 -118
- package/dist/agent/turn/TurnContextBuilder.d.ts +0 -14
- package/dist/agent/turn/TurnContextBuilder.js +0 -31
- package/dist/agent/turn/TurnLifecycleService.d.ts +0 -24
- package/dist/agent/turn/TurnLifecycleService.js +0 -47
- package/dist/agent/turn/TurnPersistenceService.d.ts +0 -13
- package/dist/agent/turn/TurnPersistenceService.js +0 -35
- package/dist/agent/turn/TurnPromptBuilder.d.ts +0 -19
- package/dist/agent/turn/TurnPromptBuilder.js +0 -43
- package/dist/agent/turn/TurnStreamConsumer.d.ts +0 -11
- package/dist/agent/turn/TurnStreamConsumer.js +0 -84
- package/dist/agentTurnService.d.ts +0 -24
- package/dist/agentTurnService.js +0 -269
- package/dist/surfaces/web-sse/createSseEventEmitter.d.ts +0 -14
- package/endpoints/chatSurfaces.ts +0 -66
- package/endpoints/context.ts +0 -52
- package/endpoints/core.ts +0 -181
- package/endpoints/sessions.ts +0 -183
- package/errors.ts +0 -10
- package/index.ts +0 -192
- package/pnpm-workspace.yaml +0 -4
- package/sanitizeSpeechText.ts +0 -32
- package/sessionStore.ts +0 -105
- package/surfaces/web-sse/createSseEventEmitter.ts +0 -311
- package/tsconfig.json +0 -14
- package/types.ts +0 -127
- /package/dist/{agent/turn/turnTypes.js → application/ports.js} +0 -0
- /package/dist/{agentEvents.js → domain/agentEvents.js} +0 -0
- /package/dist/{agent → domain}/languageDetect.d.ts +0 -0
- /package/dist/{agent → domain}/languageDetect.js +0 -0
- /package/dist/{agent → domain}/systemPrompt.d.ts +0 -0
- /package/dist/{agent → domain}/toolCallEvents.d.ts +0 -0
- /package/dist/{agent → domain}/toolCallEvents.js +0 -0
- /package/dist/{endpoints/context.js → domain/turnTypes.js} +0 -0
- /package/dist/{agent/turn/VegaLiteStreamBuffer.js → domain/vegaLiteStreamBuffer.js} +0 -0
- /package/dist/{agent/runtime/AgentContext.js → llm/agentContext.js} +0 -0
- /package/dist/{agent → llm}/middleware/sequenceDebug.js +0 -0
- /package/dist/{agent/checkpointer.d.ts → persistence/checkpointStore.d.ts} +0 -0
- /package/dist/{agent/checkpointer.js → persistence/checkpointStore.js} +0 -0
- /package/dist/{errors.d.ts → shared/errors.d.ts} +0 -0
- /package/dist/{errors.js → shared/errors.js} +0 -0
- /package/dist/{sanitizeSpeechText.d.ts → shared/sanitizeSpeechText.d.ts} +0 -0
- /package/dist/{sanitizeSpeechText.js → shared/sanitizeSpeechText.js} +0 -0
- /package/dist/{apiBasedTools.d.ts → tools/apiBasedTools.d.ts} +0 -0
- /package/dist/{agent/tools → tools}/apiTool.js +0 -0
- /package/dist/{agent/tools → tools}/fetchSkill.d.ts +0 -0
- /package/dist/{agent/tools → tools}/fetchToolSchema.js +0 -0
- /package/dist/{agent/tools → tools}/getUserLocation.d.ts +0 -0
- /package/dist/{agent/tools → tools}/getUserLocation.js +0 -0
- /package/dist/{agent/tools → tools}/index.js +0 -0
- /package/dist/{agent/tools → tools}/navigateUser.js +0 -0
- /package/dist/{agent → tools}/skills/registry.d.ts +0 -0
- /package/dist/{agent → tools}/skills/registry.js +0 -0
- /package/dist/{endpoints/chatSurfaces.d.ts → transport/http/chatSurfaceEndpoints.d.ts} +0 -0
- /package/dist/{endpoints/chatSurfaces.js → transport/http/chatSurfaceEndpoints.js} +0 -0
- /package/dist/{endpoints/core.d.ts → transport/http/coreEndpoints.d.ts} +0 -0
- /package/dist/{endpoints/sessions.d.ts → transport/http/sessionEndpoints.d.ts} +0 -0
|
@@ -1,301 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<SessionsHistory
|
|
3
|
-
:class="agentStore.isSessionHistoryOpen ? 'translate-x-0' : '-translate-x-full'"
|
|
4
|
-
/>
|
|
5
|
-
<div
|
|
6
|
-
v-if="agentStore.isSessionHistoryOpen"
|
|
7
|
-
@click="agentStore.setSessionHistoryOpen(false)"
|
|
8
|
-
class="absolute bg-black/10 backdrop-blur-md z-10 h-full w-full"
|
|
9
|
-
>
|
|
10
|
-
</div>
|
|
11
|
-
<div ref="chatContainerRef" class="relative flex-1 min-h-0 overflow-hidden" @click="recalculateScroll()">
|
|
12
|
-
<CustomAutoScrollContainer
|
|
13
|
-
v-if="showScrollContainer"
|
|
14
|
-
:enabled="!showScrollToBottomButton"
|
|
15
|
-
class="relative h-full flex flex-col overflow-y-auto translate-x-[-50%] left-1/2"
|
|
16
|
-
ref="scrollContainer"
|
|
17
|
-
:threshold="THRESHOLD_TO_SHOW_BUTTON"
|
|
18
|
-
behavior="smooth"
|
|
19
|
-
:wrapperStyle = "{
|
|
20
|
-
height: '100%',
|
|
21
|
-
maxHeight: '100%',
|
|
22
|
-
maxWidth: agentStore.isFullScreen ? agentStore.MAX_WIDTH+'rem' : '100%',
|
|
23
|
-
width: '100%',
|
|
24
|
-
marginLeft: 'auto',
|
|
25
|
-
marginRight: 'auto',
|
|
26
|
-
}"
|
|
27
|
-
:style="{
|
|
28
|
-
maxWidth: agentStore.isFullScreen ? agentStore.MAX_WIDTH+'rem' : '100%',
|
|
29
|
-
transition: `
|
|
30
|
-
max-width ${agentTransitions.TRANSITION_DURATION}ms ease-in-out,
|
|
31
|
-
transform ${agentTransitions.TRANSITION_DURATION}ms ease-in-out
|
|
32
|
-
`
|
|
33
|
-
}"
|
|
34
|
-
>
|
|
35
|
-
|
|
36
|
-
<div
|
|
37
|
-
v-for="(message, index) in props.messages" :key="index"
|
|
38
|
-
class="flex flex-col w-full mt-2 pb-2"
|
|
39
|
-
:class="message.role === 'user' ? 'self-end' : 'self-start'"
|
|
40
|
-
ref="messagesRefs"
|
|
41
|
-
>
|
|
42
|
-
<MessageRenderer :message="message" :isLastMessageInChat="index === props.messages.length - 1"/>
|
|
43
|
-
</div>
|
|
44
|
-
<div
|
|
45
|
-
v-if="props.messages.length === 0"
|
|
46
|
-
class="flex-1 flex flex-col items-center justify-center text-gray-400 tracking-widest text-xl font-medium h-max"
|
|
47
|
-
:style="{
|
|
48
|
-
height: chatContainerRef ? chatContainerRef.clientHeight + 'px' : '100%',
|
|
49
|
-
}"
|
|
50
|
-
>
|
|
51
|
-
<p>{{ $t('Start the conversation') }}</p>
|
|
52
|
-
<p class="tracking-normal text-base text">{{ $t('Give any input to begin') }}</p>
|
|
53
|
-
</div>
|
|
54
|
-
<div v-if="showBottomSpacer" class="w-full" :style="{ height: spacerHeight + 'px' }"></div>
|
|
55
|
-
</CustomAutoScrollContainer>
|
|
56
|
-
<button @click="scrollContainer.scrollToBottom();">
|
|
57
|
-
<IconArrowDownOutline
|
|
58
|
-
class="absolute z-10 bottom-8 left-1/2 -translate-x-1/2 bg-lightPrimary/20 dark:bg-darkPrimary/10
|
|
59
|
-
border border-lightPrimary dark:border-darkPrimary text-lightPrimary dark:text-darkPrimary p-2 w-10 h-10
|
|
60
|
-
rounded-full transition-opacity duration-100 ease-in backdrop-blur-sm"
|
|
61
|
-
:class="showScrollToBottomButton ? 'opacity-100' : 'opacity-0 pointer-events-none'"
|
|
62
|
-
:disabled="!showScrollToBottomButton"
|
|
63
|
-
/>
|
|
64
|
-
</button>
|
|
65
|
-
</div>
|
|
66
|
-
</template>
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
<script setup lang="ts">
|
|
70
|
-
import type { IMessage } from '../types';
|
|
71
|
-
import { useTemplateRef, ref, onMounted, onUnmounted, watch, nextTick, defineAsyncComponent } from 'vue';
|
|
72
|
-
import { IconArrowDownOutline } from '@iconify-prerendered/vue-flowbite';
|
|
73
|
-
import SessionsHistory from '../SessionsHistory.vue';
|
|
74
|
-
import { useAgentStore } from '../composables/useAgentStore';
|
|
75
|
-
import { useAgentTransitions } from '../composables/useAgentTransitions';
|
|
76
|
-
import MessageRenderer from './MessageRenderer.vue';
|
|
77
|
-
|
|
78
|
-
const CustomAutoScrollContainer = defineAsyncComponent(() => import('../CustomAutoScrollContainer.vue'));
|
|
79
|
-
|
|
80
|
-
const props = defineProps<{
|
|
81
|
-
messages: IMessage[]
|
|
82
|
-
}>();
|
|
83
|
-
|
|
84
|
-
defineExpose({
|
|
85
|
-
handleSendMessage
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
const scrollContainer = useTemplateRef('scrollContainer');
|
|
89
|
-
const showScrollToBottomButton = ref(false);
|
|
90
|
-
const innerScrollContainerRef = ref<HTMLElement | null>(null);
|
|
91
|
-
const agentStore = useAgentStore();
|
|
92
|
-
const agentTransitions = useAgentTransitions();
|
|
93
|
-
const showScrollContainer = ref(true);
|
|
94
|
-
const chatContainerRef = ref<HTMLElement | null>(null);
|
|
95
|
-
const messagesRefs = ref<Array<HTMLElement | null>>([]);
|
|
96
|
-
/*
|
|
97
|
-
* Whenever user sends a message, it adds a bottom spacer, that takes the remaining height
|
|
98
|
-
* without last user and last agent message.
|
|
99
|
-
*
|
|
100
|
-
* On send message, happens the following logic:
|
|
101
|
-
* 1) showBottomSpacer is set to true
|
|
102
|
-
* 2) updateSpacerHeight is called, which calculates the height for spacer based on scroll container height and messages height
|
|
103
|
-
* 3) Spacer moves text up
|
|
104
|
-
*/
|
|
105
|
-
const showBottomSpacer = ref(false);
|
|
106
|
-
const spacerHeight = ref(0);
|
|
107
|
-
const MASK_HEIGHT = 20;
|
|
108
|
-
const EMPTY_MESSAGE_HEIGHT = 18;
|
|
109
|
-
const THRESHOLD_TO_SHOW_BUTTON = 10;
|
|
110
|
-
let messageResizeObserver: ResizeObserver | null = null;
|
|
111
|
-
let observedLastUserMessageElement: HTMLElement | null = null;
|
|
112
|
-
let observedLastAgentMessageElement: HTMLElement | null = null;
|
|
113
|
-
let updateSpacerFrameId: number | null = null;
|
|
114
|
-
let pendingSpacerUpdate: Promise<void> | null = null;
|
|
115
|
-
let spacerUpdateQueued = false;
|
|
116
|
-
|
|
117
|
-
onMounted(async () => {
|
|
118
|
-
messageResizeObserver = new ResizeObserver(() => {
|
|
119
|
-
scheduleSpacerHeightUpdate();
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
await import('@incremark/theme/styles.css')
|
|
123
|
-
await agentStore.fetchPlaceholderMessages()
|
|
124
|
-
await refreshSpacerTracking();
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
onUnmounted(() => {
|
|
128
|
-
if (innerScrollContainerRef.value) {
|
|
129
|
-
innerScrollContainerRef.value.removeEventListener('scroll', recalculateScroll);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
stopObservingLastMessages();
|
|
133
|
-
messageResizeObserver?.disconnect();
|
|
134
|
-
if (updateSpacerFrameId !== null) {
|
|
135
|
-
cancelAnimationFrame(updateSpacerFrameId);
|
|
136
|
-
updateSpacerFrameId = null;
|
|
137
|
-
}
|
|
138
|
-
agentStore.stopPlaceholderAnimation();
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
watch(() => agentStore.activeSessionId, () => {
|
|
142
|
-
resetSpacer();
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
watch(() => agentStore.activeSessionId, async () => {
|
|
146
|
-
showScrollContainer.value = false;
|
|
147
|
-
await nextTick();
|
|
148
|
-
showScrollContainer.value = true;
|
|
149
|
-
await refreshSpacerTracking();
|
|
150
|
-
recalculateScroll();
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
watch(() => props.messages.length, async () => {
|
|
154
|
-
await refreshSpacerTracking();
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
watch(scrollContainer, async () => {
|
|
158
|
-
if (innerScrollContainerRef.value) {
|
|
159
|
-
innerScrollContainerRef.value.removeEventListener('scroll', recalculateScroll);
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
if (scrollContainer.value) {
|
|
163
|
-
innerScrollContainerRef.value = scrollContainer.value.container.scrollEl;
|
|
164
|
-
|
|
165
|
-
innerScrollContainerRef.value.addEventListener('scroll', recalculateScroll);
|
|
166
|
-
await refreshSpacerTracking();
|
|
167
|
-
}
|
|
168
|
-
})
|
|
169
|
-
|
|
170
|
-
function resetSpacer() {
|
|
171
|
-
showBottomSpacer.value = false;
|
|
172
|
-
spacerHeight.value = 0;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
function getLastMessageElement(role: 'user' | 'assistant') {
|
|
176
|
-
const lastMessageIndex = props.messages.findLastIndex((message: IMessage) => message.role === role);
|
|
177
|
-
return messagesRefs.value[lastMessageIndex] ?? null;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
function getHeightOfLastUserMessage() {
|
|
181
|
-
return getLastMessageElement('user')?.clientHeight ?? 0;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
function getHeightOfLastAgentMessage() {
|
|
185
|
-
return getLastMessageElement('assistant')?.clientHeight ?? 0;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
function getScrollClientHeight() {
|
|
189
|
-
return scrollContainer.value?.container.scrollEl.clientHeight ?? scrollContainer.value?.scrollParams.clientHeight ?? 0;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
async function updateSpacerHeight() {
|
|
193
|
-
if (!showBottomSpacer.value) {
|
|
194
|
-
return;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
const clientHeight = getScrollClientHeight();
|
|
198
|
-
|
|
199
|
-
if (!clientHeight) {
|
|
200
|
-
return;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
const lastUserMessageHeight = getHeightOfLastUserMessage();
|
|
204
|
-
const lastAgentMessageHeight = getHeightOfLastAgentMessage();
|
|
205
|
-
|
|
206
|
-
spacerHeight.value = Math.max(0, clientHeight - (lastUserMessageHeight + MASK_HEIGHT + lastAgentMessageHeight));
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
function scheduleSpacerHeightUpdate() {
|
|
210
|
-
spacerUpdateQueued = true;
|
|
211
|
-
|
|
212
|
-
if (updateSpacerFrameId !== null) {
|
|
213
|
-
return;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
updateSpacerFrameId = requestAnimationFrame(() => {
|
|
217
|
-
updateSpacerFrameId = null;
|
|
218
|
-
|
|
219
|
-
if (pendingSpacerUpdate) {
|
|
220
|
-
return;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
spacerUpdateQueued = false;
|
|
224
|
-
pendingSpacerUpdate = updateSpacerHeight().finally(() => {
|
|
225
|
-
pendingSpacerUpdate = null;
|
|
226
|
-
|
|
227
|
-
// Auto-scroll to bottom if response generation is in progress
|
|
228
|
-
if (agentStore.isResponseInProgress) {
|
|
229
|
-
nextTick(() => {
|
|
230
|
-
scrollContainer.value?.scrollToBottom();
|
|
231
|
-
});
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
if (spacerUpdateQueued) {
|
|
235
|
-
scheduleSpacerHeightUpdate();
|
|
236
|
-
}
|
|
237
|
-
});
|
|
238
|
-
});
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
function stopObservingLastMessages() {
|
|
242
|
-
if (!messageResizeObserver) {
|
|
243
|
-
return;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
if (observedLastUserMessageElement) {
|
|
247
|
-
messageResizeObserver.unobserve(observedLastUserMessageElement);
|
|
248
|
-
observedLastUserMessageElement = null;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
if (observedLastAgentMessageElement) {
|
|
252
|
-
messageResizeObserver.unobserve(observedLastAgentMessageElement);
|
|
253
|
-
observedLastAgentMessageElement = null;
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
function observeLastMessages() {
|
|
258
|
-
if (!messageResizeObserver) {
|
|
259
|
-
return;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
stopObservingLastMessages();
|
|
263
|
-
|
|
264
|
-
observedLastUserMessageElement = getLastMessageElement('user');
|
|
265
|
-
observedLastAgentMessageElement = getLastMessageElement('assistant');
|
|
266
|
-
|
|
267
|
-
if (observedLastUserMessageElement) {
|
|
268
|
-
messageResizeObserver.observe(observedLastUserMessageElement);
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
if (observedLastAgentMessageElement) {
|
|
272
|
-
messageResizeObserver.observe(observedLastAgentMessageElement);
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
async function refreshSpacerTracking() {
|
|
277
|
-
await nextTick();
|
|
278
|
-
observeLastMessages();
|
|
279
|
-
await updateSpacerHeight();
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
async function handleSendMessage() {
|
|
283
|
-
const clientHeight = getScrollClientHeight();
|
|
284
|
-
|
|
285
|
-
if (clientHeight) {
|
|
286
|
-
showBottomSpacer.value = true;
|
|
287
|
-
await updateSpacerHeight();
|
|
288
|
-
await nextTick();
|
|
289
|
-
scrollContainer.value?.scrollToBottom();
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
function recalculateScroll() {
|
|
294
|
-
if (scrollContainer.value) {
|
|
295
|
-
scrollContainer.value.handleScroll(false,spacerHeight.value + THRESHOLD_TO_SHOW_BUTTON);
|
|
296
|
-
const isScrolledUp = scrollContainer.value.isUserScrolledUp();
|
|
297
|
-
showScrollToBottomButton.value = !!isScrolledUp;
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
</script>
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<ProcessingTimeline
|
|
3
|
-
:message="message"
|
|
4
|
-
:isLastMessageInChat="isLastMessageInChat"
|
|
5
|
-
/>
|
|
6
|
-
<template
|
|
7
|
-
v-for="(part, index) in getMessageParts(message)"
|
|
8
|
-
:key="part.type"
|
|
9
|
-
>
|
|
10
|
-
<TextRenderer
|
|
11
|
-
v-if="part.type === 'text' && !checkIfMessageSystemMessage(part.text ?? '')"
|
|
12
|
-
:message="part.text"
|
|
13
|
-
:role="props.message.role"
|
|
14
|
-
:state="part.state ?? (props.message.role === 'user' ? 'done' : undefined)"
|
|
15
|
-
/>
|
|
16
|
-
<ToolApprovalRenderer
|
|
17
|
-
v-else-if="part.type === 'data-tool-approval'"
|
|
18
|
-
:data="part.data ?? {}"
|
|
19
|
-
/>
|
|
20
|
-
<SystemMessageRenderer
|
|
21
|
-
v-else-if="part.type === 'text'"
|
|
22
|
-
:message="part.text"
|
|
23
|
-
/>
|
|
24
|
-
</template>
|
|
25
|
-
|
|
26
|
-
</template>
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
<script setup lang="ts">
|
|
32
|
-
import TextRenderer from './TextRenderer.vue';
|
|
33
|
-
import type { IMessage } from '../types';
|
|
34
|
-
import { getMessageParts } from '../utils';
|
|
35
|
-
import ProcessingTimeline from './ProcessingTimeline.vue';
|
|
36
|
-
import SystemMessageRenderer from './SystemMessageRenderer.vue';
|
|
37
|
-
import ToolApprovalRenderer from './ToolApprovalRenderer.vue';
|
|
38
|
-
import { RESERVED_SYSTEM_MESSAGE_CONTENT } from '../composables/agentStore/constants';
|
|
39
|
-
|
|
40
|
-
const props = defineProps<{
|
|
41
|
-
message: IMessage
|
|
42
|
-
isLastMessageInChat: boolean
|
|
43
|
-
}>();
|
|
44
|
-
|
|
45
|
-
function checkIfMessageSystemMessage(message: IMessage): boolean {
|
|
46
|
-
const isReserved = Object.values(RESERVED_SYSTEM_MESSAGE_CONTENT).includes(message as RESERVED_SYSTEM_MESSAGE_CONTENT);
|
|
47
|
-
return isReserved;
|
|
48
|
-
}
|
|
49
|
-
</script>
|
|
@@ -1,286 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<template v-if="ToolOrReasoningParts.length > 0 || inProgress">
|
|
3
|
-
<div
|
|
4
|
-
class="shine-text-container ml-2 px-4 flex items-center gap-1 select-none hover:opacity-80 tracking-wide font-medium text-sm text-listTableHeadingText dark:text-darkListTableHeadingText"
|
|
5
|
-
:class="[ToolOrReasoningParts.length > 0 ? 'cursor-pointer' : '']"
|
|
6
|
-
@click="isExpanded = !isExpanded"
|
|
7
|
-
>
|
|
8
|
-
<p
|
|
9
|
-
:class="[
|
|
10
|
-
inProgress ? `
|
|
11
|
-
shine-text text-listTableHeadingText dark:text-darkListTableHeadingText
|
|
12
|
-
bg-[linear-gradient(-65deg,currentColor,_rgb(120,120,120)_50%,currentColor)]
|
|
13
|
-
dark:bg-[linear-gradient(-65deg,currentColor,_rgb(250,250,250,0.8)_70%,currentColor)]
|
|
14
|
-
`
|
|
15
|
-
: '']"
|
|
16
|
-
>
|
|
17
|
-
{{ inProgress ? $t('Thinking') : $t('Thoughts') }}
|
|
18
|
-
</p>
|
|
19
|
-
|
|
20
|
-
<span v-if="thinkingDuration > 0">({{ (thinkingDuration/1000).toFixed(2) }} s)</span>
|
|
21
|
-
<IconAngleDownOutline
|
|
22
|
-
v-if="ToolOrReasoningParts.length > 0"
|
|
23
|
-
:class="isExpanded ? 'rotate-180' : 'rotate-0'"
|
|
24
|
-
class="transition-transform duration-200"
|
|
25
|
-
/>
|
|
26
|
-
</div>
|
|
27
|
-
<transition name="expand" class="sm:max-h-96 max-h-48 overflow-y-auto mb-4 pt-1">
|
|
28
|
-
<CustomAutoScrollContainer
|
|
29
|
-
ref="scrollContainerRef"
|
|
30
|
-
:enabled="isResponseInProgress"
|
|
31
|
-
behavior="smooth"
|
|
32
|
-
v-if="ToolOrReasoningParts.length > 0"
|
|
33
|
-
v-show="isExpanded"
|
|
34
|
-
:threshold="5"
|
|
35
|
-
:wrapperStyle="{
|
|
36
|
-
marginRight: '1rem',
|
|
37
|
-
}"
|
|
38
|
-
:scrollBarAutoHide="false"
|
|
39
|
-
>
|
|
40
|
-
<ol class="ml-8 my-2 relative border-l border-l-2 border-black border-default border-listTableHeadingText dark:border-darkListTableHeadingText">
|
|
41
|
-
<template v-for="(part, index) in ToolOrReasoningParts" :key="index">
|
|
42
|
-
<ReasoningRenderer v-if="part.type === 'reasoning'" :state="part.state" :text="part.text" />
|
|
43
|
-
<ToolsGroup v-else-if="part.type==='data-tool-call'" :toolGroup="groupToolCallParts(message, part)" :isExpanded="isExpanded" />
|
|
44
|
-
<li v-else-if="part.type === 'data-rendering'" class="mb-6 mx-2 mt-2 px-2 z-50 overflow-hidden">
|
|
45
|
-
<span class="bg-lightNavbar dark:bg-darkNavbar absolute flex items-center text-listTableHeadingText dark:text-darkListTableHeadingText justify-center w-5 h-5 rounded-full -start-[0.68rem] ring-4 ring-lightNavbar dark:ring-darkNavbar">
|
|
46
|
-
<div class="w-2 h-2 rounded-full bg-current animate-pulse"></div>
|
|
47
|
-
</span>
|
|
48
|
-
<h3 class="flex items-center mb-1 text-sm ml-3 gap-1 text-listTableHeadingText dark:text-darkListTableHeadingText">
|
|
49
|
-
<span class="font-semibold">{{ part.data?.label ?? 'Rendering...' }}</span>
|
|
50
|
-
<ThreeDotsAnimation />
|
|
51
|
-
</h3>
|
|
52
|
-
</li>
|
|
53
|
-
</template>
|
|
54
|
-
</ol>
|
|
55
|
-
</CustomAutoScrollContainer>
|
|
56
|
-
</transition>
|
|
57
|
-
</template>
|
|
58
|
-
</template>
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
<script setup lang="ts">
|
|
63
|
-
import type { IFormattedToolCallPart, IMessage, IPart, IToolGroup } from '../types';
|
|
64
|
-
import { ref, computed, watch, onUnmounted, onMounted, defineAsyncComponent } from 'vue';
|
|
65
|
-
import ReasoningRenderer from './ReasoningRenderer.vue';
|
|
66
|
-
import { IconAngleDownOutline } from '@iconify-prerendered/vue-flowbite';
|
|
67
|
-
import ThreeDotsAnimation from './ThreeDotsAnimation.vue';
|
|
68
|
-
import { useAgentStore } from '../composables/useAgentStore';
|
|
69
|
-
import { getMessageParts } from '../utils';
|
|
70
|
-
import ToolsGroup from './ToolsGroup.vue';
|
|
71
|
-
|
|
72
|
-
const CustomAutoScrollContainer = defineAsyncComponent(() => import('../CustomAutoScrollContainer.vue'));
|
|
73
|
-
|
|
74
|
-
const props = defineProps<{
|
|
75
|
-
message: IMessage
|
|
76
|
-
isLastMessageInChat: boolean
|
|
77
|
-
}>()
|
|
78
|
-
|
|
79
|
-
const agentStore = useAgentStore();
|
|
80
|
-
const thinkingStartTime = ref<number | null>(null);
|
|
81
|
-
const thinkingDuration = ref(0);
|
|
82
|
-
const scrollContainerRef = ref<InstanceType<typeof CustomAutoScrollContainer> | null>(null);
|
|
83
|
-
const innerScrollContainerRef = ref<HTMLElement | null>(null);
|
|
84
|
-
const isExpanded = ref(true);
|
|
85
|
-
let isUserScrolled = false;
|
|
86
|
-
const ToolOrReasoningParts = computed(() => {
|
|
87
|
-
return props.message.parts.filter((part: IPart) => {
|
|
88
|
-
return part.type === 'data-tool-call'
|
|
89
|
-
|| part.type === 'reasoning'
|
|
90
|
-
|| isActiveRenderingPart(part);
|
|
91
|
-
});
|
|
92
|
-
});
|
|
93
|
-
const isResponseInProgress = computed(() =>{
|
|
94
|
-
return props.isLastMessageInChat && agentStore.isResponseInProgress;
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
const showFakeThinkingMessage = computed(() => {
|
|
98
|
-
if (props.message.parts.length === 0 && props.isLastMessageInChat) return true;
|
|
99
|
-
return false;
|
|
100
|
-
})
|
|
101
|
-
|
|
102
|
-
const inProgress = computed(() => {
|
|
103
|
-
return isResponseInProgress.value || showFakeThinkingMessage.value;
|
|
104
|
-
})
|
|
105
|
-
|
|
106
|
-
onMounted(() => {
|
|
107
|
-
thinkingStartTime.value = Date.now();
|
|
108
|
-
if (isResponseInProgress.value) {
|
|
109
|
-
isExpanded.value = true;
|
|
110
|
-
} else {
|
|
111
|
-
isExpanded.value = false;
|
|
112
|
-
}
|
|
113
|
-
})
|
|
114
|
-
|
|
115
|
-
onUnmounted(() => {
|
|
116
|
-
scrollContainerRef.value?.container.scrollEl?.removeEventListener('scroll', handleScroll);
|
|
117
|
-
scrollContainerRef.value?.container.scrollEl?.removeEventListener('wheel', toggleIsUserScrolled);
|
|
118
|
-
scrollContainerRef.value?.container.scrollEl?.removeEventListener('touchmove', toggleIsUserScrolled);
|
|
119
|
-
})
|
|
120
|
-
|
|
121
|
-
watch(scrollContainerRef, async () => {
|
|
122
|
-
if (innerScrollContainerRef.value) {
|
|
123
|
-
innerScrollContainerRef.value.removeEventListener('scroll', handleScroll);
|
|
124
|
-
innerScrollContainerRef.value.removeEventListener('wheel', toggleIsUserScrolled);
|
|
125
|
-
innerScrollContainerRef.value.removeEventListener('touchmove', toggleIsUserScrolled);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
if (scrollContainerRef.value) {
|
|
129
|
-
innerScrollContainerRef.value = scrollContainerRef.value.container.scrollEl;
|
|
130
|
-
innerScrollContainerRef.value.addEventListener('scroll', handleScroll);
|
|
131
|
-
innerScrollContainerRef.value.addEventListener('wheel', toggleIsUserScrolled);
|
|
132
|
-
innerScrollContainerRef.value.addEventListener('touchmove', toggleIsUserScrolled);
|
|
133
|
-
|
|
134
|
-
}
|
|
135
|
-
})
|
|
136
|
-
|
|
137
|
-
watch(isResponseInProgress, (newValue: boolean) => {
|
|
138
|
-
if (!newValue) {
|
|
139
|
-
isExpanded.value = false;
|
|
140
|
-
thinkingDuration.value = Date.now() - (thinkingStartTime.value ?? Date.now());
|
|
141
|
-
}
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
const formatToolCallPart = (part: IPart, currentMessage: IMessage): IFormattedToolCallPart | null => {
|
|
145
|
-
if (part.type !== 'data-tool-call' || part.data?.phase !== 'start') {
|
|
146
|
-
return null;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
const finishedPart = currentMessage.parts.find(candidate => {
|
|
150
|
-
return candidate.type === 'data-tool-call'
|
|
151
|
-
&& candidate.data?.toolCallId === part.data?.toolCallId
|
|
152
|
-
&& candidate.data?.phase === 'end';
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
return {
|
|
156
|
-
type: 'data-tool-call',
|
|
157
|
-
toolInfo: {
|
|
158
|
-
toolCallId: part.data.toolCallId,
|
|
159
|
-
toolName: part.data.toolName,
|
|
160
|
-
phase: finishedPart ? 'end' : 'start',
|
|
161
|
-
durationMs: finishedPart?.data?.durationMs,
|
|
162
|
-
input: part.data.input,
|
|
163
|
-
output: finishedPart?.data?.output,
|
|
164
|
-
toolInfo: part.data.toolInfo,
|
|
165
|
-
error: finishedPart?.data?.error,
|
|
166
|
-
}
|
|
167
|
-
};
|
|
168
|
-
};
|
|
169
|
-
|
|
170
|
-
const getVisibleTimelineParts = (message: IMessage) => {
|
|
171
|
-
return getMessageParts(message).filter(part => {
|
|
172
|
-
return part.type === 'reasoning' || (part.type === 'data-tool-call' && part.data?.phase === 'start');
|
|
173
|
-
});
|
|
174
|
-
};
|
|
175
|
-
|
|
176
|
-
function isActiveRenderingPart(part: IPart) {
|
|
177
|
-
return part.type === 'data-rendering'
|
|
178
|
-
&& part.data?.phase === 'start'
|
|
179
|
-
&& !props.message.parts.some((candidate: IPart) => {
|
|
180
|
-
return candidate.type === 'data-rendering' && candidate.data?.phase === 'end';
|
|
181
|
-
});
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
const groupToolCallParts = (message: IMessage, currentPart: IPart): IToolGroup[] => {
|
|
185
|
-
if (currentPart.type !== 'data-tool-call') {
|
|
186
|
-
return [];
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
const visibleParts = getVisibleTimelineParts(message);
|
|
190
|
-
const currentPartIndex = visibleParts.findIndex(part => part === currentPart);
|
|
191
|
-
|
|
192
|
-
if (currentPartIndex === -1) {
|
|
193
|
-
return [];
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
if (currentPartIndex > 0 && visibleParts[currentPartIndex - 1]?.type === 'data-tool-call') {
|
|
197
|
-
return [];
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
const groupedParts: IToolGroup[] = [];
|
|
201
|
-
|
|
202
|
-
for (let index = currentPartIndex; index < visibleParts.length; index += 1) {
|
|
203
|
-
const part = visibleParts[index];
|
|
204
|
-
|
|
205
|
-
if (part.type === 'reasoning') {
|
|
206
|
-
break;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
const formattedPart = formatToolCallPart(part, message);
|
|
210
|
-
|
|
211
|
-
if (!formattedPart) {
|
|
212
|
-
continue;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
const lastGroup = groupedParts[groupedParts.length - 1];
|
|
216
|
-
|
|
217
|
-
if (lastGroup?.title === formattedPart.toolInfo.toolName) {
|
|
218
|
-
lastGroup.groupedTools.push(formattedPart);
|
|
219
|
-
continue;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
groupedParts.push({
|
|
223
|
-
title: formattedPart.toolInfo.toolName,
|
|
224
|
-
groupedTools: [formattedPart],
|
|
225
|
-
});
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
return groupedParts;
|
|
229
|
-
};
|
|
230
|
-
|
|
231
|
-
function handleScroll() {
|
|
232
|
-
if (isUserScrolled) {
|
|
233
|
-
scrollContainerRef.value?.handleScroll();
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
function toggleIsUserScrolled() {
|
|
238
|
-
isUserScrolled = true;
|
|
239
|
-
setTimeout(() => {
|
|
240
|
-
isUserScrolled = false;
|
|
241
|
-
}, 100);
|
|
242
|
-
}
|
|
243
|
-
</script>
|
|
244
|
-
|
|
245
|
-
<style scoped>
|
|
246
|
-
.expand-enter-active,
|
|
247
|
-
.expand-leave-active {
|
|
248
|
-
transition: all 0.3s ease;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
.expand-enter-from,
|
|
252
|
-
.expand-leave-to {
|
|
253
|
-
opacity: 0;
|
|
254
|
-
max-height: 0;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
.expand-enter-to,
|
|
258
|
-
.expand-leave-from {
|
|
259
|
-
opacity: 1;
|
|
260
|
-
max-height: 384px;
|
|
261
|
-
|
|
262
|
-
@media (max-width: 640px) {
|
|
263
|
-
max-height: 192px;
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
.shine-text {
|
|
269
|
-
|
|
270
|
-
background-clip: text;
|
|
271
|
-
-webkit-background-clip: text;
|
|
272
|
-
-webkit-text-fill-color: transparent;
|
|
273
|
-
background-position: -12rem;
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
.shine-text {
|
|
277
|
-
animation: shineText 6s infinite linear;
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
@keyframes shineText {
|
|
281
|
-
to {
|
|
282
|
-
background-position: -100%;
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
</style>
|