aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSophie Zhang <sophie_zhang@brown.edu>2023-02-23 13:00:18 -0500
committerSophie Zhang <sophie_zhang@brown.edu>2023-02-23 13:00:18 -0500
commitfb4304c0e02aef4a0cedabfdc23ef39c4ca0eca8 (patch)
treebe1f4b138f8d36903f23831bb537292a5c7b62a6
parent4475adee0f13d9ec407aff6a47094c7ce808af0c (diff)
text
-rw-r--r--package-lock.json56
-rw-r--r--package.json2
-rw-r--r--src/client/apis/gpt/Summarization.ts18
-rw-r--r--src/client/views/pdf/AnchorMenu.tsx22
-rw-r--r--src/client/views/pdf/GPTPopup.scss45
-rw-r--r--src/client/views/pdf/GPTPopup.tsx15
6 files changed, 137 insertions, 21 deletions
diff --git a/package-lock.json b/package-lock.json
index 6ea3936cd..488b11bd4 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2139,6 +2139,15 @@
"@types/react": "*"
}
},
+ "@types/react-typist": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/@types/react-typist/-/react-typist-2.0.3.tgz",
+ "integrity": "sha512-5eDrikVNJ73qR5XEV+ZNApoYarzYUD5OOPdNwnNY5RtqituZl9haq0OdZUaac2R751QSOe3zDwnqYhBmdFhG7g==",
+ "dev": true,
+ "requires": {
+ "@types/react": "*"
+ }
+ },
"@types/reactcss": {
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/@types/reactcss/-/reactcss-1.2.6.tgz",
@@ -5303,6 +5312,16 @@
"integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=",
"dev": true
},
+ "d": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
+ "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==",
+ "dev": true,
+ "requires": {
+ "es5-ext": "^0.10.50",
+ "type": "^1.0.1"
+ }
+ },
"d3-array": {
"version": "2.12.1",
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz",
@@ -6470,6 +6489,28 @@
"is-symbol": "^1.0.2"
}
},
+ "es5-ext": {
+ "version": "0.10.62",
+ "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz",
+ "integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==",
+ "dev": true,
+ "requires": {
+ "es6-iterator": "^2.0.3",
+ "es6-symbol": "^3.1.3",
+ "next-tick": "^1.1.0"
+ }
+ },
+ "es6-iterator": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
+ "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==",
+ "dev": true,
+ "requires": {
+ "d": "1",
+ "es5-ext": "^0.10.35",
+ "es6-symbol": "^3.1.1"
+ }
+ },
"es6-promise": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.2.1.tgz",
@@ -6481,6 +6522,7 @@
"integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==",
"dev": true,
"requires": {
+ "d": "^1.0.1",
"ext": "^1.1.2"
}
},
@@ -18561,6 +18603,14 @@
}
}
},
+ "react-typist": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/react-typist/-/react-typist-2.0.5.tgz",
+ "integrity": "sha512-iZCkeqeegO0TlkTMiH2JD1tvMtY9RrXkRylnAI6m8aCVAUUwNzoWTVF7CKLij6THeOMcUDCznLDDvNp55s+YZA==",
+ "requires": {
+ "prop-types": "^15.5.10"
+ }
+ },
"react-use-measure": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/react-use-measure/-/react-use-measure-2.1.1.tgz",
@@ -21393,6 +21443,12 @@
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
"integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q="
},
+ "type": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz",
+ "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==",
+ "dev": true
+ },
"type-check": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
diff --git a/package.json b/package.json
index 480657d5f..2b415f256 100644
--- a/package.json
+++ b/package.json
@@ -77,6 +77,7 @@
"@types/react-select": "^3.1.2",
"@types/react-table": "^6.8.9",
"@types/react-transition-group": "^4.4.5",
+ "@types/react-typist": "^2.0.3",
"@types/request": "^2.48.8",
"@types/request-promise": "^4.1.48",
"@types/rimraf": "^2.0.5",
@@ -286,6 +287,7 @@
"react-select": "^3.2.0",
"react-table": "^6.11.5",
"react-transition-group": "^4.4.2",
+ "react-typist": "^2.0.5",
"readline": "^1.3.0",
"rehype-raw": "^6.1.1",
"remark-gfm": "^3.0.1",
diff --git a/src/client/apis/gpt/Summarization.ts b/src/client/apis/gpt/Summarization.ts
index 3706c7a5b..931e0e48f 100644
--- a/src/client/apis/gpt/Summarization.ts
+++ b/src/client/apis/gpt/Summarization.ts
@@ -1,13 +1,14 @@
import { Configuration, OpenAIApi } from 'openai';
const gptSummarize = async (text: string) => {
+ text += '.';
try {
const configuration = new Configuration({
apiKey: process.env.OPENAI_KEY,
});
const openai = new OpenAIApi(configuration);
const response = await openai.createCompletion({
- model: 'text-davinci-003',
+ model: 'text-curie-001',
max_tokens: 256,
temperature: 0.7,
prompt: `Summarize this text in one sentence: ${text}`,
@@ -19,17 +20,4 @@ const gptSummarize = async (text: string) => {
}
};
-// Summarizing with the MeaningCloud API
-const fetchSummary = async (text: string, numSentences?: number) => {
- const key = '0b41c071f838e573847f477e8f69e9d9';
- const queryURL = '';
- const sentences = numSentences ? numSentences : 3;
- const URL = `https://api.meaningcloud.com/summarization-1.0?key=${key}&txt=${text}&sentences=${sentences}`;
-
- const res = await fetch(URL);
- const data = await res.json();
- console.log(data.summary);
- return data.summary;
-};
-
-export { fetchSummary, gptSummarize };
+export { gptSummarize };
diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx
index 63c8f9145..6bcfbe4c2 100644
--- a/src/client/views/pdf/AnchorMenu.tsx
+++ b/src/client/views/pdf/AnchorMenu.tsx
@@ -47,6 +47,7 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
// GPT additions (flow 2)
@observable private summarizedText: string = '';
+ @observable private loadingSummary: boolean = false;
@observable private showGPTPopup: boolean = false;
@action
setGPTPopupVis = (vis: boolean) => {
@@ -56,6 +57,10 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
setSummarizedText = (txt: string) => {
this.summarizedText = txt;
};
+ @action
+ setLoading = (loading: boolean) => {
+ this.loadingSummary = loading;
+ };
private selectedText: string = '';
setSelectedText = (txt: string) => {
@@ -98,7 +103,13 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
componentDidMount() {
this._disposer2 = reaction(
() => this._opacity,
- opacity => !opacity && (this._showLinkPopup = false),
+ opacity => {
+ if (!opacity) {
+ this._showLinkPopup = false;
+ this.setGPTPopupVis(false);
+ this.setSummarizedText('');
+ }
+ },
{ fireImmediately: true }
);
this._disposer = reaction(
@@ -106,6 +117,7 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
selected => {
this._showLinkPopup = false;
this.setGPTPopupVis(false);
+ this.setSummarizedText('');
AnchorMenu.Instance.fadeOut(true);
}
);
@@ -117,12 +129,14 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
invokeGPT = async (e: React.PointerEvent) => {
this.setGPTPopupVis(true);
+ this.setLoading(true);
const res = await gptSummarize(this.selectedText);
if (res) {
this.setSummarizedText(res);
} else {
this.setSummarizedText('Something went wrong.');
}
+ this.setLoading(false);
};
pointerDown = (e: React.PointerEvent) => {
@@ -229,12 +243,12 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
<FontAwesomeIcon icon="comment-alt" size="lg" />
</button>
</Tooltip>
- <Tooltip key="gpt" title={<div className="dash-tooltip">Summarize with GPT-3</div>}>
- <button className="antimodeMenu-button annotate" onPointerDown={this.getGPTSummary} style={{ cursor: 'grab' }}>
+ <Tooltip key="gpt" title={<div className="dash-tooltip">Summarize with AI</div>}>
+ <button className="antimodeMenu-button annotate" onPointerDown={this.invokeGPT} style={{ cursor: 'grab' }}>
<FontAwesomeIcon icon="comment-dots" size="lg" />
</button>
</Tooltip>
- <GPTPopup key="gptpopup" visible={this.showGPTPopup} text={this.summarizedText} />
+ <GPTPopup key="gptpopup" visible={this.showGPTPopup} text={this.summarizedText} loadingSummary={this.loadingSummary} />
{AnchorMenu.Instance.OnAudio === unimplementedFunction ? null : (
<Tooltip key="annoaudiotate" title={<div className="dash-tooltip">Click to Record Annotation</div>}>
<button className="antimodeMenu-button annotate" onPointerDown={this.audioDown} style={{ cursor: 'grab' }}>
diff --git a/src/client/views/pdf/GPTPopup.scss b/src/client/views/pdf/GPTPopup.scss
index 6f2e39b7e..9605cfd07 100644
--- a/src/client/views/pdf/GPTPopup.scss
+++ b/src/client/views/pdf/GPTPopup.scss
@@ -1,9 +1,52 @@
+$textgrey: #707070;
+$bordergrey: #d3d3d3;
+
.summary-box {
background-color: #ffffff;
+ box-shadow: 0 2px 5px #7474748d;
+ color: $textgrey;
position: absolute;
- top: 0;
+ bottom: 40px;
width: 200px;
height: 200px;
+ border-radius: 15px;
padding: 20px;
overflow: auto;
+
+ .summary-heading {
+ display: flex;
+ align-items: center;
+ border-bottom: 1px solid $bordergrey;
+ margin-bottom: 10px;
+ padding-bottom: 5px;
+
+ .summary-text {
+ font-size: 12px;
+ font-weight: 500;
+ letter-spacing: 1px;
+ margin: 0;
+ padding-right: 10px;
+ }
+ }
+}
+
+// Typist CSS
+.Typist .Cursor {
+ display: inline-block;
+}
+.Typist .Cursor--blinking {
+ opacity: 1;
+ animation: blink 1s linear infinite;
+}
+
+@keyframes blink {
+ 0% {
+ opacity: 1;
+ }
+ 50% {
+ opacity: 0;
+ }
+ 100% {
+ opacity: 1;
+ }
}
diff --git a/src/client/views/pdf/GPTPopup.tsx b/src/client/views/pdf/GPTPopup.tsx
index 110351126..76f3c8187 100644
--- a/src/client/views/pdf/GPTPopup.tsx
+++ b/src/client/views/pdf/GPTPopup.tsx
@@ -1,10 +1,13 @@
import { observer } from 'mobx-react';
import React = require('react');
+import ReactLoading from 'react-loading';
+import Typist from 'react-typist';
import './GPTPopup.scss';
interface GPTPopupProps {
visible: boolean;
text: string;
+ loadingSummary: boolean;
}
@observer
@@ -12,7 +15,17 @@ export class GPTPopup extends React.Component<GPTPopupProps> {
render() {
return (
<div className="summary-box" style={{ display: this.props.visible ? 'block' : 'none' }}>
- {`Summary: ${this.props.text}`}
+ <div className="summary-heading">
+ <label className="summary-text">SUMMARY</label>
+ {this.props.loadingSummary ? <ReactLoading type="spin" color="#bcbcbc" width={14} height={14} /> : <></>}
+ </div>
+ {!this.props.loadingSummary ? (
+ <Typist key={this.props.text} avgTypingDelay={20} cursor={{ hideWhenDone: true }}>
+ {this.props.text}
+ </Typist>
+ ) : (
+ <></>
+ )}
</div>
);
}