Skip to content
This repository was archived by the owner on Jul 22, 2025. It is now read-only.

Commit 2755cac

Browse files
committed
WIP dump
1 parent 1573e6a commit 2755cac

File tree

2 files changed

+113
-21
lines changed

2 files changed

+113
-21
lines changed

assets/javascripts/discourse/components/modal/diff-modal.gjs

Lines changed: 90 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ import { bind } from "discourse/lib/decorators";
1515
import { i18n } from "discourse-i18n";
1616
import SmoothStreamer from "../../lib/smooth-streamer";
1717
import AiIndicatorWave from "../ai-indicator-wave";
18+
import { cancel, later } from "@ember/runloop";
19+
20+
const WORD_TYPING_DELAY = 200;
1821

1922
export 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>

assets/stylesheets/modules/ai-helper/common/ai-helper.scss

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,34 @@
1111
text-decoration: line-through;
1212
}
1313

14+
mark {
15+
background-color: var(--highlight-low);
16+
}
17+
1418
.preview-area {
1519
height: 200px;
1620
}
1721
}
1822

23+
// TODO: cleanup and scope to inline-diff class
24+
.composer-ai-helper-modal__suggestion .cooked {
25+
ins,
26+
del {
27+
text-decoration: none;
28+
}
29+
30+
mark {
31+
background-color: var(--highlight-low) !important;
32+
border-bottom: 2px solid var(--highlight-high) !important;
33+
34+
ins,
35+
del {
36+
background: transparent;
37+
text-decoration: none;
38+
}
39+
}
40+
}
41+
1942
@keyframes fadeOpacity {
2043
0% {
2144
opacity: 1;

0 commit comments

Comments
 (0)