@@ -15,6 +15,9 @@ import { bind } from "discourse/lib/decorators";
1515import { i18n } from " discourse-i18n" ;
1616import SmoothStreamer from " ../../lib/smooth-streamer" ;
1717import AiIndicatorWave from " ../ai-indicator-wave" ;
18+ import { cancel , later } from " @ember/runloop" ;
19+
20+ const WORD_TYPING_DELAY = 200 ;
1821
1922export default class ModalDiffModal extends Component {
2023 @service currentUser;
@@ -23,11 +26,18 @@ export default class ModalDiffModal extends Component {
2326 @tracked loading = false ;
2427 @tracked diff;
2528 @tracked suggestion = " " ;
26- @tracked
27- smoothStreamer = new SmoothStreamer (
28- () => this .suggestion ,
29- (newValue ) => (this .suggestion = newValue)
30- );
29+ @tracked isStreaming = false ;
30+ @tracked lastResultText = " " ;
31+ // @tracked
32+ // smoothStreamer = new SmoothStreamer(
33+ // () => this.suggestion,
34+ // (newValue) => (this.suggestion = newValue)
35+ // );
36+ @tracked words = [];
37+ originalWords = [];
38+ diffedSuggestion = " " ;
39+ typingTimer = null ;
40+ currentWordIndex = 0 ;
3141
3242 constructor () {
3343 super (... arguments );
@@ -46,35 +56,92 @@ export default class ModalDiffModal extends Component {
4656 this .messageBus .subscribe (channel, this .updateResult );
4757 }
4858
59+ compareText (oldText = " " , newText = " " ) {
60+ const oldWords = oldText .trim ().split (/ \s + / );
61+ const newWords = newText .trim ().split (/ \s + / );
62+
63+ const diff = [];
64+ let i = 0 ;
65+
66+ while (i < newWords .length ) {
67+ const oldWord = oldWords[i];
68+ const newWord = newWords[i];
69+
70+ let wordHTML;
71+ if (oldWord === undefined ) {
72+ wordHTML = ` <ins>${ newWord} </ins>` ;
73+ } else if (oldWord !== newWord) {
74+ wordHTML = ` <del>${ oldWord} </del> <ins>${ newWord} </ins>` ;
75+ } else {
76+ wordHTML = newWord;
77+ }
78+
79+ if (i === newWords .length - 1 ) {
80+ wordHTML = ` <mark class="highlight">${ wordHTML} </mark>` ;
81+ }
82+
83+ diff .push (wordHTML);
84+ i++ ;
85+ }
86+
87+ return diff .join (" " );
88+ }
89+
4990 @action
5091 async updateResult (result ) {
51- if (result) {
52- this .loading = false ;
92+ this .loading = false ;
93+
94+ const newText = result .result ;
95+ const diffText = newText .slice (this .lastResultText .length ).trim ();
96+ const newWords = diffText .split (/ \s + / ).filter (Boolean );
97+
98+ if (newWords .length > 0 ) {
99+ this .words .push (... newWords);
100+ if (! this .typingTimer ) {
101+ this .streamNextWord ();
102+ }
53103 }
54- await this .smoothStreamer .updateResult (result, " result" );
104+
105+ this .lastResultText = newText;
55106
56107 if (result .done ) {
57- this .diff = result .diff ;
108+ this .isStreaming = false ;
109+ } else {
110+ this .isStreaming = true ;
111+ }
112+ }
113+
114+ startStreamingWords () {
115+ if (this .suggestion === " " ) {
116+ this .suggestion = " " ;
58117 }
118+ this .streamNextWord ();
119+ }
59120
60- const mdTablePromptId = this .currentUser ? .ai_helper_prompts .find (
61- (prompt ) => prompt .name === " markdown_table"
62- ).id ;
121+ streamNextWord () {
122+ if (this .currentWordIndex < this .words .length ) {
123+ this .suggestion += this .words [this .currentWordIndex ] + " " ;
124+ this .diff = this .compareText (
125+ this .args .model .selectedText ,
126+ this .suggestion
127+ );
63128
64- // Markdown table prompt looks better with
65- // before/after results than diff
66- // despite having `type: diff`
67- if (this .args .model .mode === mdTablePromptId) {
68- this .diff = null ;
129+ this .currentWordIndex ++ ;
130+ this .typingTimer = later (this , this .streamNextWord , WORD_TYPING_DELAY );
131+ } else {
132+ this .typingTimer = null ;
69133 }
70134 }
71135
72136 @action
73137 async suggestChanges () {
74- this .smoothStreamer .resetStreaming ();
138+ // this.smoothStreamer.resetStreaming();
75139 this .diff = null ;
76140 this .suggestion = " " ;
77141 this .loading = true ;
142+ this .lastResultText = " " ;
143+ this .words = [];
144+ this .currentWordIndex = 0 ;
78145
79146 try {
80147 return await ajax (" /discourse-ai/ai-helper/stream_suggestion" , {
@@ -123,10 +190,12 @@ export default class ModalDiffModal extends Component {
123190 class ={{concatClass
124191 " composer-ai-helper-modal__suggestion"
125192 " streamable-content"
126- ( if this . smoothStreamer.isStreaming " streaming" " " )
127193 }}
128194 >
129- {{#if this . smoothStreamer.isStreaming }}
195+ <CookText @ rawText ={{this .diff }} class =" cooked" />
196+ {{!-- <div class="composer-ai-helper-modal__old-value">
197+ {{@model.selectedText}}
198+ {{!-- {{#if this.smoothStreamer.isStreaming}}
130199 <CookText
131200 @rawText={{this.smoothStreamer.renderedText}}
132201 class="cooked"
@@ -145,7 +214,7 @@ export default class ModalDiffModal extends Component {
145214 />
146215 </div>
147216 {{/if}}
148- {{/if }}
217+ {{/if}} --}}
149218 </div >
150219 {{/if }}
151220 </div >
0 commit comments