3636 <icon-new-session />
3737 </button >
3838 </template >
39- <div v-if =" activeMessages.length === 0" >
40- <tr-welcome title =" AI助手" description =" 您好,我是您的开发小助手" :icon =" welcomeIcon" class =" robot-welcome" >
41- </tr-welcome >
42- <tr-prompts
43- :items =" promptItems"
44- :wrap =" true"
45- item-class =" prompt-item"
46- class =" tiny-prompts"
47- @item-click =" handlePromptItemClick"
48- ></tr-prompts >
39+ <div class =" robot-chat-container-content" ref =" chatContainerRef" >
40+ <div v-if =" activeMessages.length === 0" >
41+ <tr-welcome
42+ title =" AI助手"
43+ description =" 您好,我是您的开发小助手"
44+ :icon =" welcomeIcon"
45+ class =" robot-welcome"
46+ >
47+ </tr-welcome >
48+ <tr-prompts
49+ :items =" promptItems"
50+ :wrap =" true"
51+ item-class =" prompt-item"
52+ class =" tiny-prompts"
53+ @item-click =" handlePromptItemClick"
54+ ></tr-prompts >
55+ </div >
56+ <tr-bubble-provider :content-renderers =" contentRenderers" v-else >
57+ <tr-bubble-list :items =" activeMessages" :roles =" roles" autoScroll ></tr-bubble-list >
58+ </tr-bubble-provider >
4959 </div >
50- <tr-bubble-provider :message-renderers =" { markdown: MarkdownRenderer }" v-else >
51- <tr-bubble-list :items =" activeMessages" :roles =" roles" autoScroll ></tr-bubble-list >
52- </tr-bubble-provider >
5360 <template #footer >
5461 <tr-sender
5562 :maxlength =" 4000"
6168 @submit =" sendContent(inputContent, false)"
6269 >
6370 <template #footer-left >
64- <mcp-server ></mcp-server >
71+ <mcp-server :position = " mcpDrawerPosition " ></mcp-server >
6572 </template >
6673 </tr-sender >
6774 </template >
7380
7481<script lang="ts">
7582/* metaService: engine.plugins.robot.Main */
76- import { ref , onMounted , watchEffect , type CSSProperties , h , resolveComponent } from ' vue'
83+ import {
84+ ref ,
85+ onMounted ,
86+ watchEffect ,
87+ type CSSProperties ,
88+ h ,
89+ resolveComponent ,
90+ computed ,
91+ watch ,
92+ nextTick ,
93+ type Component
94+ } from ' vue'
7795import { Notify , Loading , TinyPopover } from ' @opentiny/vue'
7896import { useCanvas , useHistory , usePage , useModal , getMetaApi , META_SERVICE } from ' @opentiny/tiny-engine-meta-register'
7997import { extend } from ' @opentiny/vue-renderless/common/object'
@@ -85,6 +103,7 @@ import { getBlockContent, initBlockList, getAIModelOptions } from './js/robotSet
85103import McpServer from ' ./mcp/McpServer.vue'
86104import useMcpServer from ' ./mcp/useMcp'
87105import MarkdownRenderer from ' ./mcp/MarkdownRenderer.vue'
106+ import LoadingRenderer from ' ./mcp/LoadingRenderer.vue'
88107import { sendMcpRequest } from ' ./mcp/utils'
89108
90109export default {
@@ -118,6 +137,8 @@ export default {
118137 const tokenValue = ref (' ' )
119138 const showPopover = ref (false )
120139
140+ const chatContainerRef = ref (null )
141+
121142 const { pageSettingState, getDefaultPage } = usePage ()
122143 const ROOT_ID = pageSettingState .ROOT_ID
123144 const sleep = (delay ) => new Promise ((resolve ) => setTimeout (resolve , delay ))
@@ -142,6 +163,14 @@ export default {
142163 )
143164 }
144165
166+ const scrollContent = async () => {
167+ await nextTick ()
168+ const el = chatContainerRef .value as HTMLElement | null
169+ if (el ) {
170+ el .scrollTop = el .scrollHeight
171+ }
172+ }
173+
145174 const createNewPage = (schema ) => {
146175 if (! (pageSettingState .isNew && pageSettingState .isAIPage )) {
147176 pageSettingState .isNew = true
@@ -188,8 +217,7 @@ export default {
188217
189218 const getAiRespMessage = (role = ' assistant' , content ) => ({
190219 role ,
191- content ,
192- name: ' AI'
220+ content
193221 })
194222
195223 const requestLoading = ref (false )
@@ -198,6 +226,7 @@ export default {
198226 if (useMcpServer ().isToolsEnabled ) {
199227 try {
200228 requestLoading .value = true
229+ await scrollContent ()
201230 await sendMcpRequest (messages .value , {
202231 model: selectedModel .value .value ,
203232 headers: {
@@ -209,6 +238,7 @@ export default {
209238 } finally {
210239 inProcesing .value = false
211240 requestLoading .value = false
241+ await scrollContent ()
212242 }
213243 return
214244 }
@@ -236,14 +266,6 @@ export default {
236266 })
237267 }
238268
239- const scrollContent = async () => {
240- await sleep (100 )
241- const scrollElement = document .getElementById (' chatgpt-window' )
242- if (scrollElement ) {
243- scrollElement .scrollTop = scrollElement .scrollHeight
244- }
245- }
246-
247269 const resetContent = async () => {
248270 activeMessages .value = messages .value
249271 await scrollContent ()
@@ -257,8 +279,7 @@ export default {
257279
258280 const getMessage = (content ) => ({
259281 role: ' user' ,
260- content ,
261- name: ' John'
282+ content
262283 })
263284
264285 const sendContent = async (content , isModel ) => {
@@ -412,11 +433,38 @@ export default {
412433
413434 // 对话角色配置
414435 const roles: Record <string , BubbleRoleConfig > = {
415- assistant: { placement: ' start' , avatar: aiAvatar , maxWidth: ' 90%' , contentRenderer: MarkdownRenderer },
436+ assistant: {
437+ placement: ' start' ,
438+ avatar: aiAvatar ,
439+ maxWidth: ' 90%' ,
440+ contentRenderer: MarkdownRenderer ,
441+ customContentField: ' renderContent'
442+ },
416443 user: { placement: ' end' , avatar: userAvatar , maxWidth: ' 90%' , contentRenderer: MarkdownRenderer }
417444 }
418445
446+ watch ([() => activeMessages .value .length , () => activeMessages .value .at (- 1 )?.renderContent ?.length ?? 0 ], () => {
447+ scrollContent ()
448+ })
449+
450+ const contentRenderers: Record <string , Component > = {
451+ markdown: MarkdownRenderer ,
452+ loading: LoadingRenderer
453+ }
454+
455+ const mcpDrawerPosition = computed (() => {
456+ return {
457+ type: ' fixed' ,
458+ position: {
459+ top: ' var(--base-top-panel-height)' ,
460+ bottom: 0 ,
461+ ... (fullscreen .value ? { left: 0 } : { right: ' var(--tr-container-width)' })
462+ }
463+ }
464+ })
465+
419466 return {
467+ chatContainerRef ,
420468 robotVisible ,
421469 avatarUrl ,
422470 chatWindowOpened ,
@@ -439,8 +487,9 @@ export default {
439487 handlePromptItemClick ,
440488 welcomeIcon ,
441489 roles ,
442- MarkdownRenderer ,
443- requestLoading
490+ contentRenderers ,
491+ requestLoading ,
492+ mcpDrawerPosition
444493 }
445494 }
446495}
@@ -486,10 +535,38 @@ export default {
486535 }
487536
488537 .tr-bubble-list {
538+ font-size : 14px ;
489539 flex : 1 ;
490540 .tr-bubble {
491541 word-break : break-word ;
492542 }
543+ ul ,
544+ ol {
545+ padding-left : 10px ;
546+ }
547+ ul > li {
548+ list-style : disc ;
549+ }
550+ ol > li {
551+ list-style : decimal ;
552+ }
553+ table {
554+ border-collapse : collapse ; // 合并边框
555+ border : 1px solid #ccc ;
556+ width : 100% ;
557+ margin : 1rem 0 ;
558+ th ,
559+ td {
560+ border : 1px solid #ccc ; /* 单元格边框 */
561+ padding : 8px ;
562+ }
563+ tr :nth- child(even) {
564+ background-color : #f2f2f2 ;
565+ }
566+ tr :hover {
567+ background-color : #e6f7ff ;
568+ }
569+ }
493570 }
494571
495572 .robot-welcome > div {
0 commit comments