aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authoraidahosa1 <aisosa_idahosa@brown.edu>2024-06-14 12:42:58 -0400
committeraidahosa1 <aisosa_idahosa@brown.edu>2024-06-14 12:42:58 -0400
commit585e6ece3c2bee7b5a747ec571c5c2af1861d324 (patch)
tree2e133dfbedb8b7bf8b24062b5058e4fd339988e2 /src
parentcca4b5bbc32ccfaafbaba9306545eaddc6953954 (diff)
starting dragging ??????
Diffstat (limited to 'src')
-rw-r--r--src/client/apis/gpt/GPT.ts2
-rw-r--r--src/client/views/collections/CollectionCardDeckView.scss39
-rw-r--r--src/client/views/collections/CollectionCardDeckView.tsx79
-rw-r--r--src/client/views/global/globalScripts.ts7
-rw-r--r--src/client/views/pdf/GPTPopup/GPTPopup.scss1
-rw-r--r--src/client/views/pdf/GPTPopup/GPTPopup.tsx151
6 files changed, 211 insertions, 68 deletions
diff --git a/src/client/apis/gpt/GPT.ts b/src/client/apis/gpt/GPT.ts
index 5dc818db8..585398204 100644
--- a/src/client/apis/gpt/GPT.ts
+++ b/src/client/apis/gpt/GPT.ts
@@ -43,7 +43,7 @@ const callTypeMap: { [type: string]: GPTCallOpts } = {
model: 'gpt-4o',
maxTokens: 2048,
temp: 0.5,
- prompt: "I'm going to give you a list of descriptions. Each one is seperated by ====== on either side. They will vary in length, so make sure to only seperate when you see ======. Sort them into lists by shared content. MAKE SURE EACH DESCRIPTOR IS IN ONLY ONE LIST. Generate only the list with each list seperated by ====== with the elements seperated by ~~~~~~. Try to do around 4 groups, but a little more or less is ok.",
+ prompt: "I'm going to give you a list of descriptions. Each one is separated by `======` on either side. Descriptions will vary in length, so make sure to only separate when you see `======`. Sort them into lists by shared content. Make sure each description is in only one list. Each list should be separated by `======` with the elements within it separated by `~~~~~~`. Immediately afterward, surrounded by `------` on BOTH SIDES, provide some insight into your reasoning for the way you sorted. It is VERY important that you format it exactly as described, ensuring the proper number of `=` `~` and `-` (6 of each) and no commas.Try to create around 4 groups, but a little more or less is ok. Also, I may provide some more insight after this colon:"
},
describe: { model: 'gpt-4-vision-preview', maxTokens: 2048, temp: 0, prompt: 'Describe these images in 3-5 words' },
chatcard: { model: 'gpt-4-turbo', maxTokens: 512, temp: 0.5, prompt: 'Answer the following question as a short flashcard response. Do not include a label.' },
diff --git a/src/client/views/collections/CollectionCardDeckView.scss b/src/client/views/collections/CollectionCardDeckView.scss
index 8a721a626..5f7d1b901 100644
--- a/src/client/views/collections/CollectionCardDeckView.scss
+++ b/src/client/views/collections/CollectionCardDeckView.scss
@@ -26,23 +26,41 @@
display: flex;
padding: 3px;
// width: 300px;
+ // height:100px;
+ pointer-events: none; /* This ensures the container does not capture hover events */
+
background-color: rgb(218, 218, 218); /* Background color of the container */
border-radius: 50px; /* Rounds the corners of the container */
transform: translateY(25px);
// box-shadow: 0 4px 8px rgba(0,0,0,0.1); /* Optional: Adds shadow for depth */
align-items: center; /* Centers buttons vertically */
justify-content: start; /* Centers buttons horizontally */
+
+ button {
+ pointer-events: auto; /* Re-enable pointer events for the buttons */
+
+ width: 70px;
+ height: 70px;
+ border-radius: 50%;
+ background-color: $dark-gray;
+ // border-color: $medium-blue;
+ margin: 5px; // transform: translateY(-50px);
+ background-color: transparent;
+ }
}
-button {
- width: 35px;
- height: 35px;
- border-radius: 50%;
- background-color: $dark-gray;
- // border-color: $medium-blue;
- margin: 5px; // transform: translateY(-50px);
+.no-card-span{
+ position: relative;
+ width: fit-content;
+ text-align: center;
+ font-size: 65px;
+
+
+
}
+
+
// button:hover {
// transform: translateY(-50px);
// }
@@ -74,6 +92,13 @@ button {
flex-direction: column;
}
+// .card-item:hover {
+// box-shadow: 0 20px 20px $medium-blue;
+// transform: scale(1.05);
+
+
+// }
+
.card-item-inactive {
opacity: 0.5;
}
diff --git a/src/client/views/collections/CollectionCardDeckView.tsx b/src/client/views/collections/CollectionCardDeckView.tsx
index d68fb57f8..7ea345e10 100644
--- a/src/client/views/collections/CollectionCardDeckView.tsx
+++ b/src/client/views/collections/CollectionCardDeckView.tsx
@@ -20,6 +20,13 @@ import { GPTPopup, GPTPopupMode } from '../pdf/GPTPopup/GPTPopup';
import './CollectionCardDeckView.scss';
import { CollectionSubView } from './CollectionSubView';
import { FieldsDropdown } from '../FieldsDropdown';
+import { Button, IconButton } from 'browndash-components';
+import { faStar } from '@fortawesome/free-solid-svg-icons';
+import { FaStar, FaHeart, FaRobot, FaCloud } from 'react-icons/fa';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { SettingsManager } from '../../util/SettingsManager';
+import { Tooltip } from '@mui/material';
+
enum cardSortings {
Time = 'time',
@@ -72,8 +79,24 @@ export class CollectionCardView extends CollectionSubView() {
constructor(props: any) {
super(props);
makeObservable(this);
+ this.setRegenerateCallback();
+
+ }
+
+ setRegenerateCallback() {
+ GPTPopup.Instance.setRegenerateCallback(this.childPairStringListAndUpdateSortDesc);
}
+ @action
+ childPairStringListAndUpdateSortDesc = async () => {
+ const sortDesc = await this.childPairStringList(); // Await the promise to get the string result
+ GPTPopup.Instance.setSortDesc(sortDesc.join());
+ GPTPopup.Instance.onSortComplete = (sortResult: string) => this.processGptOutput(sortResult);
+
+ };
+
+
+
componentDidMount(): void {
this._disposers.sort = reaction(
() => ({ cardSort: this.cardSort, field: this.cardSort_customField }),
@@ -331,7 +354,8 @@ export class CollectionCardView extends CollectionSubView() {
*/
calculateTranslateY = (isHovered: boolean, isSelected: boolean, realIndex: number, amCards: number, calcRowIndex: number) => {
if (isSelected || amCards == 1) return 50 * this.fitContentScale;
- const trans = isHovered ? this.translateHover(realIndex) : 0;
+ // const trans = isHovered ? this.translateHover(realIndex) : 0;
+ const trans = 0
return trans + this.translateY(amCards, calcRowIndex, realIndex);
};
@@ -433,26 +457,50 @@ export class CollectionCardView extends CollectionSubView() {
if (cardSort !== cardSortings.Custom) return '';
const amButtons = Math.max(4, this.childDocs?.reduce((set, d) => this.cardSort_customField && set.add(NumCast(d[this.cardSort_customField])), new Set<number>()).size ?? 0);
const activeButtonIndex = CollectionCardView.getButtonGroup(this.cardSort_customField, doc);
- const totalWidth = amButtons * 35 + amButtons * 2 * 5 + 6;
+ const totalWidth = amButtons * 72 + amButtons * 2 * 5 + 6;
return (
- <div className="card-button-container" style={{ width: `${totalWidth}px` }}>
+ <div className="card-button-container" style={{ width: `${totalWidth}px`, fontSize: '50px' }} >
{numberRange(amButtons).map(i => (
// eslint-disable-next-line jsx-a11y/control-has-associated-label
- <button
- key={i}
- type="button"
- style={{ backgroundColor: activeButtonIndex === i ? '#4476f7' : '#323232' }} //
- onClick={() => this.toggleButton(i, doc)}
- />
+ <Tooltip title={<div key = {i} className="dash-tooltip">Click to add/ remove this card from group {i +1}</div>}>
+
+ <button key={i} type="button"
+ // style={{ backgroundColor: activeButtonIndex === i ? '#4476f7' : '#323232' }}
+ onClick={() => this.toggleButton(i, doc)}>
+ {this.getButtonIcons(activeButtonIndex === i)}
+ </button>
+
+ </Tooltip>
+
))}
</div>
);
};
+
+ getButtonIcons = (isActive: boolean) => {
+ switch (this.cardSort_customField) {
+ case 'like':
+ return <FontAwesomeIcon icon = 'heart' size= 'lg' style = {{color: isActive ? '#4476f7' : '#323232'}}/>;
+ case 'chat':
+ return <FontAwesomeIcon icon = 'robot' size= 'lg' style = {{color: isActive ? '#4476f7' : '#323232'}}/>;
+ case 'idea':
+ return <FontAwesomeIcon icon = 'cloud' size= 'lg' style = {{color: isActive ? '#4476f7' : '#323232'}}/>;
+ default:
+ return <FontAwesomeIcon icon = 'star' size= 'lg' style = {{color: isActive ? '#4476f7' : '#323232'}}/>;
+ }
+ };
/**
* Actually renders all the cards
*/
renderCards = () => {
const anySelected = this.childDocs.some(doc => DocumentView.SelectedDocs().includes(doc));
+ const isEmpty = this.childDocsWithoutLinks.length === 0;
+
+ if (isEmpty) {
+ return <span className = 'no-card-span' style= {{width: `100%`}}>
+ Sorry ! There are no cards in this group</span>
+ }
+
// Map sorted documents to their rendered components
return this.sortedDocs.map((doc, index) => {
const realIndex = this.sortedDocs.filter(sortDoc => !DocumentView.SelectedDocs().includes(sortDoc)).indexOf(doc);
@@ -486,23 +534,27 @@ export class CollectionCardView extends CollectionSubView() {
}}
style={{
width: this.panelWidth(),
- height: 'max-content', // this.panelHeight(childPair.layout)(),
+ height: 'max-content',
transform: `translateY(${this.calculateTranslateY(this._hoveredNodeIndex === index, isSelected, realIndex, amCards, calcRowIndex)}px)
translateX(${isSelected ? this.translateSelected(calcRowIndex) : this.translateOverflowX(realIndex, amCards)}px)
rotate(${!isSelected ? this.rotate(amCards, calcRowIndex) : 0}deg)
- scale(${isSelected ? 1.25 : 1})`,
- }}
+ scale(${isSelected ? 1.25 : this._hoveredNodeIndex === index ? 1.05 : 1})`,
+ }}
onMouseEnter={() => this.setHoveredNodeIndex(index)}>
{this.displayDoc(doc, childScreenToLocal)}
{this.renderButtons(doc, this.cardSort)}
</div>
);
- });
+ }
+
+
+ );
};
render() {
+
return (
<div
className="collectionCardView-outer"
@@ -533,4 +585,5 @@ export class CollectionCardView extends CollectionSubView() {
</div>
);
}
+
}
diff --git a/src/client/views/global/globalScripts.ts b/src/client/views/global/globalScripts.ts
index 549cb5c8f..cef913837 100644
--- a/src/client/views/global/globalScripts.ts
+++ b/src/client/views/global/globalScripts.ts
@@ -352,24 +352,23 @@ ScriptingGlobals.add(function setCardSort(value?: any, checkResult?: boolean) {
switch(value){
case 'Custom 1':
doc.cardSort_customField = "like";
- doc.cardSort_visibleSortGroups = new List<number>();
break;
case 'Custom 2':
doc.cardSort_customField = "star";
- doc.cardSort_visibleSortGroups = new List<number>();
break;
case 'Custom 3':
doc.cardSort_customField = "idea";
- doc.cardSort_visibleSortGroups = new List<number>();
break;
case 'Chat GPT':
doc.cardSort = "custom";
doc.cardSort_customField = "chat";
- doc.cardSort_visibleSortGroups = new List<number>();
break;
default:
break;
}
+
+ doc.cardSort_visibleSortGroups = new List<number>();
+
}
// const batch = map.get(attr)?.waitForRender ? UndoManager.StartBatch('set freeform attribute') : { end: () => {} };
diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.scss b/src/client/views/pdf/GPTPopup/GPTPopup.scss
index 6d8793f82..042b1dea5 100644
--- a/src/client/views/pdf/GPTPopup/GPTPopup.scss
+++ b/src/client/views/pdf/GPTPopup/GPTPopup.scss
@@ -84,6 +84,7 @@ $highlightedText: #82e0ff;
font-size: 9px;
padding: 10px;
color: #ffffff;
+ width: 100%;
background-color: $button;
border-radius: 5px;
}
diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.tsx b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
index c0f17ba4e..468922532 100644
--- a/src/client/views/pdf/GPTPopup/GPTPopup.tsx
+++ b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
@@ -20,6 +20,7 @@ import './GPTPopup.scss';
import { SettingsManager } from '../../../util/SettingsManager';
import { SnappingManager } from '../../../util/SnappingManager';
+
export enum GPTPopupMode {
SUMMARY,
EDIT,
@@ -149,6 +150,28 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> {
this.cardsDoneLoading = done;
}
+ @observable sortRespText: string = ''
+
+ @action setSortRespText(resp: string) {
+ this.sortRespText = resp
+ }
+
+
+ @observable chatSortPrompt: string = ""
+
+ sortPromptChanged = action((e: React.ChangeEvent<HTMLInputElement>) => {
+ this.chatSortPrompt = e.target.value;
+ });
+
+ @observable private regenerateCallback: (() => Promise<void>) | null = null;
+
+ @action public setRegenerateCallback(callback: () => Promise<void>) {
+ this.regenerateCallback = callback;
+ }
+
+
+
+
public addDoc: (doc: Doc | Doc[], sidebarKey?: string | undefined) => boolean = () => false;
public createFilteredDoc: (axes?: any) => boolean = () => false;
public addToCollection: ((doc: Doc | Doc[], annotationKey?: string | undefined) => boolean) | undefined;
@@ -160,21 +183,34 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> {
this.setLoading(true);
this.setSortDone(false);
+ if (this.regenerateCallback) {
+ await this.regenerateCallback();
+ }
+
try {
- const res = await gptAPICall(this.sortDesc, GPTCallType.SORT);
+ const res = await gptAPICall(this.sortDesc, GPTCallType.SORT, this.chatSortPrompt);
// Trigger the callback with the result
if (this.onSortComplete) {
this.onSortComplete(res || 'Something went wrong :(');
+
+ // Extract explanation surrounded by ------ at the top or both at the top and bottom
+ const explanationMatch = res.match(/------\s*([\s\S]*?)\s*(?:------|$)/) || [];
+ const explanation = explanationMatch[1] ? explanationMatch[1].trim() : 'No explanation found';
+
+ // Set the extracted explanation to sortRespText
+ this.setSortRespText(explanation);
+
console.log(res);
}
} catch (err) {
console.error(err);
}
-
+
this.setLoading(false);
this.setSortDone(true);
};
-
+
+
/**
* Generates a Dalle image and uploads it to the server.
*/
@@ -315,49 +351,75 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> {
};
sortBox = () => (
- <>
- <div>
+ <>
+ <div>
{this.heading('SORTING')}
- <>
- {!this.cardsDoneLoading || this.loading ? (
- <div className="content-wrapper">
- <div className="loading-spinner">
- <ReactLoading type="spin" color={StrCast(Doc.UserDoc().userVariantColor)} height={30} width={30} />
- {this.loading ? <span>Loading...</span> : <span>Reading Cards...</span>}
- </div>
+ <>
+ {!this.cardsDoneLoading || this.loading ? (
+ <div className="content-wrapper">
+ <div className="loading-spinner">
+ <ReactLoading type="spin" color={StrCast(Doc.UserDoc().userVariantColor)} height={30} width={30} />
+ {this.loading ? <span>Loading...</span> : <span>Reading Cards...</span>}
+ </div>
+ </div>
+ ) : (
+ !this.sortDone && (
+ <>
+ <div className="btns-wrapper-gpt">
+ <input
+ className="searchBox-input"
+ defaultValue=""
+ autoComplete="off"
+ onChange={this.sortPromptChanged}
+ onKeyDown={e => {
+ if (e.key === 'Enter') {
+ this.generateSort();
+ }
+ e.stopPropagation();
+ }}
+ type="text"
+ placeholder="How do you want to sort your cards ?"
+ id="search-input"
+ style={{ width: '100%' }}
+ />
</div>
- ) : (
- !this.sortDone && (
- <div className="btns-wrapper-gpt">
- <Button
- tooltip="Have ChatGPT sort your cards for you!"
- text="Sort!"
- onClick={this.generateSort}
- color={StrCast(Doc.UserDoc().userVariantColor)}
- type={Type.TERT}
- style={{
- width: '90%',
- textAlign: 'center',
- color: '#ffffff',
- fontSize: '16px',
- }}
- />
- </div>
- )
- )}
-
- {this.sortDone && (
- <div>
- <div className="content-wrapper">
- <p>{this.text === 'Something went wrong :(' ? 'Something went wrong :(' : 'Sorting done! Feel free to move things around / regenerate :) !'}</p>
- <IconButton tooltip="Generate Again" onClick={() => this.setSortDone(false)} icon={<FontAwesomeIcon icon="redo-alt" size="lg" />} color={StrCast(Doc.UserDoc().userVariantColor)} />
- </div>
+ <div className="btns-wrapper-gpt">
+ <Button
+ tooltip="Have ChatGPT sort your cards for you!"
+ text="Sort!"
+ onClick={this.generateSort}
+ color={StrCast(Doc.UserDoc().userVariantColor)}
+ type={Type.TERT}
+ style={{
+ width: '90%',
+ textAlign: 'center',
+ color: '#ffffff',
+ fontSize: '16px',
+ }}
+ />
</div>
- )}
- </>
- </div>
- </>
- );
+ </>
+ )
+ )}
+
+ {this.sortDone && (
+ <div>
+ <div className="content-wrapper">
+ <p>{this.text === 'Something went wrong :(' ? 'Something went wrong :(' : `${this.sortRespText}`}</p>
+ <IconButton
+ tooltip="Generate Again"
+ onClick={() => this.setSortDone(false)}
+ icon={<FontAwesomeIcon icon="redo-alt" size="lg" />}
+ color={StrCast(Doc.UserDoc().userVariantColor)}
+ />
+ </div>
+ </div>
+ )}
+ </>
+ </div>
+ </>
+);
+
imageBox = () => (
<div style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
{this.heading('GENERATED IMAGE')}
@@ -509,9 +571,12 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> {
render() {
return (
+
<div className="summary-box" style={{ display: this.visible ? 'flex' : 'none' }}>
{this.mode === GPTPopupMode.SUMMARY ? this.summaryBox() : this.mode === GPTPopupMode.DATA ? this.dataAnalysisBox() : this.mode === GPTPopupMode.IMAGE ? this.imageBox() : this.mode === GPTPopupMode.SORT ? this.sortBox() : null}
</div>
+
+
);
}
}