11<script setup lang="ts">
22import { IconArrowDown , IconAtom } from ' @opentiny/tiny-robot-svgs'
3- import { nextTick , ref , watch , watchEffect } from ' vue'
3+ import { nextTick , onBeforeUnmount , ref , watch } from ' vue'
44import { useBubbleContentRenderer , useBubbleEventFn , useOmitMessageFields } from ' ../composables'
55import { BubbleContentRendererProps , ChatMessageContent } from ' ../index.type'
66
@@ -23,16 +23,58 @@ const { restMessage, restProps } = useOmitMessageFields(props, ['reasoning_conte
2323const renderer = useBubbleContentRenderer (restMessage , props .contentIndex )
2424
2525const open = ref (true )
26+ const displayedReasoningContent = ref (props .message .reasoning_content ?? ' ' )
27+ const detailRef = ref <HTMLParagraphElement | null >(null )
2628
27- watchEffect (() => {
28- // 思考过程默认展开
29- open .value = props .message .state ?.open ?? true
30- })
29+ let renderFrame: number | null = null
30+
31+ const scrollDetailToBottom = () => {
32+ if (! open .value || ! detailRef .value ) {
33+ return
34+ }
35+
36+ detailRef .value .scrollTop = detailRef .value .scrollHeight
37+ }
38+
39+ const scheduleDetailUpdate = () => {
40+ if (typeof window === ' undefined' || typeof window .requestAnimationFrame !== ' function' ) {
41+ displayedReasoningContent .value = props .message .reasoning_content ?? ' '
42+ return
43+ }
44+
45+ if (renderFrame !== null ) {
46+ return
47+ }
48+
49+ renderFrame = window .requestAnimationFrame (() => {
50+ renderFrame = null
51+ displayedReasoningContent .value = props .message .reasoning_content ?? ' '
52+ nextTick (scrollDetailToBottom )
53+ })
54+ }
55+
56+ watch (
57+ () => props .message .state ?.open ,
58+ (value ) => {
59+ // 思考过程默认展开
60+ open .value = value ?? true
61+ if (open .value ) {
62+ scheduleDetailUpdate ()
63+ }
64+ },
65+ { immediate: true },
66+ )
67+
68+ watch (() => props .message .reasoning_content , scheduleDetailUpdate , { immediate: true })
3169
3270const handleBubbleEvent = useBubbleEventFn ()
3371
3472const handleClick = () => {
3573 open .value = ! open .value
74+ if (open .value ) {
75+ scheduleDetailUpdate ()
76+ }
77+
3678 handleBubbleEvent ({
3779 name: ' state:update' ,
3880 payload: {
@@ -42,23 +84,12 @@ const handleClick = () => {
4284 })
4385}
4486
45- const detailRef = ref <HTMLParagraphElement | null >(null )
46-
47- watch (
48- () => props .message .reasoning_content ,
49- () => {
50- nextTick (() => {
51- if (! detailRef .value ) {
52- return
53- }
54-
55- detailRef .value .scrollTo ({
56- top: detailRef .value .scrollHeight ,
57- behavior: ' smooth' ,
58- })
59- })
60- },
61- )
87+ onBeforeUnmount (() => {
88+ if (renderFrame !== null && typeof window !== ' undefined' ) {
89+ window .cancelAnimationFrame (renderFrame )
90+ renderFrame = null
91+ }
92+ })
6293 </script >
6394
6495<template >
@@ -77,7 +108,7 @@ watch(
77108 </div >
78109 <div class =" border-line" ></div >
79110 </div >
80- <p class =" detail-content" ref =" detailRef" >{{ props.message.reasoning_content }}</p >
111+ <p class =" detail-content" ref =" detailRef" >{{ displayedReasoningContent }}</p >
81112 </div >
82113 </div >
83114 <component :is =" renderer.renderer" v-bind =" { ...renderer.attributes, ...restProps }" />
0 commit comments