fix: update font files and enhance popup assist functionality with new features

This commit is contained in:
troyeguo
2025-05-02 10:12:21 +08:00
parent c4747ef4b8
commit 8d47f7a79a
17 changed files with 570 additions and 305 deletions

View File

@@ -40,7 +40,7 @@
"jszip": "^3.10.1",
"localforage": "^1.10.0",
"mammoth": "^1.8.0",
"marked": "^15.0.3",
"marked": "^15.0.11",
"megajs": "^1.3.7",
"mhtml2html": "^3.0.0",
"node-machine-id": "^1.1.12",

View File

File diff suppressed because one or more lines are too long

View File

File diff suppressed because one or more lines are too long

View File

@@ -63,6 +63,8 @@
"Default": "Default",
"Small": "Small",
"Medium": "Medium",
"Reading Assistant": "Reading",
"Chat Assistant": "Chat",
"Large": "Large",
"Cancellation successful": "Cancellation successful",
"Only supported by desktop version": "Only supported with the desktop version",

View File

@@ -284,6 +284,18 @@
"Your trial period has expired": "您的试用已结束",
"Exit Pro": "退出专业版",
"I've paid": "我已支付",
"Summarize this chapter for me": "帮我总结这一章的内容",
"What are the key points of this chapter": "整理并输出格式清晰的内容要点",
"Remove data source": "移除数据源",
"Ask anything about this chapter": "询问关于这篇文章的任何内容",
"Ask anything about reading or learning": "询问关于阅读和学习的任何问题",
"Hi there! I'm happy to help with any questions about reading or learning": "你好,我可以解答你关于阅读和学习的任何问题。",
"Hi there! What questions do you have about this chapter?": "你好,关于这章内容,您有什么想知道的吗?",
"Thinking, please wait...": "思考中,请稍候...",
"Chat Assistant": "AI 助手",
"Reading Assistant": "AI 问书",
"Recommend me some books from Colleen Hoover": "推荐几本刘慈欣的小说",
"Explain Stoicism and its principles to me": "斯多葛学派的主要思想和观点是什么",
"Need help": "需要帮助",
"Upgrade": "升级",
"You haven't upgraded to Pro yet": "您还不是会员哦",

View File

Binary file not shown.

View File

@@ -98,4 +98,6 @@
<glyph unicode="&#xe958;" glyph-name="s3compatible" d="M620.757 789.333c-155.434 0-285.76-107.2-321.194-251.669-2.85 0.161-6.191 0.254-9.552 0.256h-0.006c-113.024 0-204.672-91.627-204.672-204.672 0-0.082 0-0.18 0-0.278 0-56.404 22.914-107.457 59.942-144.357l0.006-0.006c36.887-36.982 87.897-59.862 144.25-59.862 0.167 0 0.334 0 0.501 0.001h-0.026c56.581 0.026 107.783 23.034 144.783 60.194l0.007 0.008c32.106 32.107 50.133 75.094 55.317 122.838l-74.112 5.823c-16.79 2.090-22.187 14.805-11.968 28.373l125.568 155.351c10.155 13.546 23.957 11.882 30.613-3.755l71.19-173.974c6.656-15.637-1.75-26.688-18.56-24.618l-55.126 5.077c-6.25-57.643-32.192-108.523-68.458-148.864 38.654-17.19 83.752-27.2 131.188-27.2 0.109 0 0.218 0 0.327 0h-0.017c182.614 0 330.667 148.053 330.667 330.667s-148.053 330.666-330.667 330.666z" />
<glyph unicode="&#xe959;" glyph-name="desktop" horiz-adv-x="887" d="M723.49 863.029h-559.515c-44.161 0-79.975-41.325-79.975-92.279v-461.126c0-50.866 35.814-92.188 79.975-92.188h159.873v-92.194h-159.873v-92.276h559.515v92.276h-159.872v92.194h159.872c44.080 0 79.899 41.322 79.899 92.187v461.127c0 50.954-35.82 92.279-79.899 92.279z" />
<glyph unicode="&#xe95a;" glyph-name="phone" horiz-adv-x="751" d="M547.718 899c39.814 0 72.090-44.012 72.090-98.304v-704.515c0-54.286-32.276-98.304-72.090-98.304h-344.429c-39.814 0-72.089 44.018-72.089 98.304v704.515c0 54.292 32.275 98.304 72.089 98.304h344.429zM375.503 281.87c-24.331 0-44.055-26.894-44.055-60.079 0-33.178 19.724-60.075 44.055-60.075s44.055 26.897 44.055 60.075c0 33.184-19.724 60.079-44.055 60.079z" />
<glyph unicode="&#xe95b;" glyph-name="send" d="M1015.485 483.536c-7.599 15.198-20.198 27.797-35.396 35.396l-865.299 432.649c-18.998 9.499-40.495 10.999-60.593 4.299s-36.296-20.898-45.795-39.795c-9.899-19.898-11.099-43.095-3.1-63.893l155.382-404.253-155.382-404.153c-7.599-19.798-6.999-41.395 1.6-60.693 8.599-19.398 24.297-34.196 43.995-41.795 9.199-3.5 18.798-5.299 28.497-5.299 12.199 0 24.397 2.8 35.496 8.299l865.199 432.749c39.095 19.598 54.994 67.392 35.396 106.488zM79.095 15.491l151.182 392.954h310.364c21.797 0 39.495 17.698 39.495 39.495s-17.698 39.495-39.495 39.495h-310.364l-150.782 393.354 865.199-432.649-865.599-432.649z" />
<glyph unicode="&#xe95c;" glyph-name="loading" d="M512 864c-229.717 0-416-186.283-416-416s186.283-416 416-416v104.021c-172.283 0.024-311.936 139.693-311.936 311.979 0 172.301 139.678 311.979 311.979 311.979s311.979-139.678 311.979-311.979v0h103.979c0 229.717-186.283 416-416 416z" />
</font></defs></svg>

Before

Width:  |  Height:  |  Size: 92 KiB

After

Width:  |  Height:  |  Size: 93 KiB

View File

Binary file not shown.

View File

Binary file not shown.

View File

@@ -1,10 +1,10 @@
@font-face {
font-family: 'icomoon';
src: url('fonts/icomoon.eot?6v0s5e');
src: url('fonts/icomoon.eot?6v0s5e#iefix') format('embedded-opentype'),
url('fonts/icomoon.ttf?6v0s5e') format('truetype'),
url('fonts/icomoon.woff?6v0s5e') format('woff'),
url('fonts/icomoon.svg?6v0s5e#icomoon') format('svg');
src: url('fonts/icomoon.eot?rfb0hg');
src: url('fonts/icomoon.eot?rfb0hg#iefix') format('embedded-opentype'),
url('fonts/icomoon.ttf?rfb0hg') format('truetype'),
url('fonts/icomoon.woff?rfb0hg') format('woff'),
url('fonts/icomoon.svg?rfb0hg#icomoon') format('svg');
font-weight: normal;
font-style: normal;
font-display: block;
@@ -25,6 +25,12 @@
-moz-osx-font-smoothing: grayscale;
}
.icon-loading:before {
content: "\e95c";
}
.icon-send:before {
content: "\e95b";
}
.icon-email:before {
content: "\e953";
}

View File

@@ -15,75 +15,70 @@ import {
import toast from "react-hot-toast";
import DatabaseService from "../../../utils/storage/databaseService";
import { checkPlugin } from "../../../utils/common";
import { getSummaryStream } from "../../../utils/request/reader";
import { getAnswerStream } from "../../../utils/request/reader";
import { marked } from "marked";
import { sampleQuestion } from "../../../constants/settingList";
declare var window: any;
class PopupAssist extends React.Component<PopupAssistProps, PopupAssistState> {
private chatBoxRef: React.RefObject<HTMLDivElement>;
constructor(props: PopupAssistProps) {
super(props);
this.state = {
sumText: this.props.t("Please wait"),
prototype: "",
sumService:
ConfigService.getReaderConfig("sumService") ||
answer: "",
aiService:
ConfigService.getReaderConfig("aiService") ||
"official-ai-assistant-plugin",
sumTarget:
ConfigService.getReaderConfig("sumTarget") ||
getDefaultTransTarget({
English: "English",
"Simplified Chinese": "Simplified Chinese",
"Traditional Chinese": "Traditional Chinese",
}),
isAddNew: false,
isWaiting: false,
question: "",
chatHistory: [],
askHistory: [],
mode: "ask",
inputQuestion: "",
};
this.chatBoxRef = React.createRef();
}
componentDidMount() {
this.handleSum();
}
async handleSum() {
let originalText = this.props.originalText
.replace(/(\r\n|\n|\r)/gm, "")
.replace(/-/gm, "")
// Remove common garbage characters
.replace(
/[^\x20-\x7E\u00A0-\u00FF\u0100-\u017F\u4E00-\u9FFF\u3000-\u303F]/g,
""
)
// Remove consecutive spaces
.replace(/\s{2,}/g, " ")
.trim();
scrollToBottom = () => {
if (this.chatBoxRef.current) {
const scrollHeight = this.chatBoxRef.current.scrollHeight;
const height = this.chatBoxRef.current.clientHeight;
const maxScrollTop = scrollHeight - height;
this.chatBoxRef.current.scrollTop = maxScrollTop > 0 ? maxScrollTop : 0;
}
};
async handleAnswer() {
let originalText =
this.state.mode === "ask"
? this.props.originalText
.replace(/(\r\n|\n|\r)/gm, "")
.replace(/-/gm, "")
// Remove common garbage characters
.replace(
/[^\x20-\x7E\u00A0-\u00FF\u0100-\u017F\u4E00-\u9FFF\u3000-\u303F]/g,
""
)
// Remove consecutive spaces
.replace(/\s{2,}/g, " ")
.trim()
: "";
if (
(!this.state.sumService ||
(!this.state.aiService ||
this.props.plugins.findIndex(
(item) => item.key === this.state.sumService
(item) => item.key === this.state.aiService
) === -1) &&
!this.props.isAuthed
) {
this.setState({ isAddNew: true });
}
this.handleSummary(originalText);
this.handleDoAnswer(originalText);
}
handleSummary = async (text: string) => {
let sumText = "";
handleDoAnswer = async (text: string) => {
try {
if (
this.state.sumService &&
this.state.sumService !== "official-ai-assistant-plugin"
this.state.aiService &&
this.state.aiService !== "official-ai-assistant-plugin"
) {
let plugin = this.props.plugins.find(
(item) => item.key === this.state.sumService
);
if (!plugin) return;
let dictFunc = plugin.script;
// eslint-disable-next-line no-eval
eval(dictFunc);
sumText = await window.getsumText(
text,
"auto",
this.state.sumTarget,
axios,
this.props.t,
plugin.config
);
} else if (this.props.isAuthed) {
let plugin = this.props.plugins.find(
(item) => item.key === "official-ai-assistant-plugin"
@@ -92,283 +87,437 @@ class PopupAssist extends React.Component<PopupAssistProps, PopupAssistState> {
return;
}
let isFirst = true;
getSummaryStream(
let res = await getAnswerStream(
text,
ConfigService.getReaderConfig("sumTarget") ||
getDefaultTransTarget(plugin.langList),
this.state.question,
this.state.mode === "ask"
? this.state.askHistory
: this.state.chatHistory,
this.state.mode,
(result) => {
console.log(result, "result");
if (result && result.text) {
if (isFirst) {
this.setState({
sumText: result.text,
answer: result.text,
isWaiting: false,
});
isFirst = false;
} else {
this.setState({
sumText: this.state.sumText + result.text,
answer: this.state.answer + result.text,
});
}
}
this.scrollToBottom();
}
);
}
if (sumText.startsWith("https://")) {
openExternalUrl(sumText, true);
} else {
this.setState(
{
sumText: sumText,
},
() => {
let moreElement = document.querySelector(".dict-learn-more");
if (moreElement) {
moreElement.addEventListener("click", () => {
openExternalUrl(
window.learnMoreUrl || "https://www.koodoreader.com"
);
});
}
console.log(res, "res4534543");
if (res.data && res.data.done) {
if (this.state.mode === "ask") {
this.setState({
askHistory: [
...this.state.askHistory,
{
role: "assistant",
content: this.state.answer,
},
],
answer: "",
question: "",
isWaiting: false,
});
} else {
this.setState({
chatHistory: [
...this.state.chatHistory,
{
role: "assistant",
content: this.state.answer,
},
],
answer: "",
question: "",
isWaiting: false,
});
}
);
}
if (res.code === 20006) {
this.setState({
isWaiting: false,
answer: "",
question: "",
});
}
this.scrollToBottom();
}
} catch (error) {
console.error(error);
this.setState({
sumText: this.props.t("Error happened"),
answer: this.props.t("Error happened"),
});
}
};
handleChangesumService = (sumService: string) => {
let plugin = this.props.plugins.find((item) => item.key === sumService);
handleChangeAiService = (aiService: string) => {
let plugin = this.props.plugins.find((item) => item.key === aiService);
if (!plugin) {
return;
}
this.setState(
{
sumService: sumService,
aiService: aiService,
isAddNew: false,
},
() => {
ConfigService.setReaderConfig("sumService", sumService);
ConfigService.setReaderConfig("aiService", aiService);
if (!plugin) return;
this.setState(
{
sumTarget: getDefaultTransTarget(plugin.langList),
},
() => {
if (!plugin) return;
ConfigService.setReaderConfig(
"sumTarget",
getDefaultTransTarget(plugin.langList)
);
this.handleSum();
}
);
this.handleAnswer();
}
);
};
render() {
const renderSumBox = () => {
handleRenderHistoryMessage = (message: any[]) => {
return message.map((item, index) => {
return (
<div className="dict-container">
<div className="dict-service-container">
<select
className="dict-service-selector"
style={{ margin: 0 }}
onChange={(event: React.ChangeEvent<HTMLSelectElement>) => {
if (event.target.value === "add-new") {
this.setState({ isAddNew: true });
return;
}
this.handleChangesumService(event.target.value);
}}
>
{this.props.plugins
.filter((item) => item.type === "assistant")
.map((item) => {
return (
<option
value={item.key}
key={item.key}
className="add-dialog-shelf-list-option"
selected={
this.state.sumService === item.key ? true : false
}
>
{this.props.t(item.displayName)}
</option>
);
})}
<option
value={"add-new"}
key={"add-new"}
className="add-dialog-shelf-list-option"
>
{this.props.t("Add new plugin")}
</option>
</select>
</div>
<div className="dict-service-container" style={{ right: 150 }}>
<select
className="dict-service-selector"
style={{ margin: 0 }}
onChange={(event: React.ChangeEvent<HTMLSelectElement>) => {
let plugin = this.props.plugins.find(
(item) => item.key === this.state.sumService
);
if (!plugin) {
return;
}
this.setState(
{
sumTarget:
event.target.value ||
getDefaultTransTarget(plugin.langList),
},
() => {
ConfigService.setReaderConfig(
"sumTarget",
event.target.value
);
this.handleSum();
}
);
}}
>
{this.props.plugins.find(
(item) => item.key === this.state.sumService
)?.langList &&
Object.keys(
this.props.plugins.find(
(item) => item.key === this.state.sumService
)?.langList as any
).map((item, index) => {
return (
<option
value={item}
key={index}
className="add-dialog-shelf-list-option"
selected={this.state.sumTarget === item ? true : false}
>
{this.props.t(
Object.values(
this.props.plugins.find(
(item) => item.key === this.state.sumService
)?.langList as any[]
)[index]
)}
</option>
);
})}
</select>
</div>
{this.state.isAddNew && (
<div
className="trans-add-new-container"
style={{ fontWeight: 500, marginTop: "60px", height: "170px" }}
>
<textarea
name="url"
placeholder={this.props.t(
"Paste the code of the plugin here, check out document to learn how to get more plugins"
)}
id="trans-add-content-box"
className="trans-add-content-box"
onContextMenu={() => {
handleContextMenu("trans-add-content-box");
}}
/>
<div className="trans-add-button-container">
<div
className="trans-add-cancel"
style={{ color: "#2084e8" }}
onClick={() => {
if (
ConfigService.getReaderConfig("lang") &&
ConfigService.getReaderConfig("lang").startsWith("zh")
) {
openExternalUrl(WEBSITE_URL + "/zh/plugin");
} else {
openExternalUrl(WEBSITE_URL + "/en/plugin");
}
}}
>
<Trans>Document</Trans>
</div>
<div
className="trans-add-cancel"
onClick={() => {
this.setState({ isAddNew: false });
}}
>
<Trans>Cancel</Trans>
</div>
<div
className="trans-add-confirm"
style={{ backgroundColor: "#2084e8" }}
onClick={async () => {
let value: string = (
document.querySelector(
"#trans-add-content-box"
) as HTMLTextAreaElement
).value;
if (value) {
let plugin: any = JSON.parse(value);
plugin.key = plugin.identifier;
if (!(await checkPlugin(plugin))) {
toast.error(this.props.t("Plugin verification failed"));
return;
}
if (
this.props.plugins.find(
(item) => item.key === plugin.key
)
) {
await DatabaseService.updateRecord(plugin, "plugins");
} else {
await DatabaseService.saveRecord(plugin, "plugins");
}
this.props.handleFetchPlugins();
toast.success(this.props.t("Addition successful"));
}
this.setState({
isAddNew: false,
sumText: this.props.t("Please select the service"),
});
}}
>
<Trans>Confirm</Trans>
</div>
</div>
</div>
)}
{!this.state.isAddNew && (
<>
<div
className="dict-text-box"
style={{ marginTop: "60px", height: "180px" }}
>
{Parser(
DOMPurify.sanitize(
this.state.sumText + "<address></address>"
) || " ",
{
replace: (_domNode) => {},
}
)}
</div>
<p className="dict-learn-more">
{this.props.t("Generated with AI")}
</p>
</>
<div
key={index}
className={
item.role === "assistant"
? "popup-message-assistant"
: "popup-message-user"
}
>
{Parser(
DOMPurify.sanitize(
marked.parse(item.content) + "<address></address>"
) || " ",
{
replace: (_domNode) => {},
}
)}
</div>
);
};
return renderSumBox();
});
};
handleNewQuestion = (question: string) => {
if (this.state.mode === "ask") {
this.setState(
{
askHistory: [
...this.state.askHistory,
{
role: "user",
content: this.props.t(question),
},
],
question: this.props.t(question),
answer: "",
isWaiting: true,
},
() => {
this.handleAnswer();
}
);
} else {
this.setState(
{
chatHistory: [
...this.state.chatHistory,
{
role: "user",
content: this.props.t(question),
},
],
question: this.props.t(question),
answer: this.props.t(""),
isWaiting: true,
},
() => {
this.handleAnswer();
}
);
}
setTimeout(() => {
this.scrollToBottom();
}, 100);
};
render() {
return (
<div className="dict-container">
<div
className="dict-service-container"
style={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
width: "calc(100% - 50px)",
top: "20px",
}}
>
<div
style={{
display: "flex",
alignItems: "center",
justifyContent: "flex-start",
}}
>
<div
className={
this.state.mode === "ask"
? "trans-service-selector"
: "trans-service-selector-inactive"
}
onClick={() => {
this.setState({ isAddNew: false, mode: "ask" });
}}
>
<span className={`icon-bookmark trans-icon`}></span>
{this.props.t("Reading Assistant")}
</div>
<div
className={
this.state.mode === "chat"
? "trans-service-selector"
: "trans-service-selector-inactive"
}
onClick={() => {
this.setState({ isAddNew: false, mode: "chat" });
}}
>
<span className={`icon-idea trans-icon`}></span>
{this.props.t("Chat Assistant")}
</div>
</div>
<select
className="dict-service-selector"
style={{ margin: 0, color: "#f16464" }}
onChange={(event: React.ChangeEvent<HTMLSelectElement>) => {
if (event.target.value === "add-new") {
this.setState({ isAddNew: true });
return;
}
this.handleChangeAiService(event.target.value);
}}
>
{this.props.plugins
.filter((item) => item.type === "assistant")
.map((item) => {
return (
<option
value={item.key}
key={item.key}
className="add-dialog-shelf-list-option"
selected={this.state.aiService === item.key ? true : false}
>
{this.props.t(item.displayName)}
</option>
);
})}
<option
value={"add-new"}
key={"add-new"}
className="add-dialog-shelf-list-option"
>
{this.props.t("Add new plugin")}
</option>
</select>
</div>
{this.state.isAddNew && (
<div
className="trans-add-new-container"
style={{ fontWeight: 500, marginTop: "60px", height: "170px" }}
>
<textarea
name="url"
placeholder={this.props.t(
"Paste the code of the plugin here, check out document to learn how to get more plugins"
)}
id="trans-add-content-box"
className="trans-add-content-box"
onContextMenu={() => {
handleContextMenu("trans-add-content-box");
}}
/>
<div className="trans-add-button-container">
<div
className="trans-add-cancel"
style={{ color: "#f16464" }}
onClick={() => {
if (
ConfigService.getReaderConfig("lang") &&
ConfigService.getReaderConfig("lang").startsWith("zh")
) {
openExternalUrl(WEBSITE_URL + "/zh/plugin");
} else {
openExternalUrl(WEBSITE_URL + "/en/plugin");
}
}}
>
<Trans>Document</Trans>
</div>
<div
className="trans-add-cancel"
onClick={() => {
this.setState({ isAddNew: false });
}}
>
<Trans>Cancel</Trans>
</div>
<div
className="trans-add-confirm"
onClick={async () => {
let value: string = (
document.querySelector(
"#trans-add-content-box"
) as HTMLTextAreaElement
).value;
if (value) {
let plugin: any = JSON.parse(value);
plugin.key = plugin.identifier;
if (!(await checkPlugin(plugin))) {
toast.error(this.props.t("Plugin verification failed"));
return;
}
if (
this.props.plugins.find((item) => item.key === plugin.key)
) {
await DatabaseService.updateRecord(plugin, "plugins");
} else {
await DatabaseService.saveRecord(plugin, "plugins");
}
this.props.handleFetchPlugins();
toast.success(this.props.t("Addition successful"));
}
this.setState({
isAddNew: false,
answer: this.props.t("Please select the service"),
});
}}
>
<Trans>Confirm</Trans>
</div>
</div>
</div>
)}
{!this.state.isAddNew && (
<>
<div
className="dict-text-box"
style={{
marginTop: "60px",
height: "225px",
paddingBottom: "60px",
paddingLeft: "0px",
paddingRight: "0px",
}}
ref={this.chatBoxRef}
>
{this.handleRenderHistoryMessage(
this.state.mode === "ask"
? this.state.askHistory
: this.state.chatHistory
)}
{this.state.isWaiting ? (
<div
className="popup-message-assistant"
style={{ float: "left" }}
>
<span
className="icon-loading"
style={{
marginRight: "10px",
marginTop: "5px",
}}
></span>
<span>{this.props.t("Thinking, please wait...")}</span>
</div>
) : (this.state.mode === "ask"
? this.state.askHistory
: this.state.chatHistory
).length > 0 ? (
<div className="popup-message-assistant">
{Parser(
DOMPurify.sanitize(
marked.parse(this.state.answer ? this.state.answer : "") +
"<address></address>"
) || " ",
{
replace: (_domNode) => {},
}
)}
</div>
) : (
<div className="popup-message-assistant">
{this.state.mode === "ask"
? this.props.t(
"Hi there! What questions do you have about this chapter?"
)
: this.props.t(
"Hi there! I'm happy to help with any questions about reading or learning"
)}
</div>
)}
</div>
<div className="popup-assist-shortcut-container">
{sampleQuestion
.filter((item) => item.mode === this.state.mode)
.map((item) => {
return (
<div
className="popup-assist-shortcut"
onClick={() => {
this.handleNewQuestion(item.question);
}}
>
{this.props.t(item.question)}
</div>
);
})}
</div>
<div>
<textarea
name="url"
placeholder={this.props.t(
this.state.mode === "ask"
? "Ask anything about this chapter"
: "Ask anything about reading or learning"
)}
id="trans-add-content-box"
className="trans-add-content-box"
style={{
height: "40px",
paddingRight: "40px",
resize: "none",
}}
onContextMenu={() => {
handleContextMenu("trans-add-content-box");
}}
value={this.state.inputQuestion}
onChange={(event: React.ChangeEvent<HTMLTextAreaElement>) => {
this.setState({
inputQuestion: event.target.value,
});
}}
/>
<span
className={`icon-send send-icon`}
onClick={() => {
if (this.state.answer || this.state.isWaiting) {
return;
}
this.handleNewQuestion(this.state.inputQuestion);
this.setState({
inputQuestion: "",
});
}}
></span>
</div>
</>
)}
</div>
);
}
}
export default PopupAssist;

View File

@@ -11,9 +11,13 @@ export interface PopupAssistProps {
t: (title: string) => string;
}
export interface PopupAssistState {
sumText: string;
prototype: string;
sumService: string;
sumTarget: string;
aiService: string;
isAddNew: boolean;
isWaiting: boolean;
question: string;
askHistory: any[];
chatHistory: any[];
answer: string;
mode: string;
inputQuestion: string;
}

View File

@@ -0,0 +1,50 @@
.send-icon {
position: absolute;
bottom: 16px;
right: 35px;
font-size: 18px;
cursor: pointer;
}
.popup-assist-shortcut-container {
width: 100%;
}
.popup-assist-shortcut {
margin-right: 20px;
color: #f16464;
opacity: 0.8;
cursor: pointer;
margin-top: 10px;
}
.trans-add-content-box::placeholder {
opacity: 0.8;
}
.popup-message-user {
background-color: #f0f0f0;
border-radius: 8px;
padding: 10px;
margin-bottom: 10px;
float: right;
border-bottom-right-radius: 0px;
}
.popup-message-assistant {
padding: 10px;
padding-left: 0px;
padding-right: 0px;
margin-bottom: 10px;
float: left;
width: 100%;
}
@keyframes spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
/* Apply the animation to the icon-loading class */
.icon-loading {
display: inline-block;
animation: spin 1s linear infinite;
}

View File

@@ -46,7 +46,10 @@ class PopupBox extends React.Component<PopupBoxProps, PopupBoxStates> {
<>
<div
className="popup-box-container"
style={{ marginLeft: this.props.isNavLocked ? 150 : 0 }}
style={{
marginLeft: this.props.isNavLocked ? 150 : 0,
height: this.props.menuMode === "assistant" ? "400px" : "300px",
}}
>
{this.props.menuMode === "note" ? (
<PopupNote {...PopupProps} />

View File

@@ -284,3 +284,21 @@ export const officialDictList = [
nativeLang: "Classical Chinese",
},
];
export const sampleQuestion = [
{
mode: "ask",
question: "Summarize this chapter for me",
},
{
mode: "ask",
question: "What are the key points of this chapter",
},
{
mode: "chat",
question: "Recommend me some books from Colleen Hoover",
},
{
mode: "chat",
question: "Explain Stoicism and its principles to me",
},
];

View File

@@ -39,6 +39,25 @@ export const getSummaryStream = async (
);
return result;
};
export const getAnswerStream = async (
text: string,
question: string,
history: any[],
mode: string,
onMessage: (result) => void
) => {
let readerRequest = await getReaderRequest();
let result = await readerRequest.getAnswerFetch(
{
text,
question,
history,
mode,
},
onMessage
);
return result;
};
export const getDictionary = async (word: string, from: string, to: string) => {
let readerRequest = await getReaderRequest();
let result = await readerRequest.getDictionary({ word, from, to });

View File

@@ -10547,10 +10547,10 @@ mammoth@^1.8.0:
underscore "^1.13.1"
xmlbuilder "^10.0.0"
marked@^15.0.3:
version "15.0.3"
resolved "https://registry.yarnpkg.com/marked/-/marked-15.0.3.tgz#f75ae6ca71aeeaea0cfa2c2f120016ac8373aede"
integrity sha512-Ai0cepvl2NHnTcO9jYDtcOEtVBNVYR31XnEA3BndO7f5As1wzpcOceSUM8FDkNLJNIODcLpDTWay/qQhqbuMvg==
marked@^15.0.11:
version "15.0.11"
resolved "https://registry.yarnpkg.com/marked/-/marked-15.0.11.tgz#08a8d12c285e16259e44287b89ce0d871c9d55e8"
integrity sha512-1BEXAU2euRCG3xwgLVT1y0xbJEld1XOrmRJpUwRCcy7rxhSCwMrmEu9LXoPhHSCJG41V7YcQ2mjKRr5BA3ITIA==
matcher@^3.0.0:
version "3.0.0"