@@ -19,8 +19,7 @@ import I18n from "discourse-i18n";
19
19
import DMenu from " float-kit/components/d-menu" ;
20
20
import DTooltip from " float-kit/components/d-tooltip" ;
21
21
import AiSummarySkeleton from " ../../components/ai-summary-skeleton" ;
22
-
23
- const STREAMED_TEXT_SPEED = 15 ;
22
+ import smoothStreamText from " ../../modifiers/smooth-stream-text" ;
24
23
25
24
export default class AiSummaryBox extends Component {
26
25
@service siteSettings;
@@ -36,10 +35,7 @@ export default class AiSummaryBox extends Component {
36
35
@tracked canRegenerate = false ;
37
36
@tracked loading = false ;
38
37
@tracked isStreaming = false ;
39
- @tracked streamedText = " " ;
40
- @tracked currentIndex = 0 ;
41
- typingTimer = null ;
42
- streamedTextLength = 0 ;
38
+ @tracked haltAnimation = false ;
43
39
44
40
get outdatedSummaryWarningText () {
45
41
let outdatedText = I18n .t (" summary.outdated" );
@@ -55,8 +51,6 @@ export default class AiSummaryBox extends Component {
55
51
}
56
52
57
53
resetSummary () {
58
- this .streamedText = " " ;
59
- this .currentIndex = 0 ;
60
54
this .text = " " ;
61
55
this .summarizedOn = null ;
62
56
this .summarizedBy = null ;
@@ -145,26 +139,6 @@ export default class AiSummaryBox extends Component {
145
139
});
146
140
}
147
141
148
- typeCharacter () {
149
- if (this .streamedTextLength < this .text .length ) {
150
- this .streamedText += this .text .charAt (this .streamedTextLength );
151
- this .streamedTextLength ++ ;
152
-
153
- this .typingTimer = later (this , this .typeCharacter , STREAMED_TEXT_SPEED );
154
- } else {
155
- this .typingTimer = null ;
156
- }
157
- }
158
-
159
- onTextUpdate () {
160
- // Reset only if there’s a new summary to process
161
- if (this .typingTimer ) {
162
- cancel (this .typingTimer );
163
- }
164
-
165
- this .typeCharacter ();
166
- }
167
-
168
142
@bind
169
143
async _updateSummary (update ) {
170
144
const topicSummary = {
@@ -173,30 +147,20 @@ export default class AiSummaryBox extends Component {
173
147
... update .ai_topic_summary ,
174
148
};
175
149
const newText = topicSummary .raw || " " ;
150
+ this .text = newText;
176
151
this .loading = false ;
177
152
178
153
if (update .done ) {
179
154
this .text = newText;
180
- this .streamedText = newText;
181
- this .displayedTextLength = newText .length ;
182
155
this .isStreaming = false ;
156
+ this .haltAnimation = true ;
183
157
this .summarizedOn = shortDateNoYear (
184
158
moment (topicSummary .updated_at , " YYYY-MM-DD HH:mm:ss Z" )
185
159
);
186
160
this .summarizedBy = topicSummary .algorithm ;
187
161
this .newPostsSinceSummary = topicSummary .new_posts_since_summary ;
188
162
this .outdated = topicSummary .outdated ;
189
163
this .canRegenerate = topicSummary .outdated && topicSummary .can_regenerate ;
190
-
191
- // Clear pending animations
192
- if (this .typingTimer ) {
193
- cancel (this .typingTimer );
194
- this .typingTimer = null ;
195
- }
196
- } else if (newText .length > this .text .length ) {
197
- this .text = newText;
198
- this .isStreaming = true ;
199
- this .onTextUpdate ();
200
164
}
201
165
}
202
166
@@ -260,8 +224,13 @@ export default class AiSummaryBox extends Component {
260
224
{{#if this . loading }}
261
225
<AiSummarySkeleton />
262
226
{{else }}
263
- <div class =" generated-summary cooked" >
264
- <CookText @ rawText ={{this .streamedText }} />
227
+ <div
228
+ class =" generated-summary"
229
+ {{smoothStreamText this . text this . haltAnimation}}
230
+ >
231
+ {{#if this . haltAnimation }}
232
+ <CookText @ rawText ={{this .text }} />
233
+ {{/if }}
265
234
</div >
266
235
{{#if this . summarizedOn }}
267
236
<div class =" summarized-on" >
0 commit comments