DEV: First version
showing message bus error, but it kinda works? wip on a fix.
This commit is contained in:
parent
9551b1a4d1
commit
3ce8d943a7
|
@ -19,8 +19,7 @@ import I18n from "discourse-i18n";
|
||||||
import DMenu from "float-kit/components/d-menu";
|
import DMenu from "float-kit/components/d-menu";
|
||||||
import DTooltip from "float-kit/components/d-tooltip";
|
import DTooltip from "float-kit/components/d-tooltip";
|
||||||
import AiSummarySkeleton from "../../components/ai-summary-skeleton";
|
import AiSummarySkeleton from "../../components/ai-summary-skeleton";
|
||||||
|
import smoothStreamText from "../../modifiers/smooth-stream-text";
|
||||||
const STREAMED_TEXT_SPEED = 15;
|
|
||||||
|
|
||||||
export default class AiSummaryBox extends Component {
|
export default class AiSummaryBox extends Component {
|
||||||
@service siteSettings;
|
@service siteSettings;
|
||||||
|
@ -36,10 +35,7 @@ export default class AiSummaryBox extends Component {
|
||||||
@tracked canRegenerate = false;
|
@tracked canRegenerate = false;
|
||||||
@tracked loading = false;
|
@tracked loading = false;
|
||||||
@tracked isStreaming = false;
|
@tracked isStreaming = false;
|
||||||
@tracked streamedText = "";
|
@tracked haltAnimation = false;
|
||||||
@tracked currentIndex = 0;
|
|
||||||
typingTimer = null;
|
|
||||||
streamedTextLength = 0;
|
|
||||||
|
|
||||||
get outdatedSummaryWarningText() {
|
get outdatedSummaryWarningText() {
|
||||||
let outdatedText = I18n.t("summary.outdated");
|
let outdatedText = I18n.t("summary.outdated");
|
||||||
|
@ -55,8 +51,6 @@ export default class AiSummaryBox extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
resetSummary() {
|
resetSummary() {
|
||||||
this.streamedText = "";
|
|
||||||
this.currentIndex = 0;
|
|
||||||
this.text = "";
|
this.text = "";
|
||||||
this.summarizedOn = null;
|
this.summarizedOn = null;
|
||||||
this.summarizedBy = null;
|
this.summarizedBy = null;
|
||||||
|
@ -145,26 +139,6 @@ export default class AiSummaryBox extends Component {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
typeCharacter() {
|
|
||||||
if (this.streamedTextLength < this.text.length) {
|
|
||||||
this.streamedText += this.text.charAt(this.streamedTextLength);
|
|
||||||
this.streamedTextLength++;
|
|
||||||
|
|
||||||
this.typingTimer = later(this, this.typeCharacter, STREAMED_TEXT_SPEED);
|
|
||||||
} else {
|
|
||||||
this.typingTimer = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onTextUpdate() {
|
|
||||||
// Reset only if there’s a new summary to process
|
|
||||||
if (this.typingTimer) {
|
|
||||||
cancel(this.typingTimer);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.typeCharacter();
|
|
||||||
}
|
|
||||||
|
|
||||||
@bind
|
@bind
|
||||||
async _updateSummary(update) {
|
async _updateSummary(update) {
|
||||||
const topicSummary = {
|
const topicSummary = {
|
||||||
|
@ -173,13 +147,13 @@ export default class AiSummaryBox extends Component {
|
||||||
...update.ai_topic_summary,
|
...update.ai_topic_summary,
|
||||||
};
|
};
|
||||||
const newText = topicSummary.raw || "";
|
const newText = topicSummary.raw || "";
|
||||||
|
this.text = newText;
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
|
|
||||||
if (update.done) {
|
if (update.done) {
|
||||||
this.text = newText;
|
this.text = newText;
|
||||||
this.streamedText = newText;
|
|
||||||
this.displayedTextLength = newText.length;
|
|
||||||
this.isStreaming = false;
|
this.isStreaming = false;
|
||||||
|
this.haltAnimation = true;
|
||||||
this.summarizedOn = shortDateNoYear(
|
this.summarizedOn = shortDateNoYear(
|
||||||
moment(topicSummary.updated_at, "YYYY-MM-DD HH:mm:ss Z")
|
moment(topicSummary.updated_at, "YYYY-MM-DD HH:mm:ss Z")
|
||||||
);
|
);
|
||||||
|
@ -187,16 +161,6 @@ export default class AiSummaryBox extends Component {
|
||||||
this.newPostsSinceSummary = topicSummary.new_posts_since_summary;
|
this.newPostsSinceSummary = topicSummary.new_posts_since_summary;
|
||||||
this.outdated = topicSummary.outdated;
|
this.outdated = topicSummary.outdated;
|
||||||
this.canRegenerate = topicSummary.outdated && topicSummary.can_regenerate;
|
this.canRegenerate = topicSummary.outdated && topicSummary.can_regenerate;
|
||||||
|
|
||||||
// Clear pending animations
|
|
||||||
if (this.typingTimer) {
|
|
||||||
cancel(this.typingTimer);
|
|
||||||
this.typingTimer = null;
|
|
||||||
}
|
|
||||||
} else if (newText.length > this.text.length) {
|
|
||||||
this.text = newText;
|
|
||||||
this.isStreaming = true;
|
|
||||||
this.onTextUpdate();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -260,8 +224,13 @@ export default class AiSummaryBox extends Component {
|
||||||
{{#if this.loading}}
|
{{#if this.loading}}
|
||||||
<AiSummarySkeleton />
|
<AiSummarySkeleton />
|
||||||
{{else}}
|
{{else}}
|
||||||
<div class="generated-summary cooked">
|
<div
|
||||||
<CookText @rawText={{this.streamedText}} />
|
class="generated-summary"
|
||||||
|
{{smoothStreamText this.text this.haltAnimation}}
|
||||||
|
>
|
||||||
|
{{#if this.haltAnimation}}
|
||||||
|
<CookText @rawText={{this.text}} />
|
||||||
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
{{#if this.summarizedOn}}
|
{{#if this.summarizedOn}}
|
||||||
<div class="summarized-on">
|
<div class="summarized-on">
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
import { cancel, later } from "@ember/runloop";
|
||||||
|
import { htmlSafe } from "@ember/template";
|
||||||
|
import Modifier from "ember-modifier";
|
||||||
|
import { cook } from "discourse/lib/text";
|
||||||
|
|
||||||
|
const STREAMED_TEXT_SPEED = 15;
|
||||||
|
|
||||||
|
export default class SmoothStreamTextModifier extends Modifier {
|
||||||
|
typingTimer = null;
|
||||||
|
displayedText = "";
|
||||||
|
|
||||||
|
modify(element, [text, haltAnimation]) {
|
||||||
|
if (haltAnimation) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._startTypingAnimation(element, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
async _startTypingAnimation(element, text) {
|
||||||
|
if (this.typingTimer) {
|
||||||
|
cancel(this.typingTimer);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.displayedText.length === 0) {
|
||||||
|
element.innerHTML = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
this._typeCharacter(element, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
async _typeCharacter(element, text) {
|
||||||
|
if (this.displayedText.length < text.length) {
|
||||||
|
this.displayedText += text.charAt(this.displayedText.length);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const cookedText = await cook(this.displayedText);
|
||||||
|
element.classList.add("cooked");
|
||||||
|
element.innerHTML = htmlSafe(cookedText);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error cooking text during typing: ", error);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.typingTimer = later(
|
||||||
|
this,
|
||||||
|
this._typeCharacter,
|
||||||
|
element,
|
||||||
|
text,
|
||||||
|
STREAMED_TEXT_SPEED
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
this.typingTimer = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
willRemove() {
|
||||||
|
if (this.typingTimer) {
|
||||||
|
cancel(this.typingTimer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue