mirror of
https://github.com/koodo-reader/koodo-reader.git
synced 2026-06-16 11:50:41 -04:00
feat: implement update intervals for text accumulation in PopupAssist, PopupDict, and PopupTrans components
This commit is contained in:
@@ -18,6 +18,8 @@ class PopupAssist extends React.Component<PopupAssistProps, PopupAssistState> {
|
||||
private chatBoxRef: React.RefObject<HTMLDivElement>;
|
||||
private textareaRef: React.RefObject<HTMLTextAreaElement>;
|
||||
private singleLineScrollHeight: number = 0;
|
||||
private answerTextAccumulator: string = "";
|
||||
private updateInterval: ReturnType<typeof setInterval> | null = null;
|
||||
|
||||
constructor(props: PopupAssistProps) {
|
||||
super(props);
|
||||
@@ -35,6 +37,30 @@ class PopupAssist extends React.Component<PopupAssistProps, PopupAssistState> {
|
||||
this.chatBoxRef = React.createRef();
|
||||
this.textareaRef = React.createRef();
|
||||
}
|
||||
|
||||
private startUpdateInterval() {
|
||||
if (this.updateInterval) {
|
||||
clearInterval(this.updateInterval);
|
||||
}
|
||||
this.updateInterval = setInterval(() => {
|
||||
if (this.answerTextAccumulator) {
|
||||
this.setState({ answer: this.answerTextAccumulator });
|
||||
if (ConfigService.getReaderConfig("isManualScroll") !== "yes") {
|
||||
this.scrollToBottom();
|
||||
}
|
||||
}
|
||||
}, 150);
|
||||
}
|
||||
|
||||
private stopUpdateInterval(finalAnswer?: string) {
|
||||
if (this.updateInterval) {
|
||||
clearInterval(this.updateInterval);
|
||||
this.updateInterval = null;
|
||||
}
|
||||
if (finalAnswer !== undefined) {
|
||||
this.setState({ answer: finalAnswer });
|
||||
}
|
||||
}
|
||||
componentDidMount(): void {
|
||||
if (this.props.quoteText) {
|
||||
this.setState({ inputQuestion: this.props.quoteText + "\n" }, () => {
|
||||
@@ -132,7 +158,6 @@ class PopupAssist extends React.Component<PopupAssistProps, PopupAssistState> {
|
||||
if (!plugin) {
|
||||
return;
|
||||
}
|
||||
let isFirst = true;
|
||||
let systemPrompt =
|
||||
ConfigService.getReaderConfig("aiAssistancePrompt") ||
|
||||
KookitConfig.DefaultPrompts.aiAssistance;
|
||||
@@ -153,6 +178,8 @@ class PopupAssist extends React.Component<PopupAssistProps, PopupAssistState> {
|
||||
if (!currentQuestion) {
|
||||
return;
|
||||
}
|
||||
this.answerTextAccumulator = "";
|
||||
this.startUpdateInterval();
|
||||
await chatStream(
|
||||
config.endpoint,
|
||||
config.providerId,
|
||||
@@ -165,25 +192,21 @@ class PopupAssist extends React.Component<PopupAssistProps, PopupAssistState> {
|
||||
return;
|
||||
}
|
||||
if (result && result.text) {
|
||||
if (isFirst) {
|
||||
this.setState({ answer: result.text, isWaiting: false });
|
||||
isFirst = false;
|
||||
} else {
|
||||
this.setState({
|
||||
answer: this.state.answer + result.text,
|
||||
});
|
||||
if (!this.answerTextAccumulator) {
|
||||
this.setState({ isWaiting: false });
|
||||
}
|
||||
}
|
||||
if (ConfigService.getReaderConfig("isManualScroll") !== "yes") {
|
||||
this.scrollToBottom();
|
||||
this.answerTextAccumulator += result.text;
|
||||
}
|
||||
}
|
||||
);
|
||||
this.stopUpdateInterval(this.answerTextAccumulator);
|
||||
const finalAnswer = this.answerTextAccumulator;
|
||||
this.answerTextAccumulator = "";
|
||||
if (this.state.mode === "ask") {
|
||||
this.setState({
|
||||
askHistory: [
|
||||
...this.state.askHistory,
|
||||
{ role: "assistant", content: this.state.answer },
|
||||
{ role: "assistant", content: finalAnswer },
|
||||
],
|
||||
answer: "",
|
||||
question: "",
|
||||
@@ -193,7 +216,7 @@ class PopupAssist extends React.Component<PopupAssistProps, PopupAssistState> {
|
||||
this.setState({
|
||||
chatHistory: [
|
||||
...this.state.chatHistory,
|
||||
{ role: "assistant", content: this.state.answer },
|
||||
{ role: "assistant", content: finalAnswer },
|
||||
],
|
||||
answer: "",
|
||||
question: "",
|
||||
@@ -214,7 +237,8 @@ class PopupAssist extends React.Component<PopupAssistProps, PopupAssistState> {
|
||||
if (!plugin) {
|
||||
return;
|
||||
}
|
||||
let isFirst = true;
|
||||
this.answerTextAccumulator = "";
|
||||
this.startUpdateInterval();
|
||||
let res = await getAnswerStream(
|
||||
text,
|
||||
this.state.question,
|
||||
@@ -224,23 +248,16 @@ class PopupAssist extends React.Component<PopupAssistProps, PopupAssistState> {
|
||||
this.state.mode,
|
||||
(result) => {
|
||||
if (result && result.text) {
|
||||
if (isFirst) {
|
||||
this.setState({
|
||||
answer: result.text,
|
||||
isWaiting: false,
|
||||
});
|
||||
isFirst = false;
|
||||
} else {
|
||||
this.setState({
|
||||
answer: this.state.answer + result.text,
|
||||
});
|
||||
if (!this.answerTextAccumulator) {
|
||||
this.setState({ isWaiting: false });
|
||||
}
|
||||
}
|
||||
if (ConfigService.getReaderConfig("isManualScroll") !== "yes") {
|
||||
this.scrollToBottom();
|
||||
this.answerTextAccumulator += result.text;
|
||||
}
|
||||
}
|
||||
);
|
||||
this.stopUpdateInterval(this.answerTextAccumulator);
|
||||
const finalAnswer = this.answerTextAccumulator;
|
||||
this.answerTextAccumulator = "";
|
||||
if (res.data && res.done) {
|
||||
if (this.state.mode === "ask") {
|
||||
this.setState({
|
||||
@@ -248,7 +265,7 @@ class PopupAssist extends React.Component<PopupAssistProps, PopupAssistState> {
|
||||
...this.state.askHistory,
|
||||
{
|
||||
role: "assistant",
|
||||
content: this.state.answer,
|
||||
content: finalAnswer,
|
||||
},
|
||||
],
|
||||
answer: "",
|
||||
@@ -261,7 +278,7 @@ class PopupAssist extends React.Component<PopupAssistProps, PopupAssistState> {
|
||||
...this.state.chatHistory,
|
||||
{
|
||||
role: "assistant",
|
||||
content: this.state.answer,
|
||||
content: finalAnswer,
|
||||
},
|
||||
],
|
||||
answer: "",
|
||||
@@ -270,13 +287,6 @@ class PopupAssist extends React.Component<PopupAssistProps, PopupAssistState> {
|
||||
});
|
||||
}
|
||||
}
|
||||
// if (res.code === 20006) {
|
||||
// this.setState({
|
||||
// isWaiting: false,
|
||||
// answer: "",
|
||||
// question: "",
|
||||
// });
|
||||
// }
|
||||
if (ConfigService.getReaderConfig("isManualScroll") !== "yes") {
|
||||
this.scrollToBottom();
|
||||
}
|
||||
|
||||
@@ -22,6 +22,9 @@ import { marked } from "marked";
|
||||
import { getIframeDoc } from "../../../utils/reader/docUtil";
|
||||
declare var window: any;
|
||||
class PopupDict extends React.Component<PopupDictProps, PopupDictState> {
|
||||
private aiTextAccumulator: string = "";
|
||||
private updateInterval: ReturnType<typeof setInterval> | null = null;
|
||||
|
||||
constructor(props: PopupDictProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
@@ -37,6 +40,27 @@ class PopupDict extends React.Component<PopupDictProps, PopupDictState> {
|
||||
isAiWaiting: false,
|
||||
};
|
||||
}
|
||||
|
||||
private startUpdateInterval() {
|
||||
if (this.updateInterval) {
|
||||
clearInterval(this.updateInterval);
|
||||
}
|
||||
this.updateInterval = setInterval(() => {
|
||||
if (this.aiTextAccumulator) {
|
||||
this.setState({ aiAnswer: this.aiTextAccumulator });
|
||||
}
|
||||
}, 150);
|
||||
}
|
||||
|
||||
private stopUpdateInterval() {
|
||||
if (this.updateInterval) {
|
||||
clearInterval(this.updateInterval);
|
||||
this.updateInterval = null;
|
||||
}
|
||||
if (this.aiTextAccumulator) {
|
||||
this.setState({ aiAnswer: this.aiTextAccumulator });
|
||||
}
|
||||
}
|
||||
componentDidMount() {
|
||||
this.handleLookUp();
|
||||
}
|
||||
@@ -89,7 +113,6 @@ class PopupDict extends React.Component<PopupDictProps, PopupDictState> {
|
||||
(item) => item.key === "custom-ai-dict-plugin"
|
||||
);
|
||||
if (!plugin) return;
|
||||
let isFirst = true;
|
||||
let targetLang =
|
||||
this.state.dictTarget ||
|
||||
ConfigService.getReaderConfig("dictTarget") ||
|
||||
@@ -102,7 +125,9 @@ class PopupDict extends React.Component<PopupDictProps, PopupDictState> {
|
||||
systemPrompt = systemPrompt.replace("{word}", text);
|
||||
systemPrompt = systemPrompt.replace("{to}", targetLang);
|
||||
let config: any = plugin.config || {};
|
||||
this.aiTextAccumulator = "";
|
||||
this.setState({ aiAnswer: "", isAiWaiting: true });
|
||||
this.startUpdateInterval();
|
||||
await chatStream(
|
||||
config.endpoint,
|
||||
config.providerId,
|
||||
@@ -112,24 +137,18 @@ class PopupDict extends React.Component<PopupDictProps, PopupDictState> {
|
||||
[],
|
||||
(result) => {
|
||||
if (result && result.done) {
|
||||
this.setState({ isAiWaiting: false });
|
||||
return;
|
||||
}
|
||||
if (result && result.text) {
|
||||
if (isFirst) {
|
||||
this.setState({
|
||||
aiAnswer: result.text,
|
||||
isAiWaiting: false,
|
||||
});
|
||||
isFirst = false;
|
||||
} else {
|
||||
this.setState({
|
||||
aiAnswer: this.state.aiAnswer + result.text,
|
||||
});
|
||||
if (!this.aiTextAccumulator) {
|
||||
this.setState({ isAiWaiting: false });
|
||||
}
|
||||
this.aiTextAccumulator += result.text;
|
||||
}
|
||||
}
|
||||
);
|
||||
this.stopUpdateInterval();
|
||||
this.aiTextAccumulator = "";
|
||||
this.setState({ isAiWaiting: false, dictText: " " });
|
||||
return;
|
||||
} else if (
|
||||
@@ -216,8 +235,9 @@ class PopupDict extends React.Component<PopupDictProps, PopupDictState> {
|
||||
};
|
||||
handleDictionaryStream = async (text: string, isFullAnalysis: boolean) => {
|
||||
try {
|
||||
this.aiTextAccumulator = "";
|
||||
this.setState({ aiAnswer: "", isAiWaiting: true });
|
||||
let isFirst = true;
|
||||
this.startUpdateInterval();
|
||||
let res = await getDictionaryStream(
|
||||
text,
|
||||
"auto",
|
||||
@@ -226,19 +246,21 @@ class PopupDict extends React.Component<PopupDictProps, PopupDictState> {
|
||||
isFullAnalysis,
|
||||
(result) => {
|
||||
if (result && result.text) {
|
||||
if (isFirst) {
|
||||
this.setState({ aiAnswer: result.text, isAiWaiting: false });
|
||||
isFirst = false;
|
||||
} else {
|
||||
this.setState({ aiAnswer: this.state.aiAnswer + result.text });
|
||||
if (!this.aiTextAccumulator) {
|
||||
this.setState({ isAiWaiting: false });
|
||||
}
|
||||
this.aiTextAccumulator += result.text;
|
||||
}
|
||||
}
|
||||
);
|
||||
this.stopUpdateInterval();
|
||||
this.aiTextAccumulator = "";
|
||||
if (res && res.done) {
|
||||
this.setState({ isAiWaiting: false });
|
||||
}
|
||||
} catch (error) {
|
||||
this.stopUpdateInterval();
|
||||
this.aiTextAccumulator = "";
|
||||
this.setState({ isAiWaiting: false });
|
||||
console.error(error);
|
||||
}
|
||||
|
||||
@@ -14,6 +14,9 @@ import { chatStream } from "../../../utils/request/common";
|
||||
import { getIframeDoc } from "../../../utils/reader/docUtil";
|
||||
declare var window: any;
|
||||
class PopupTrans extends React.Component<PopupTransProps, PopupTransState> {
|
||||
private textAccumulator: string = "";
|
||||
private updateInterval: ReturnType<typeof setInterval> | null = null;
|
||||
|
||||
constructor(props: PopupTransProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
@@ -26,6 +29,27 @@ class PopupTrans extends React.Component<PopupTransProps, PopupTransState> {
|
||||
isFinishOutput: false,
|
||||
};
|
||||
}
|
||||
|
||||
private startUpdateInterval() {
|
||||
if (this.updateInterval) {
|
||||
clearInterval(this.updateInterval);
|
||||
}
|
||||
this.updateInterval = setInterval(() => {
|
||||
if (this.textAccumulator) {
|
||||
this.setState({ translatedText: this.textAccumulator });
|
||||
}
|
||||
}, 150);
|
||||
}
|
||||
|
||||
private stopUpdateInterval() {
|
||||
if (this.updateInterval) {
|
||||
clearInterval(this.updateInterval);
|
||||
this.updateInterval = null;
|
||||
}
|
||||
if (this.textAccumulator) {
|
||||
this.setState({ translatedText: this.textAccumulator });
|
||||
}
|
||||
}
|
||||
async componentDidMount() {
|
||||
let originalText = this.props.originalText.replace(/(\r\n|\n|\r)/gm, "");
|
||||
this.setState({ originalText: originalText });
|
||||
@@ -107,7 +131,6 @@ class PopupTrans extends React.Component<PopupTransProps, PopupTransState> {
|
||||
if (!plugin) {
|
||||
return;
|
||||
}
|
||||
let isFirst = true;
|
||||
let targetLang =
|
||||
ConfigService.getReaderConfig("transTarget") ||
|
||||
getDefaultTransTarget(plugin.langList);
|
||||
@@ -124,6 +147,8 @@ class PopupTrans extends React.Component<PopupTransProps, PopupTransState> {
|
||||
systemPrompt = systemPrompt.replace("{to}", targetLang);
|
||||
systemPrompt = systemPrompt.replace("{text}", text);
|
||||
let config: any = plugin.config || {};
|
||||
this.textAccumulator = "";
|
||||
this.startUpdateInterval();
|
||||
await chatStream(
|
||||
config.endpoint,
|
||||
config.providerId,
|
||||
@@ -133,23 +158,15 @@ class PopupTrans extends React.Component<PopupTransProps, PopupTransState> {
|
||||
[],
|
||||
(result) => {
|
||||
if (result && result.done) {
|
||||
this.setState({ isFinishOutput: true });
|
||||
return;
|
||||
}
|
||||
if (result && result.text) {
|
||||
if (isFirst) {
|
||||
this.setState({
|
||||
translatedText: result.text,
|
||||
});
|
||||
isFirst = false;
|
||||
} else {
|
||||
this.setState({
|
||||
translatedText: this.state.translatedText + result.text,
|
||||
});
|
||||
}
|
||||
this.textAccumulator += result.text;
|
||||
}
|
||||
}
|
||||
);
|
||||
this.stopUpdateInterval();
|
||||
this.textAccumulator = "";
|
||||
this.setState({ isFinishOutput: true });
|
||||
} else if (
|
||||
this.props.isAuthed &&
|
||||
@@ -165,13 +182,14 @@ class PopupTrans extends React.Component<PopupTransProps, PopupTransState> {
|
||||
if (!plugin) {
|
||||
return;
|
||||
}
|
||||
let isFirst = true;
|
||||
let targetLang =
|
||||
ConfigService.getReaderConfig("transTarget") ||
|
||||
getDefaultTransTarget(plugin.langList);
|
||||
if (targetLang === "Traditional Chinese") {
|
||||
targetLang = "繁体中文";
|
||||
}
|
||||
this.textAccumulator = "";
|
||||
this.startUpdateInterval();
|
||||
await getTransStream(
|
||||
text,
|
||||
ConfigService.getReaderConfig("transSource") || "Automatic",
|
||||
@@ -179,23 +197,15 @@ class PopupTrans extends React.Component<PopupTransProps, PopupTransState> {
|
||||
getDefaultTransTarget(plugin.langList),
|
||||
(result) => {
|
||||
if (result && result.done) {
|
||||
this.setState({ isFinishOutput: true });
|
||||
return;
|
||||
}
|
||||
if (result && result.text) {
|
||||
if (isFirst) {
|
||||
this.setState({
|
||||
translatedText: result.text,
|
||||
});
|
||||
isFirst = false;
|
||||
} else {
|
||||
this.setState({
|
||||
translatedText: this.state.translatedText + result.text,
|
||||
});
|
||||
}
|
||||
this.textAccumulator += result.text;
|
||||
}
|
||||
}
|
||||
);
|
||||
this.stopUpdateInterval();
|
||||
this.textAccumulator = "";
|
||||
this.setState({ isFinishOutput: true });
|
||||
}
|
||||
};
|
||||
|
||||
@@ -34,21 +34,6 @@ export const getTransStream = async (
|
||||
);
|
||||
return result;
|
||||
};
|
||||
export const getSummaryStream = async (
|
||||
text: string,
|
||||
to: string,
|
||||
onMessage: (result) => void
|
||||
) => {
|
||||
let readerRequest = await getReaderRequest();
|
||||
let result = await readerRequest.getSummaryFetch(
|
||||
{
|
||||
text,
|
||||
to,
|
||||
},
|
||||
onMessage
|
||||
);
|
||||
return result;
|
||||
};
|
||||
export const getAnswerStream = async (
|
||||
text: string,
|
||||
question: string,
|
||||
|
||||
Reference in New Issue
Block a user