From aa3dc83bdd723db2597dc36fe09ae288ce3641da Mon Sep 17 00:00:00 2001 From: Sophie Zhang Date: Sun, 27 Aug 2023 13:22:54 -0400 Subject: validating issues --- src/client/views/pdf/GPTPopup/GPTPopup.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src/client/views/pdf/GPTPopup') diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.tsx b/src/client/views/pdf/GPTPopup/GPTPopup.tsx index 8bf626d73..9b754588a 100644 --- a/src/client/views/pdf/GPTPopup/GPTPopup.tsx +++ b/src/client/views/pdf/GPTPopup/GPTPopup.tsx @@ -88,7 +88,6 @@ export class GPTPopup extends React.Component { this.sidebarId = id; }; - @observable private imgTargetDoc: Doc | undefined; @action @@ -119,13 +118,15 @@ export class GPTPopup extends React.Component { try { let image_urls = await gptImageCall(this.imgDesc); if (image_urls && image_urls[0]) { + // need to fix this const [result] = await Networking.PostToServer('/uploadRemoteImage', { sources: [image_urls[0]] }); + console.log('Result', result); + console.log('Client', result.accessPaths.agnostic.client); const source = Utils.prepend(result.accessPaths.agnostic.client); this.setImgUrls([[image_urls[0], source]]); } } catch (err) { console.log(err); - return ''; } GPTPopup.Instance.setLoading(false); }; -- cgit v1.2.3-70-g09d2 From 8a0ae4e42d217a222d6a127ec640d5f1113832a0 Mon Sep 17 00:00:00 2001 From: Sophie Zhang Date: Mon, 16 Oct 2023 17:08:24 -0400 Subject: text box --- src/client/apis/gpt/GPT.ts | 44 +++++++++++++---- src/client/views/PropertiesView.tsx | 2 +- src/client/views/nodes/trails/PresBox.scss | 24 +++++++++ src/client/views/nodes/trails/PresBox.tsx | 63 ++++++++++++++++++++++-- src/client/views/nodes/trails/PresElementBox.tsx | 35 +++++++++++++ src/client/views/pdf/GPTPopup/GPTPopup.scss | 4 +- 6 files changed, 157 insertions(+), 15 deletions(-) (limited to 'src/client/views/pdf/GPTPopup') diff --git a/src/client/apis/gpt/GPT.ts b/src/client/apis/gpt/GPT.ts index 6bde7989b..d7e7f07fa 100644 --- a/src/client/apis/gpt/GPT.ts +++ b/src/client/apis/gpt/GPT.ts @@ -19,6 +19,32 @@ const callTypeMap: { [type: string]: GPTCallOpts } = { completion: { model: 'text-davinci-003', maxTokens: 256, temp: 0.5, prompt: '' }, }; +const configuration = new Configuration({ + apiKey: process.env.OPENAI_KEY, +}); +const openai = new OpenAIApi(configuration); + +const gptTrailSlideCustomization = async (inputText: string) => { + const prompt = + 'We are adding customization to a slide in a presentation. Given a natural language input, translate it into a json with the required fields: [title, presentation_transition, presentation_effect, and config_zoom]. title is the title/name of the slide. presentation_transition is a number in milliseconds for how long it should take to transition to a slide. presentation_effect is an effect applied to the slide when we transition to it. Its only possible values are: [None, Fade in, Flip, Rotate, Bounce, Roll]. config zoom is a number from 0 to 1.0 indicating the percentage we should be zooming into the slide.If the input does not contain info a specific key, please set their value to null. Please only return the json with these keys and their values.'; + + try { + const response = await openai.createChatCompletion({ + model: 'gpt-3.5-turbo', + messages: [ + { role: 'system', content: prompt }, + { role: 'user', content: inputText }, + ], + temperature: 0.1, + max_tokens: 1000, + }); + return response.data.choices[0].message?.content; + } catch (err) { + console.log(err); + return 'Error connecting with API.'; + } +}; + /** * Calls the OpenAI API. * @@ -29,10 +55,10 @@ const gptAPICall = async (inputText: string, callType: GPTCallType) => { if (callType === GPTCallType.SUMMARY) inputText += '.'; const opts: GPTCallOpts = callTypeMap[callType]; try { - const configuration = new Configuration({ - apiKey: process.env.OPENAI_KEY, - }); - const openai = new OpenAIApi(configuration); + // const configuration = new Configuration({ + // apiKey: process.env.OPENAI_KEY, + // }); + // const openai = new OpenAIApi(configuration); const response = await openai.createCompletion({ model: opts.model, max_tokens: opts.maxTokens, @@ -48,10 +74,10 @@ const gptAPICall = async (inputText: string, callType: GPTCallType) => { const gptImageCall = async (prompt: string, n?: number) => { try { - const configuration = new Configuration({ - apiKey: process.env.OPENAI_KEY, - }); - const openai = new OpenAIApi(configuration); + // const configuration = new Configuration({ + // apiKey: process.env.OPENAI_KEY, + // }); + // const openai = new OpenAIApi(configuration); const response = await openai.createImage({ prompt: prompt, n: n ?? 1, @@ -65,4 +91,4 @@ const gptImageCall = async (prompt: string, n?: number) => { } }; -export { gptAPICall, gptImageCall, GPTCallType }; +export { gptAPICall, gptImageCall, gptTrailSlideCustomization, GPTCallType }; diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index cb45ece42..984906ed0 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -1266,7 +1266,7 @@ export class PropertiesView extends React.Component { Doc.GetProto(LinkManager.currentLink).link_description = value; if (LinkManager.currentLink.show_description === undefined) { - LinkManager.currentLink['show_description'] = !LinkManager.currentLink['show_description']; + LinkManager.currentLink.show_description = !LinkManager.currentLink.show_description; } } }); diff --git a/src/client/views/nodes/trails/PresBox.scss b/src/client/views/nodes/trails/PresBox.scss index 0b51813a5..ac0d58368 100644 --- a/src/client/views/nodes/trails/PresBox.scss +++ b/src/client/views/nodes/trails/PresBox.scss @@ -15,6 +15,30 @@ //overflow: hidden; transition: 0.7s opacity ease; + .presBox-chatbox { + position: fixed; + bottom: 8px; + right: 8px; + width: 300px; + min-height: 100px; + border-radius: 16px; + padding: 16px; + gap: 8px; + z-index: 999; + display: flex; + flex-direction: column; + justify-content: space-between; + background-color: #ffffff; + box-shadow: 0 2px 5px #7474748d; + + input { + resize: vertical; + // min-height: 125px; + border: none; + outline: none; + } + } + .presBox-listCont { position: relative; height: calc(100% - 67px); diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index 383b400c8..ebf9c211c 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -37,7 +37,9 @@ import { FieldView, FieldViewProps } from '../FieldView'; import { ScriptingBox } from '../ScriptingBox'; import './PresBox.scss'; import { PresEffect, PresEffectDirection, PresMovement, PresStatus } from './PresEnums'; -const { Howl } = require('howler'); +import { IconButton, Type } from 'browndash-components'; +import { AiOutlineSend } from 'react-icons/ai'; +import { gptTrailSlideCustomization } from '../../../apis/gpt/GPT'; export interface pinDataTypes { scrollable?: boolean; @@ -105,7 +107,24 @@ export class PresBox extends ViewBoxBaseComponent() { @observable _treeViewMap: Map = new Map(); @observable _presKeyEvents: boolean = false; @observable _forceKeyEvents: boolean = false; - @computed get isTreeOrStack() { + + // GPT + @observable chatActive: boolean = false; + @observable chatInput: string = ''; + public slideToModify: Doc | null = null; + + @action + setChatInput = (input: string) => { + this.chatInput = input; + }; + + @action + public setChatActive = (active: boolean) => { + this.chatActive = active; + }; + + @computed + get isTreeOrStack() { return [CollectionViewType.Tree, CollectionViewType.Stacking].includes(StrCast(this.layoutDoc._type_collection) as any); } @computed get isTree() { @@ -241,6 +260,28 @@ export class PresBox extends ViewBoxBaseComponent() { } }; + @action + customizeWithGPT = async (input: string) => { + // const testInput = 'change title to Customized Slide, transition for 2.3s with fade in effect'; + if (!this.slideToModify) return; + + try { + const res = await gptTrailSlideCustomization(input); + if (typeof res === 'string') { + const resObj = JSON.parse(res); + console.log('Result ', resObj); + // this.activeItem + for (let key in resObj) { + if (resObj[key]) { + this.slideToModify[key] = resObj[key]; + } + } + } + } catch (err) { + console.error(err); + } + }; + //TODO: al: it seems currently that tempMedia doesn't stop onslidechange after clicking the button; the time the tempmedia stop depends on the start & end time // TODO: to handle child slides (entering into subtrail and exiting), also the next() and back() functions // No more frames in current doc and next slide is defined, therefore move to next slide @@ -2661,7 +2702,6 @@ export class PresBox extends ViewBoxBaseComponent() { /> ) : null} - {/* { // if the document type is a presentation, then the collection stacking view has a "+ new slide" button at the bottom of the stack {'Click on document to pin to presentaiton or make a marquee selection to pin your desired view'}}> @@ -2671,6 +2711,23 @@ export class PresBox extends ViewBoxBaseComponent() { } */} + {/* presbox chatbox */} + {this.chatActive && ( +
+ this.setChatInput(e.target.value)} /> +
+ } + onClick={() => { + this.customizeWithGPT(this.chatInput); + }} + /> +
+
+ )} ); } diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx index 82ed9e8d5..534a0f2aa 100644 --- a/src/client/views/nodes/trails/PresElementBox.tsx +++ b/src/client/views/nodes/trails/PresElementBox.tsx @@ -28,6 +28,7 @@ import React = require('react'); import { TreeView } from '../../collections/TreeView'; import { BranchingTrailManager } from '../../../util/BranchingTrailManager'; import { MultiToggle, Type } from 'browndash-components'; +import { gptTrailSlideCustomization } from '../../../apis/gpt/GPT'; /** * This class models the view a document added to presentation will have in the presentation. * It involves some functionality for its buttons and options. @@ -434,6 +435,27 @@ export class PresElementBox extends ViewBoxBaseComponent() { return width; } + // GPT + + @action + customizeWithGPT = async (input: string) => { + const testInput = 'change title to Customized Slide, transition for 2.3s with fade in effect'; + + try { + const res = await gptTrailSlideCustomization(testInput); + if (typeof res === 'string') { + const resObj = JSON.parse(res); + console.log('Result ', resObj); + + for (let key in resObj) { + this.rootDoc[key] = resObj[key]; + } + } + } catch (err) { + console.error(err); + } + }; + @computed get presButtons() { const presBox = this.presBox; const presBoxColor = StrCast(presBox?._backgroundColor); @@ -537,6 +559,19 @@ export class PresElementBox extends ViewBoxBaseComponent() { ); + items.push( + Customize}> +
{ + PresBox.Instance.setChatActive(true); + PresBox.Instance.slideToModify = this.rootDoc; + // this.customizeWithGPT(''); + }}> + e.stopPropagation()} /> +
+
+ ); return items; } diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.scss b/src/client/views/pdf/GPTPopup/GPTPopup.scss index 5d966395c..48659d0e7 100644 --- a/src/client/views/pdf/GPTPopup/GPTPopup.scss +++ b/src/client/views/pdf/GPTPopup/GPTPopup.scss @@ -11,8 +11,8 @@ $highlightedText: #82e0ff; right: 10px; width: 250px; min-height: 200px; - border-radius: 15px; - padding: 15px; + border-radius: 16px; + padding: 16px; padding-bottom: 0; z-index: 999; display: flex; -- cgit v1.2.3-70-g09d2 From e95d25eb8159bb7c753fa27e74e9baa8d3bffea6 Mon Sep 17 00:00:00 2001 From: alyssaf16 Date: Sat, 30 Mar 2024 00:06:27 -0400 Subject: working on making flashcards from pdf with ai --- src/.DS_Store | Bin 10244 -> 10244 bytes src/client/apis/gpt/GPT.ts | 8 +- src/client/documents/Documents.ts | 4 +- src/client/util/CurrentUserUtils.ts | 5 +- .../views/collections/CollectionCarouselView.scss | 18 +- .../views/collections/CollectionCarouselView.tsx | 76 +++++++++ src/client/views/collections/CollectionView.tsx | 9 + src/client/views/nodes/ComparisonBox.tsx | 181 ++++++++++++++++----- src/client/views/pdf/AnchorMenu.tsx | 23 +++ src/client/views/pdf/GPTPopup/GPTPopup.tsx | 39 ++++- src/client/views/pdf/PDFViewer.tsx | 4 + 11 files changed, 315 insertions(+), 52 deletions(-) (limited to 'src/client/views/pdf/GPTPopup') diff --git a/src/.DS_Store b/src/.DS_Store index f8d745dbf..f1f08fbb5 100644 Binary files a/src/.DS_Store and b/src/.DS_Store differ diff --git a/src/client/apis/gpt/GPT.ts b/src/client/apis/gpt/GPT.ts index cea862330..968b45273 100644 --- a/src/client/apis/gpt/GPT.ts +++ b/src/client/apis/gpt/GPT.ts @@ -4,6 +4,7 @@ enum GPTCallType { SUMMARY = 'summary', COMPLETION = 'completion', EDIT = 'edit', + FLASHCARD = 'flashcard', } type GPTCallOpts = { @@ -14,9 +15,10 @@ type GPTCallOpts = { }; const callTypeMap: { [type: string]: GPTCallOpts } = { - summary: { model: 'text-davinci-003', maxTokens: 256, temp: 0.5, prompt: 'Summarize this text in simpler terms: ' }, - edit: { model: 'text-davinci-003', maxTokens: 256, temp: 0.5, prompt: 'Reword this: ' }, - completion: { model: 'text-davinci-003', maxTokens: 256, temp: 0.5, prompt: '' }, + summary: { model: 'gpt-3.5-turbo-instruct', maxTokens: 256, temp: 0.5, prompt: 'Summarize this text in simpler terms: ' }, + edit: { model: 'gpt-3.5-turbo-instruct', maxTokens: 256, temp: 0.5, prompt: 'Reword this: ' }, + flashcard: { model: 'gpt-3.5-turbo-instruct', maxTokens: 256, temp: 0.5, prompt: 'Make flashcards out of this text with questions on the front and answers on the back: ' }, + completion: { model: 'gpt-3.5-turbo-instruct', maxTokens: 256, temp: 0.5, prompt: '' }, }; /** diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 2d2d13519..bf3d14560 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -1054,8 +1054,8 @@ export namespace Docs { return InstanceFromProto(Prototypes.get(DocumentType.SCREENSHOT), '', options); } - export function ComparisonDocument(options: DocumentOptions = { title: 'Comparison Box' }) { - return InstanceFromProto(Prototypes.get(DocumentType.COMPARISON), undefined, options); + export function ComparisonDocument(text: string, options: DocumentOptions = { title: 'Comparison Box' }) { + return InstanceFromProto(Prototypes.get(DocumentType.COMPARISON), text, options); } export function AudioDocument(url: string, options: DocumentOptions = {}, overwriteDoc?: Doc) { diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 958240f94..ab0315ba1 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -341,14 +341,15 @@ pie title Minerals in my tap water creator:(opts:DocumentOptions)=> any // how to create the empty thing if it doesn't exist }[] = [ {key: "Note", creator: opts => Docs.Create.TextDocument("", opts), opts: { _width: 200, _layout_autoHeight: true }}, - {key: "Flashcard", creator: Docs.Create.ComparisonDocument, opts: { _layout_isFlashcard: true, _width: 300, _height: 300 }}, + {key: "Flashcard", creator: opts => Docs.Create.ComparisonDocument("", opts), opts: { _layout_isFlashcard: true, _width: 300, _height: 300}}, + // {key: "Flashcard", creator: Docs.Create.ComparisonDocument("", opts), opts: { _layout_isFlashcard: true, _width: 300, _height: 300 }}, //{key: "Flashcard", creator: opts => Docs.Create.TextDocument("", opts), opts: { _width: 200, _layout_autoHeight: true, _layout_enableAltContentUI: true}}, {key: "Equation", creator: opts => Docs.Create.EquationDocument("",opts), opts: { _width: 300, _height: 35, }}, {key: "Noteboard", creator: opts => Docs.Create.NoteTakingDocument([], opts), opts: { _width: 250, _height: 200, _layout_fitWidth: true}}, {key: "Simulation", creator: opts => Docs.Create.SimulationDocument(opts), opts: { _width: 300, _height: 300, }}, {key: "Collection", creator: opts => Docs.Create.FreeformDocument([], opts), opts: { _width: 150, _height: 100, _layout_fitWidth: true }}, {key: "Webpage", creator: opts => Docs.Create.WebDocument("",opts), opts: { _width: 400, _height: 512, _nativeWidth: 850, data_useCors: true, }}, - {key: "Comparison", creator: Docs.Create.ComparisonDocument, opts: { _width: 300, _height: 300 }}, + {key: "Comparison", creator: opts => Docs.Create.ComparisonDocument("",opts), opts: { _width: 300, _height: 300 }}, {key: "Audio", creator: opts => Docs.Create.AudioDocument(nullAudio, opts),opts: { _width: 200, _height: 100, }}, {key: "Map", creator: opts => Docs.Create.MapDocument([], opts), opts: { _width: 800, _height: 600, _layout_fitWidth: true, }}, {key: "Screengrab", creator: Docs.Create.ScreenshotDocument, opts: { _width: 400, _height: 200 }}, diff --git a/src/client/views/collections/CollectionCarouselView.scss b/src/client/views/collections/CollectionCarouselView.scss index 130b31325..f115bb40a 100644 --- a/src/client/views/collections/CollectionCarouselView.scss +++ b/src/client/views/collections/CollectionCarouselView.scss @@ -13,7 +13,10 @@ } } .carouselView-back, -.carouselView-fwd { +.carouselView-fwd, +.carouselView-star, +.carouselView-remove, +.carouselView-check { position: absolute; display: flex; top: 42.5%; @@ -34,6 +37,19 @@ .carouselView-back { left: 20; } +.carouselView-star { + top: 0; + right: 20; +} +.carouselView-remove { + top: 80%; + left: 52%; +} +.carouselView-check { + top: 80%; + right: 52%; +} + .carouselView-back:hover, .carouselView-fwd:hover { background: lightgray; diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx index dae16bafb..7f5176123 100644 --- a/src/client/views/collections/CollectionCarouselView.tsx +++ b/src/client/views/collections/CollectionCarouselView.tsx @@ -36,11 +36,67 @@ export class CollectionCarouselView extends CollectionSubView() { advance = (e: React.MouseEvent) => { e.stopPropagation(); this.layoutDoc._carousel_index = (NumCast(this.layoutDoc._carousel_index) + 1) % this.childLayoutPairs.length; + var startInd = this.layoutDoc._carousel_index; + + // if the star filter is selected + if (this.layoutDoc[`_${this._props.fieldKey}_filterOp`] == 'star') { + // go to a new index that is starred, skip the ones that aren't + while (!this.childLayoutPairs?.[NumCast(startInd)].layout[`${this.fieldKey}_star`] && (startInd + 1) % this.childLayoutPairs.length != this.layoutDoc._carousel_index) { + startInd = (startInd + 1) % this.childLayoutPairs.length; + } + this.layoutDoc._carousel_index = startInd; + } + + // if the practice filter is selected + if (this.layoutDoc[`_${this._props.fieldKey}_filterOp`] == 'practice') { + // go to a new index that is missed, skip the ones that are correct + while (this.childLayoutPairs?.[NumCast(startInd)].layout[`${this.fieldKey}_missed`] == 'correct' && (startInd + 1) % this.childLayoutPairs.length != this.layoutDoc._carousel_index) { + startInd = (startInd + 1) % this.childLayoutPairs.length; + } + this.layoutDoc._carousel_index = startInd; + } }; goback = (e: React.MouseEvent) => { e.stopPropagation(); this.layoutDoc._carousel_index = (NumCast(this.layoutDoc._carousel_index) - 1 + this.childLayoutPairs.length) % this.childLayoutPairs.length; + + var startInd = this.layoutDoc._carousel_index; + + // if the star filter is selected + if (this.layoutDoc[`_${this._props.fieldKey}_filterOp`] == 'star') { + // go to a new index that is starred, skip the ones that aren't + while (!this.childLayoutPairs?.[NumCast(startInd)].layout[`${this.fieldKey}_star`] && (startInd - 1 + this.childLayoutPairs.length) % this.childLayoutPairs.length != this.layoutDoc._carousel_index) { + startInd = (startInd - 1 + this.childLayoutPairs.length) % this.childLayoutPairs.length; + } + this.layoutDoc._carousel_index = startInd; + } + + // if the practice filter is selected + if (this.layoutDoc[`_${this._props.fieldKey}_filterOp`] == 'practice') { + // go to a new index that is missed, skip the ones that are correct + while (this.childLayoutPairs?.[NumCast(startInd)].layout[`${this.fieldKey}_missed`] == 'correct' && (startInd - 1 + this.childLayoutPairs.length) % this.childLayoutPairs.length != this.layoutDoc._carousel_index) { + startInd = (startInd - 1 + this.childLayoutPairs.length) % this.childLayoutPairs.length; + } + this.layoutDoc._carousel_index = startInd; + } + }; + + star = (e: React.MouseEvent) => { + e.stopPropagation(); + // stars the document when the button is pressed + const curDoc = this.childLayoutPairs?.[NumCast(this.layoutDoc._carousel_index)]; + if (curDoc.layout[`${this.fieldKey}_star`] == undefined) curDoc.layout[`${this.fieldKey}_star`] = true; + else curDoc.layout[`${this.fieldKey}_star`] = !curDoc.layout[`${this.fieldKey}_star`]; + }; + + missed = (e: React.MouseEvent, val: string) => { + e.stopPropagation(); + const curDoc = this.childLayoutPairs?.[NumCast(this.layoutDoc._carousel_index)]; + curDoc.layout[`${this.fieldKey}_missed`] = val; + this.layoutDoc._carousel_index = (NumCast(this.layoutDoc._carousel_index) + 1) % this.childLayoutPairs.length; + this.advance; }; + captionStyleProvider = (doc: Doc | undefined, captionProps: Opt, property: string): any => { // first look for properties on the document in the carousel, then fallback to properties on the container const childValue = doc?.['caption-' + property] ? this._props.styleProvider?.(doc, captionProps, property) : undefined; @@ -106,6 +162,15 @@ export class CollectionCarouselView extends CollectionSubView() {
+
+ +
+
this.missed(e, 'missed')} style={{ visibility: this.layoutDoc[`_${this._props.fieldKey}_filterOp`] == 'practice' ? 'visible' : 'hidden' }}> + +
+
this.missed(e, 'correct')} style={{ visibility: this.layoutDoc[`_${this._props.fieldKey}_filterOp`] == 'practice' ? 'visible' : 'hidden' }}> + +
); } @@ -120,6 +185,17 @@ export class CollectionCarouselView extends CollectionSubView() { color: this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Color), }}> {this.content} +

+ Recently missed! +

{this.Document._chromeHidden ? null : this.buttons} ); diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 18eb4dd1f..168176edf 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -176,6 +176,15 @@ export class CollectionView extends ViewBoxAnnotatableComponent (this.layoutDoc[`_${this._props.fieldKey}_filterOp`] = 'all'), icon: 'eye-slash' }); + revealItems.push({ description: 'Star', event: () => (this.layoutDoc[`_${this._props.fieldKey}_filterOp`] = 'star'), icon: 'hand-point-up' }); + revealItems.push({ description: 'Practice Mode', event: () => (this.layoutDoc[`_${this._props.fieldKey}_filterOp`] = 'practice'), icon: 'rotate' }); + + //revealItems.push({ description: 'Bring to Front', event: () => SelectionManager.Views.forEach(dv => dv._props.bringToFront?.(dv.Document, false)), icon: 'arrow-up' }); + !revealOptions && cm.addItem({ description: 'Filter Flashcards', addDivider: false, noexpand: true, subitems: revealItems, icon: 'layer-group' }); + const options = cm.findByDescription('Options...'); const optionItems = options && 'subitems' in options ? options.subitems : []; !Doc.noviceMode ? optionItems.splice(0, 0, { description: `${this.Document.forceActive ? 'Select' : 'Force'} Contents Active`, event: () => (this.Document.forceActive = !this.Document.forceActive), icon: 'project-diagram' }) : null; diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx index 81f624b20..ce862cd22 100644 --- a/src/client/views/nodes/ComparisonBox.tsx +++ b/src/client/views/nodes/ComparisonBox.tsx @@ -4,13 +4,14 @@ import { observer } from 'mobx-react'; import * as React from 'react'; import { emptyFunction, returnFalse, returnNone, returnZero, setupMoveUpEvents } from '../../../Utils'; import { Doc, Opt } from '../../../fields/Doc'; -import { DocCast, NumCast, StrCast } from '../../../fields/Types'; +import { DocCast, NumCast, RTFCast, StrCast } from '../../../fields/Types'; import { DocUtils, Docs } from '../../documents/Documents'; import { DragManager, dropActionType } from '../../util/DragManager'; import { undoBatch } from '../../util/UndoManager'; import { SnappingManager } from '../../util/SnappingManager'; import { ViewBoxAnnotatableComponent, ViewBoxInterface } from '../DocComponent'; import { StyleProp } from '../StyleProvider'; +import { Tooltip } from '@mui/material'; import { CSSTransition } from 'react-transition-group'; import './ComparisonBox.scss'; import { DocumentView } from './DocumentView'; @@ -18,6 +19,8 @@ import { FieldView, FieldViewProps } from './FieldView'; import { PinProps, PresBox } from './trails'; import { FormattedTextBox } from './formattedText/FormattedTextBox'; import { RichTextField } from '../../../fields/RichTextField'; +import { GPTCallType, gptAPICall } from '../../apis/gpt/GPT'; +import { DocData } from '../../../fields/DocSymbols'; @observer export class ComparisonBox extends ViewBoxAnnotatableComponent() implements ViewBoxInterface { @@ -88,7 +91,8 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent() if (this._isAnyChildContentActive) return; this._animating = 'all 200ms'; // on click, animate slider movement to the targetWidth - this.layoutDoc[this.clipWidthKey] = (targetWidth * 100) / this._props.PanelWidth(); + // this.layoutDoc[this.clipWidthKey] = (targetWidth * 100) / this._props.PanelWidth(); + this.layoutDoc[this.clipHeightKey] = (targetWidth * 100) / this._props.PanelHeight(); setTimeout( action(() => (this._animating = '')), @@ -128,7 +132,6 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent() clearDoc = (fieldKey: string) => { delete this.dataDoc[fieldKey]; this.dataDoc[fieldKey] = 'empty'; - // this.dataDoc[this.fieldKey + '_1'] = 'empty'; }; // clearDoc = (fieldKey: string) => delete this.dataDoc[fieldKey]; @@ -182,47 +185,72 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent() this.layoutDoc[`_${this._props.fieldKey}_usePath`] = usePath === undefined ? 'alternate' : undefined; }; + hoverFlip = (side: string | undefined) => { + if (this.layoutDoc[`_${this._props.fieldKey}_revealOp`] == 'hover') this.layoutDoc[`_${this._props.fieldKey}_usePath`] = side; + }; @computed get overlayAlternateIcon() { const usepath = this.layoutDoc[`_${this._props.fieldKey}_usePath`]; return ( -
- setupMoveUpEvents(e.target, e, returnFalse, emptyFunction, e => { - console.log(this.layoutDoc[`_${this._props.fieldKey}_revealOp`]); - if (!this.layoutDoc[`_${this._props.fieldKey}_revealOp`] || this.layoutDoc[`_${this._props.fieldKey}_revealOp`] == 'flip') { - this.flipFlashcard(); - } - }) - } - style={{ - //display: this._props.isContentActive() && !SnappingManager.IsDragging ? 'flex' : 'none', - background: usepath === 'alternate' ? 'white' : 'black', - color: usepath === 'alternate' ? 'black' : 'white', - }}> - -
+ flip}> +
+ setupMoveUpEvents(e.target, e, returnFalse, emptyFunction, e => { + console.log(this.layoutDoc[`_${this._props.fieldKey}_revealOp`]); + if (!this.layoutDoc[`_${this._props.fieldKey}_revealOp`] || this.layoutDoc[`_${this._props.fieldKey}_revealOp`] == 'flip') { + this.flipFlashcard(); + } + }) + } + style={{ + //display: this._props.isContentActive() && !SnappingManager.IsDragging ? 'flex' : 'none', + background: usepath === 'alternate' ? 'white' : 'black', + color: usepath === 'alternate' ? 'black' : 'white', + }}> + +
+
); } render() { const clearButton = (which: string) => { return ( -
this.closeDown(e, which)} // prevent triggering slider movement in registerSliding - > - -
+ remove}> +
this.closeDown(e, which)} // prevent triggering slider movement in registerSliding + > + +
+
); }; const displayDoc = (which: string) => { const whichDoc = DocCast(this.dataDoc[which]); const targetDoc = DocCast(whichDoc?.annotationOn, whichDoc); // if there is no Doc in the first comparison slot, but the comparison box's fieldKey slot has a RichTextField, then render a text box to show the contents of the document's field key slot - const layoutTemplateString = !targetDoc && which.endsWith('1') && this.Document[this.fieldKey] instanceof RichTextField ? FormattedTextBox.LayoutString(this.fieldKey) : undefined; + //const layoutTemplateString = !targetDoc && which.endsWith('1') && this.Document[this.fieldKey] instanceof RichTextField ? FormattedTextBox.LayoutString(this.fieldKey) : undefined; + const subjectText = RTFCast(this.Document[this.fieldKey])?.Text; + const layoutTemplateString = !targetDoc + ? which.endsWith('0') && subjectText !== undefined + ? FormattedTextBox.LayoutString(this.fieldKey) + : which.endsWith('1') && (this.Document[which] instanceof RichTextField || typeof this.Document[which] === 'string') + ? FormattedTextBox.LayoutString(which) + : undefined + : undefined; + + if (which.endsWith('1') && !layoutTemplateString && targetDoc) { + const queryText = RTFCast(this.Document[this.fieldKey + '_1'])?.Text; + console.log('QUER' + queryText); + if (queryText?.includes('--TEXT--') && subjectText) { + this.Document[DocData][this.fieldKey + '_1'] = ''; + gptAPICall(queryText?.replace('--TEXT--', subjectText), GPTCallType.COMPLETION).then(value => (this.Document[DocData][this.fieldKey + '_1'] = value.trim())); + } + } + return targetDoc || layoutTemplateString ? ( <> () moveDocument={which.endsWith('1') ? this.moveDoc1 : this.moveDoc2} removeDocument={which.endsWith('1') ? this.remDoc1 : this.remDoc2} NativeWidth={() => NumCast(this.layoutDoc.width, 200)} - NativeHeight={() => NumCast(this.layoutDoc.height, 200)} + NativeHeight={(): number => { + return NumCast(this.layoutDoc.height, 200); + }} isContentActive={emptyFunction} isDocumentActive={returnFalse} whenChildContentsActiveChanged={this.whenChildContentsActiveChanged} @@ -251,6 +281,8 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent() ); }; const displayBox = (which: string, index: number, cover: number) => { + // if (this.layoutDoc[`_${this._props.fieldKey}_revealOp`] == 'hide/reveal') this.layoutDoc[this.clipHeightKey] = 100; + // else this.layoutDoc.height = 300; return (
this.registerSliding(e, cover)} ref={ele => this.createDropTarget(ele, which, index)}> {displayDoc(which)} @@ -258,26 +290,89 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent() ); }; + const displayBoxReveal = (which: string, which2: string, index: number, cover: number) => { + return ( +
+
this.registerSliding(e, cover)} + ref={ele => this.createDropTarget(ele, which, 0)}> + {displayDoc(which)} +
+
this.registerSliding(e, cover)} + ref={ele => this.createDropTarget(ele, which2, 1)}> + {displayDoc(which2)} +
+
+ ); + }; + if (this.Document._layout_isFlashcard) { const side = this.layoutDoc[`_${this._props.fieldKey}_usePath`] === 'alternate' ? 1 : 0; - if (!(this.dataDoc[this.fieldKey + '_0'] || this.dataDoc[this.fieldKey + '_0'] == 'empty')) this.dataDoc[this.fieldKey + '_0'] = DocUtils.copyDragFactory(Doc.UserDoc().emptyNote as Doc); + // add text box when first created + if (!(this.dataDoc[this.fieldKey + '_0'] || this.dataDoc[this.fieldKey + '_0'] == 'empty')) { + console.log('TEXT HERE' + this.dataDoc.data); + const newDoc = Docs.Create.TextDocument(StrCast(this.dataDoc.data)); + this.addDoc(newDoc, this.fieldKey + '_0'); + } + // if (!(this.dataDoc[this.fieldKey + '_0'] || this.dataDoc[this.fieldKey + '_0'] == 'empty')) this.dataDoc[this.fieldKey + '_0'] = DocUtils.copyDragFactory(Doc.UserDoc().emptyNote as Doc); if (!(this.dataDoc[this.fieldKey + '_1'] || this.dataDoc[this.fieldKey + '_1'] == 'empty')) this.dataDoc[this.fieldKey + '_1'] = DocUtils.copyDragFactory(Doc.UserDoc().emptyNote as Doc); - return ( -
{ - if (this.layoutDoc[`_${this._props.fieldKey}_revealOp`] == 'hover') this.layoutDoc[`_${this._props.fieldKey}_usePath`] = 'alternate'; - }} - onMouseLeave={() => { - if (this.layoutDoc[`_${this._props.fieldKey}_revealOp`] == 'hover') this.layoutDoc[`_${this._props.fieldKey}_usePath`] = undefined; - }}> - {displayBox(`${this.fieldKey}_${side}`, side, this._props.PanelWidth() - 3)} + if (this.layoutDoc[`_${this._props.fieldKey}_revealOp`] == 'hide/reveal') { + { + return ( +
+ {displayBox(`${this.fieldKey}_0`, side, this._props.PanelHeight() - 3)} + {displayBox(`${this.fieldKey}_1`, 1, this._props.PanelHeight() - 3)} +
+ ); + // return ( + //
+ // {displayBox(`${this.fieldKey}_2`, 1, this._props.PanelHeight() - 3)} + //
+ // {displayBox(`${this.fieldKey}_1`, 0, 0)} + //
+ //
+ // ); + // return ( + //
+ // {displayBox(`${this.fieldKey}_${side === 0 ? 1 : 0}`, side, this._props.PanelHeight() / 2)} + // {displayBox(`${this.fieldKey}_${side === 0 ? 0 : 1}`, 1, this._props.PanelHeight() / 2)} - {this.overlayAlternateIcon} -
- ); + //
+ // ); + } + } else { + return ( +
{ + this.hoverFlip('alternate'); + }} + onMouseLeave={() => { + this.hoverFlip(undefined); + }}> + {displayBox(`${this.fieldKey}_${side === 0 ? 1 : 0}`, side, this._props.PanelWidth() - 3)} + {/* {displayBoxReveal(`${this.fieldKey}_${side}`, side, this._props.PanelWidth() - 3)} + {displayBoxReveal(`${this.fieldKey}_${side === 0 ? 1 : 0}`, side === 0 ? 1 : 0, this._props.PanelWidth() - 3)} */} + {/*
{displayBoxReveal(`${this.fieldKey}_${side}`, side, this._props.PanelWidth() - 3)}
*/} + {/*
+ {displayBox(`${this.fieldKey}_${side === 0 ? 1 : 0}`, side === 0 ? 1 : 0, this._props.PanelWidth() - 3)} +
*/} + + {this.overlayAlternateIcon} +
+ ); + } } else { return (
diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx index d0688c338..844c1e36d 100644 --- a/src/client/views/pdf/AnchorMenu.tsx +++ b/src/client/views/pdf/AnchorMenu.tsx @@ -6,6 +6,7 @@ import * as React from 'react'; import { ColorResult } from 'react-color'; import { Utils, returnFalse, setupMoveUpEvents, unimplementedFunction } from '../../../Utils'; import { Doc, Opt } from '../../../fields/Doc'; +import { DocUtils, Docs } from '../../documents/Documents'; import { GPTCallType, gptAPICall } from '../../apis/gpt/GPT'; import { DocumentType } from '../../documents/DocumentTypes'; import { SelectionManager } from '../../util/SelectionManager'; @@ -87,6 +88,22 @@ export class AnchorMenu extends AntimodeMenu { GPTPopup.Instance.setLoading(false); }; + gptFlashcards = async (e: React.PointerEvent) => { + // move this logic to gptpopup, need to implement generate again + // GPTPopup.Instance.setVisible(true); + // GPTPopup.Instance.setMode(GPTPopupMode.FLASHCARD); + // GPTPopup.Instance.setLoading(true); + + try { + const res = await gptAPICall(this.selectedText, GPTCallType.FLASHCARD); + GPTPopup.Instance.setText(res || 'Something went wrong.'); + GPTPopup.Instance.transferToFlashcard(); + } catch (err) { + console.error(err); + } + GPTPopup.Instance.setLoading(false); + }; + pointerDown = (e: React.PointerEvent) => { setupMoveUpEvents( this, @@ -176,6 +193,12 @@ export class AnchorMenu extends AntimodeMenu { color={SettingsManager.userColor} /> )} + } + color={SettingsManager.userColor} + /> {AnchorMenu.Instance.OnAudio === unimplementedFunction ? null : ( { } }; + transferToFlashcard = () => { + const senArr = this.text.split('.'); + for (var i = 0; i < senArr.length; i += 2) { + console.log('SEN: ' + senArr[i]); + const newDoc = Docs.Create.ComparisonDocument(senArr[i], { _layout_isFlashcard: true, _width: 300, _height: 300 }); + newDoc.text = senArr[i]; + this.addDoc(newDoc, this.sidebarId); + const anchor = AnchorMenu.Instance?.GetAnchor(undefined, false); + if (anchor) { + DocUtils.MakeLink(newDoc, anchor, { + link_relationship: 'GPT Summary', + }); + } + } + }; + /** * Transfers the image urls to actual image docs */ @@ -213,6 +232,23 @@ export class GPTPopup extends ObservableReactComponent { ); }; + flashcardBox = () => { + // const textArr = this.text.split("."); + // textArr.forEach(function(sentence) { + // console.log(sentence); + + // }); + // const newDoc = Docs.Create.ComparisonDocument(); + // this.addToCollection?.(newDoc); + // // const newDoc = Docs.Create.ComparisonDocument(); + // DocUtils.copyDragFactory(Doc.UserDoc().emptyFlashcard as Doc); + // // this.addToCollection?.(newDoc); + // // return newDoc; + // + const newDoc = Docs.Create.TextDocument('Hello there'); + this.addDoc?.(newDoc); + }; + data = () => { return (
@@ -268,6 +304,7 @@ export class GPTPopup extends ObservableReactComponent { <> } color={StrCast(Doc.UserDoc().userVariantColor)} />
); - // return ( - //
- // {displayBox(`${this.fieldKey}_2`, 1, this._props.PanelHeight() - 3)} - //
- // {displayBox(`${this.fieldKey}_1`, 0, 0)} - //
- //
- // ); - // return ( - //
- // {displayBox(`${this.fieldKey}_${side === 0 ? 1 : 0}`, side, this._props.PanelHeight() / 2)} - // {displayBox(`${this.fieldKey}_${side === 0 ? 0 : 1}`, 1, this._props.PanelHeight() / 2)} - - //
- // ); } } else { return ( @@ -362,12 +349,6 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent() this.hoverFlip(undefined); }}> {displayBox(`${this.fieldKey}_${side === 0 ? 1 : 0}`, side, this._props.PanelWidth() - 3)} - {/* {displayBoxReveal(`${this.fieldKey}_${side}`, side, this._props.PanelWidth() - 3)} - {displayBoxReveal(`${this.fieldKey}_${side === 0 ? 1 : 0}`, side === 0 ? 1 : 0, this._props.PanelWidth() - 3)} */} - {/*
{displayBoxReveal(`${this.fieldKey}_${side}`, side, this._props.PanelWidth() - 3)}
*/} - {/*
- {displayBox(`${this.fieldKey}_${side === 0 ? 1 : 0}`, side === 0 ? 1 : 0, this._props.PanelWidth() - 3)} -
*/} {this.overlayAlternateIcon}
diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx index 844c1e36d..7cb6a20f4 100644 --- a/src/client/views/pdf/AnchorMenu.tsx +++ b/src/client/views/pdf/AnchorMenu.tsx @@ -15,6 +15,7 @@ import { AntimodeMenu, AntimodeMenuProps } from '../AntimodeMenu'; import { LinkPopup } from '../linking/LinkPopup'; import './AnchorMenu.scss'; import { GPTPopup, GPTPopupMode } from './GPTPopup/GPTPopup'; +import { PDFViewer } from 'pdfjs-dist/web/pdf_viewer.mjs'; @observer export class AnchorMenu extends AntimodeMenu { @@ -57,6 +58,7 @@ export class AnchorMenu extends AntimodeMenu { public get Active() { return this._left > 0; } + public addToCollection: ((doc: Doc | Doc[], annotationKey?: string | undefined) => boolean) | undefined; componentWillUnmount() { this._disposer?.(); @@ -96,14 +98,34 @@ export class AnchorMenu extends AntimodeMenu { try { const res = await gptAPICall(this.selectedText, GPTCallType.FLASHCARD); + GPTPopup.Instance.setText(res || 'Something went wrong.'); - GPTPopup.Instance.transferToFlashcard(); + this.transferToFlashcard(res); } catch (err) { console.error(err); } GPTPopup.Instance.setLoading(false); }; + transferToFlashcard = (text: string) => { + const senArr = text.split('Question'); + const collectionArr: Doc[] = []; + for (var i = 1; i < senArr.length; i++) { + console.log('Arr ' + i + ': ' + senArr[i]); + const newDoc = Docs.Create.ComparisonDocument(senArr[i], { _layout_isFlashcard: true, _width: 300, _height: 300 }); + newDoc.text = senArr[i]; + collectionArr.push(newDoc); + } + const newCol = Docs.Create.CarouselDocument(collectionArr, { + _width: 200, + _height: 200, + _layout_fitWidth: true, + _layout_autoHeight: true, + }); + + this.addToCollection?.(newCol); + }; + pointerDown = (e: React.PointerEvent) => { setupMoveUpEvents( this, diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.tsx b/src/client/views/pdf/GPTPopup/GPTPopup.tsx index dbcdd4e3a..32c1721ec 100644 --- a/src/client/views/pdf/GPTPopup/GPTPopup.tsx +++ b/src/client/views/pdf/GPTPopup/GPTPopup.tsx @@ -144,6 +144,15 @@ export class GPTPopup extends ObservableReactComponent { _layout_autoHeight: true, }); this.addDoc(newDoc, this.sidebarId); + // const arr = [newDoc]; + // const newCol = Docs.Create.CarouselDocument(arr, { + // _width: 200, + // _height: 200, + // _layout_fitWidth: true, + // _layout_autoHeight: true, + // }); + // this.addDoc(newDoc, this.sidebarId); + // this.addDoc(newCol, this.sidebarId); const anchor = AnchorMenu.Instance?.GetAnchor(undefined, false); if (anchor) { DocUtils.MakeLink(newDoc, anchor, { @@ -152,21 +161,24 @@ export class GPTPopup extends ObservableReactComponent { } }; - transferToFlashcard = () => { - const senArr = this.text.split('.'); - for (var i = 0; i < senArr.length; i += 2) { - console.log('SEN: ' + senArr[i]); - const newDoc = Docs.Create.ComparisonDocument(senArr[i], { _layout_isFlashcard: true, _width: 300, _height: 300 }); - newDoc.text = senArr[i]; - this.addDoc(newDoc, this.sidebarId); - const anchor = AnchorMenu.Instance?.GetAnchor(undefined, false); - if (anchor) { - DocUtils.MakeLink(newDoc, anchor, { - link_relationship: 'GPT Summary', - }); - } - } - }; + // transferToFlashcard = () => { + // const senArr = this.text.split('Question'); + // const collectionArr: Doc[] = []; + // for (var i = 1; i < senArr.length; i++) { + // console.log('Arr ' + i + ': ' + senArr[i]); + // const newDoc = Docs.Create.ComparisonDocument(senArr[i], { _layout_isFlashcard: true, _width: 300, _height: 300 }); + // newDoc.text = senArr[i]; + // collectionArr.push(newDoc); + // } + // const newCol = Docs.Create.CarouselDocument(collectionArr, { + // _width: 200, + // _height: 200, + // _layout_fitWidth: true, + // _layout_autoHeight: true, + // }); + // this.addDoc(newCol, this.sidebarId); + // this.addToCollection?.(newCol); + // }; /** * Transfers the image urls to actual image docs diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 7d8529a1c..cecaf17ff 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -417,6 +417,7 @@ export class PDFViewer extends ObservableReactComponent { // Changing which document to add the annotation to (the currently selected PDF) GPTPopup.Instance.setSidebarId('data_sidebar'); GPTPopup.Instance.addDoc = this._props.sidebarAddDoc; + AnchorMenu.Instance.addToCollection = this._props.DocumentView?.()._props.addDocument; // const newDoc = Docs.Create.ComparisonDocument({ _layout_isFlashcard: true, _width: 300, _height: 300 }); // this.props.addDocument?.(newDoc); }; -- cgit v1.2.3-70-g09d2 From 2de4f0938dedf8a6b2d60886df5c59bce7c72f59 Mon Sep 17 00:00:00 2001 From: srichman333 Date: Tue, 23 Apr 2024 17:26:23 -0400 Subject: ui for filtered visualization from ai --- src/client/views/nodes/DataVizBox/DataVizBox.tsx | 14 +++++++++++--- src/client/views/pdf/GPTPopup/GPTPopup.tsx | 8 ++++++++ 2 files changed, 19 insertions(+), 3 deletions(-) (limited to 'src/client/views/pdf/GPTPopup') diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx index dbba9c7f3..a6de64d85 100644 --- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx +++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx @@ -427,19 +427,27 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() im GPTPopup.Instance.generateDataAnalysis(); }); + /** + * creates a new dataviz document filter from this one + * it appears to the right of this document, with the + * parameters passed in being used to create an initial display + */ createFilteredDoc = (axes?: any, type?: DataVizView) => { - const embedding = Doc.MakeEmbedding(this.Document!); - embedding._dataViz = DataVizView.HISTOGRAM; + embedding._layout_showSidebar = false; + embedding._dataViz = type? type : DataVizView.HISTOGRAM; embedding._dataViz_axes = new List(axes); embedding._dataViz_parentViz = this.Document; embedding.histogramBarColors = Field.Copy(this.layoutDoc.histogramBarColors); embedding.defaultHistogramColor = this.layoutDoc.defaultHistogramColor; embedding.pieSliceColors = Field.Copy(this.layoutDoc.pieSliceColors); + embedding._layout_showSidebar = false; + embedding.width = NumCast(this.layoutDoc._width)- this.sidebarWidth(); + embedding._layout_sidebarWidthPercent = "0%"; this._props.addDocument?.(embedding); embedding._dataViz_axes = new List(axes) this.layoutDoc.dataViz_selectedRows = new List(this.records.map((rec, i) => i)) - embedding.x = Number(embedding.x) + 100.0; + embedding.x = Number(embedding.x) + Number(this.Document.width); return true; }; diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.tsx b/src/client/views/pdf/GPTPopup/GPTPopup.tsx index 686ef9c28..40946cd36 100644 --- a/src/client/views/pdf/GPTPopup/GPTPopup.tsx +++ b/src/client/views/pdf/GPTPopup/GPTPopup.tsx @@ -152,6 +152,10 @@ export class GPTPopup extends ObservableReactComponent { this.setLoading(false); }; + /** + * Completes an API call to generate a summary of + * this.selectedText in the popup. + */ generateSummary = async () => { GPTPopup.Instance.setVisible(true); GPTPopup.Instance.setMode(GPTPopupMode.SUMMARY); @@ -166,6 +170,10 @@ export class GPTPopup extends ObservableReactComponent { GPTPopup.Instance.setLoading(false); } + /** + * Completes an API call to generate an analysis of + * this.dataJson in the popup. + */ generateDataAnalysis = async () => { GPTPopup.Instance.setVisible(true); GPTPopup.Instance.setLoading(true); -- cgit v1.2.3-70-g09d2 From e2b4502f87cf5707a77736267a1d96261024874c Mon Sep 17 00:00:00 2001 From: Sophie Zhang Date: Thu, 9 May 2024 11:49:33 -0400 Subject: started cleanup --- src/client/views/nodes/trails/PresBox.tsx | 261 ++++++++++++++--------------- src/client/views/pdf/GPTPopup/GPTPopup.tsx | 1 + 2 files changed, 124 insertions(+), 138 deletions(-) (limited to 'src/client/views/pdf/GPTPopup') diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index 964b793bd..248de5f4b 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -421,20 +421,6 @@ export class PresBox extends ViewBoxBaseComponent() { console.log('Parsed GPT Result ', resObj); // this.activeItem this.setGeneratedAnimations(resObj as AnimationSettings[]); - // this.updateEffect(resObj.effect as PresEffect, false); - // this.updateEffectTiming(this.activeItem, { - // type: SpringType.CUSTOM, - // stiffness: resObj.stiffness, - // damping: resObj.damping, - // mass: resObj.mass, - // }); - - // for (let key in resObj) { - // if (resObj[key]) { - // console.log('typeof property', typeof resObj[key]); - // this.activeItem[key] = resObj[key]; - // } - // } } } catch (err) { console.error(err); @@ -1862,7 +1848,7 @@ export class PresBox extends ViewBoxBaseComponent() {
{[DocumentType.AUDIO, DocumentType.VID].includes(targetType as any as DocumentType) ? null : ( <> -
+
Slide Duration
e.stopPropagation()} onChange={e => this.updateDurationTime(e.target.value)} /> s @@ -2035,6 +2021,7 @@ export class PresBox extends ViewBoxBaseComponent() { return ( <> {/* GPT Component */} + {/* This chatbox is for customizing the properties of trails, like transition time, movement type (zoom, pan) */}

Customize with GPT

@@ -2081,6 +2068,112 @@ export class PresBox extends ViewBoxBaseComponent() { />
+ + {/* Movement */} +
{ + e.stopPropagation(); + this._openMovementDropdown = false; + this._openEffectDropdown = false; + this._openBulletEffectDropdown = false; + })}> +
+ Movement + { + this.updateMovement(val as PresMovement); + }} + dropdownType={DropdownType.SELECT} + type={Type.TERT} + /> +
+
Zoom (% screen filled)
+
+ this.updateZoom(e.target.value)} />% +
+ {/*
+
this.updateZoom(String(zoom), 0.1)}> + +
+
this.updateZoom(String(zoom), -0.1)}> + +
+
*/} +
+ {PresBox.inputter('0', '1', '100', zoom, activeItem.presentation_movement === PresMovement.Zoom, this.updateZoom)} +
+
Transition Time
+
+ e.stopPropagation()} onChange={action(e => this.updateTransitionTime(e.target.value))} /> s +
+ {/*
+
this.updateTransitionTime(String(transitionSpeed), 1000)}> + +
+
this.updateTransitionTime(String(transitionSpeed), -1000)}> + +
+
*/} +
+ {PresBox.inputter('0.1', '0.1', '10', transitionSpeed, true, this.updateTransitionTime)} +
+
Fast
+
Medium
+
Slow
+
+ {/* Easing function */} +
+ { + if (typeof val === 'string') { + if (val !== 'custom') { + this.setBezierEditorVisibility(true); + this.setEaseFunc(this.activeItem, val); + } else { + this.setEaseFunc(this.activeItem, TIMING_DEFAULT_MAPPINGS.ease); + } + } + }} + dropdownType={DropdownType.SELECT} + type={Type.TERT} + /> + {/* Custom */} +
{ + e.stopPropagation(); + this.setBezierEditorVisibility(!this.showBezierEditor); + }}> + {`${this.showBezierEditor ? 'Hide' : 'Show'} Timing Editor`} + +
+ {this.showBezierEditor && ( + <> +

+ Custom Timing Function +

+ + + )} +
+
+
+ + {/* This chatbox is for getting slide effect transition suggestions from gpt and visualizing them */}
@@ -2099,21 +2192,6 @@ export class PresBox extends ViewBoxBaseComponent() { e.stopPropagation(); }} /> - {/* (this._inputref2 = r)} - minRows={1} - placeholder="Get animation suggestions..." - className="pres-chatbox" - autoFocus={true} - value={this.animationChat} - onChange={e => { - this.setAnimationChat(e.target.value); - }} - onKeyDown={e => { - this.stopDictation(true); - e.stopPropagation(); - }} - /> */}
- {/* Movement */} -
- Movement - { - this.updateMovement(val as PresMovement); - }} - dropdownType={DropdownType.SELECT} - type={Type.TERT} - /> -
-
Zoom (% screen filled)
-
- this.updateZoom(e.target.value)} />% -
- {/*
-
this.updateZoom(String(zoom), 0.1)}> - -
-
this.updateZoom(String(zoom), -0.1)}> - -
-
*/} -
- {PresBox.inputter('0', '1', '100', zoom, activeItem.presentation_movement === PresMovement.Zoom, this.updateZoom)} -
-
Transition Time
-
- e.stopPropagation()} onChange={action(e => this.updateTransitionTime(e.target.value))} /> s -
- {/*
-
this.updateTransitionTime(String(transitionSpeed), 1000)}> - -
-
this.updateTransitionTime(String(transitionSpeed), -1000)}> - -
-
*/} -
- {PresBox.inputter('0.1', '0.1', '10', transitionSpeed, true, this.updateTransitionTime)} -
-
Fast
-
Medium
-
Slow
-
- {/* Easing function */} -
- { - if (typeof val === 'string') { - if (val !== 'custom') { - this.setBezierEditorVisibility(true); - this.setEaseFunc(this.activeItem, val); - } else { - this.setEaseFunc(this.activeItem, TIMING_DEFAULT_MAPPINGS.ease); - } - } - }} - dropdownType={DropdownType.SELECT} - type={Type.TERT} - /> - {/* Custom */} -
{ - e.stopPropagation(); - this.setBezierEditorVisibility(!this.showBezierEditor); - }}> - {`${this.showBezierEditor ? 'Hide' : 'Show'} Timing Editor`} - -
- {this.showBezierEditor && ( - <> -

- Custom Timing Function -

- - - )} -
-
{/* Toggles */} { _layout_autoHeight: true, }); this.addDoc(newDoc, this.sidebarId); + // newDoc.data = 'Hello world'; const anchor = AnchorMenu.Instance?.GetAnchor(undefined, false); if (anchor) { DocUtils.MakeLink(newDoc, anchor, { -- cgit v1.2.3-70-g09d2 From 87bca251d87b5a95da06b2212400ce9427152193 Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 14 May 2024 13:46:44 -0400 Subject: cleaning up dataviz code --- src/client/apis/gpt/GPT.ts | 9 ++-- src/client/views/nodes/DataVizBox/DataVizBox.tsx | 49 ++++++++++------------ .../views/nodes/DataVizBox/components/TableBox.tsx | 5 +-- src/client/views/pdf/GPTPopup/GPTPopup.tsx | 5 +-- 4 files changed, 33 insertions(+), 35 deletions(-) (limited to 'src/client/views/pdf/GPTPopup') diff --git a/src/client/apis/gpt/GPT.ts b/src/client/apis/gpt/GPT.ts index 8747a00a6..9deae7cff 100644 --- a/src/client/apis/gpt/GPT.ts +++ b/src/client/apis/gpt/GPT.ts @@ -24,9 +24,12 @@ const callTypeMap: { [type: string]: GPTCallOpts } = { summary: { model: 'gpt-3.5-turbo', maxTokens: 256, temp: 0.5, prompt: 'Summarize the text given in simpler terms.' }, edit: { model: 'gpt-3.5-turbo', maxTokens: 256, temp: 0.5, prompt: 'Reword the text.' }, completion: { model: 'gpt-3.5-turbo', maxTokens: 256, temp: 0.5, prompt: "You are a helpful assistant. Answer the user's prompt." }, - // data: { model: 'gpt-3.5-turbo', maxTokens: 256, temp: 0.5, prompt: "You are a helpful resarch assistant. Analyze the user's data to find meaningful patterns and/or correlation. Please keep your response short and to the point." }, - data: { model: 'gpt-3.5-turbo', maxTokens: 256, temp: 0.5, prompt: "You are a helpful resarch assistant. Analyze the user's data to find meaningful patterns and/or correlation. Please only return a JSON with a correlation column 1 propert, a correlation column 2 property, and an analysis property. " }, - + data: { + model: 'gpt-3.5-turbo', + maxTokens: 256, + temp: 0.5, + prompt: "You are a helpful resarch assistant. Analyze the user's data to find meaningful patterns and/or correlation. Please only return a JSON with a correlation column 1 propert, a correlation column 2 property, and an analysis property. ", + }, }; /** diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx index 01258a996..3f16220a5 100644 --- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx +++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx @@ -1,4 +1,5 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { Checkbox } from '@mui/material'; import { Colors, Toggle, ToggleType, Type } from 'browndash-components'; import { IReactionDisposer, ObservableMap, action, computed, makeObservable, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; @@ -14,22 +15,20 @@ import { TraceMobx } from '../../../../fields/util'; import { DocUtils, Docs } from '../../../documents/Documents'; import { DocumentManager } from '../../../util/DocumentManager'; import { UndoManager, undoable } from '../../../util/UndoManager'; +import { ContextMenu } from '../../ContextMenu'; import { ViewBoxAnnotatableComponent, ViewBoxInterface } from '../../DocComponent'; import { MarqueeAnnotator } from '../../MarqueeAnnotator'; import { SidebarAnnos } from '../../SidebarAnnos'; import { AnchorMenu } from '../../pdf/AnchorMenu'; import { GPTPopup, GPTPopupMode } from '../../pdf/GPTPopup/GPTPopup'; import { DocumentView } from '../DocumentView'; -import { FocusViewOptions, FieldView, FieldViewProps } from '../FieldView'; +import { FieldView, FieldViewProps, FocusViewOptions } from '../FieldView'; import { PinProps } from '../trails'; import './DataVizBox.scss'; import { Histogram } from './components/Histogram'; import { LineChart } from './components/LineChart'; import { PieChart } from './components/PieChart'; import { TableBox } from './components/TableBox'; -import { Checkbox } from '@mui/material'; -import { ContextMenu } from '../../ContextMenu'; -import { DragManager } from '../../../util/DragManager'; export enum DataVizView { TABLE = 'table', @@ -404,16 +403,16 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() im // represents whether or not a data viz box created from a schema table displays live updates to the canvas @action changeLiveSchemaCheckbox = () => { - this.layoutDoc.dataViz_schemaLive = !this.layoutDoc.dataViz_schemaLive - } + this.layoutDoc.dataViz_schemaLive = !this.layoutDoc.dataViz_schemaLive; + }; - // represents whether or not clicking on a peice of data in the visualization + // represents whether or not clicking on a peice of data in the visualization // (i.e. a data point in a linechart, a bar on a histogram, or a slice of a pie chart) // filters the data onto a new data viz doc created off of this one @action changeFilteringCheckbox = () => { - this.layoutDoc.dataViz_filterSelection = !this.layoutDoc.dataViz_filterSelection - } + this.layoutDoc.dataViz_filterSelection = !this.layoutDoc.dataViz_filterSelection; + }; specificContextMenu = (e: React.MouseEvent): void => { const cm = ContextMenu.Instance; @@ -421,41 +420,39 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() im const optionItems = options && 'subitems' in options ? options.subitems : []; optionItems.push({ description: `Analyze with AI`, event: () => this.askGPT(), icon: 'lightbulb' }); !options && cm.addItem({ description: 'Options...', subitems: optionItems, icon: 'eye' }); - } - + }; askGPT = action(async () => { GPTPopup.Instance.setSidebarId('data_sidebar'); GPTPopup.Instance.addDoc = this.sidebarAddDocument; GPTPopup.Instance.createFilteredDoc = this.createFilteredDoc; - GPTPopup.Instance.setDataJson(""); + GPTPopup.Instance.setDataJson(''); GPTPopup.Instance.setMode(GPTPopupMode.DATA); - let data = DataVizBox.dataset.get(CsvCast(this.dataDoc[this.fieldKey]).url.href); - let input = JSON.stringify(data); - GPTPopup.Instance.setDataJson(input); + const data = DataVizBox.dataset.get(CsvCast(this.dataDoc[this.fieldKey]).url.href); + GPTPopup.Instance.setDataJson(JSON.stringify(data)); GPTPopup.Instance.generateDataAnalysis(); }); /** * creates a new dataviz document filter from this one - * it appears to the right of this document, with the + * it appears to the right of this document, with the * parameters passed in being used to create an initial display */ - createFilteredDoc = (axes?: any, type?: DataVizView) => { + createFilteredDoc = (axes?: any) => { const embedding = Doc.MakeEmbedding(this.Document!); embedding._layout_showSidebar = false; - embedding._dataViz = type? type : DataVizView.LINECHART; + embedding._dataViz = DataVizView.LINECHART; embedding._dataViz_axes = new List(axes); embedding._dataViz_parentViz = this.Document; embedding.histogramBarColors = Field.Copy(this.layoutDoc.histogramBarColors); embedding.defaultHistogramColor = this.layoutDoc.defaultHistogramColor; embedding.pieSliceColors = Field.Copy(this.layoutDoc.pieSliceColors); embedding._layout_showSidebar = false; - embedding.width = NumCast(this.layoutDoc._width)- this.sidebarWidth(); - embedding._layout_sidebarWidthPercent = "0%"; + embedding.width = NumCast(this.layoutDoc._width) - this.sidebarWidth(); + embedding._layout_sidebarWidthPercent = '0%'; this._props.addDocument?.(embedding); - embedding._dataViz_axes = new List(axes) - this.layoutDoc.dataViz_selectedRows = new List(this.records.map((rec, i) => i)) + embedding._dataViz_axes = new List(axes); + this.layoutDoc.dataViz_selectedRows = new List(this.records.map((rec, i) => i)); embedding.x = Number(embedding.x) + Number(this.Document.width); return true; @@ -487,7 +484,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() im (this.layoutDoc._dataViz = DataVizView.PIECHART)} toggleStatus={this.layoutDoc._dataViz == -DataVizView.PIECHART} />
- {(this.layoutDoc && this.layoutDoc.dataViz_asSchema)?( + {this.layoutDoc && this.layoutDoc.dataViz_asSchema ? (
@@ -495,12 +492,12 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() im
) : null} - {this.layoutDoc._dataViz != DataVizView.TABLE?( + {this.layoutDoc._dataViz != DataVizView.TABLE ? (
Select data to filter -
) - : null } +
+ ) : null} {this.renderVizView} diff --git a/src/client/views/nodes/DataVizBox/components/TableBox.tsx b/src/client/views/nodes/DataVizBox/components/TableBox.tsx index 7ad5a0e6b..fa48d66d8 100644 --- a/src/client/views/nodes/DataVizBox/components/TableBox.tsx +++ b/src/client/views/nodes/DataVizBox/components/TableBox.tsx @@ -1,5 +1,5 @@ import { Button, Type } from 'browndash-components'; -import { IReactionDisposer, action, computed, makeObservable, observable, reaction } from 'mobx'; +import { IReactionDisposer, action, computed, makeObservable, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { Utils, emptyFunction, setupMoveUpEvents } from '../../../../../Utils'; @@ -53,13 +53,12 @@ export class TableBox extends ObservableReactComponent { makeObservable(this); } - @action componentDidMount() { // if the tableData changes (ie., when records are selected by the parent (input) visulization), // then we need to remove any selected rows that are no longer part of the visualized dataset. this._inputChangedDisposer = reaction(() => this._tableData.slice(), this.filterSelectedRowsDown, { fireImmediately: true }); const selected = NumListCast(this._props.layoutDoc.dataViz_selectedRows); - if (selected.length > 0) this.hasRowsToFilter = true; + if (selected.length > 0) runInAction(() => (this.hasRowsToFilter = true)); this.handleScroll(); } componentWillUnmount() { diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.tsx b/src/client/views/pdf/GPTPopup/GPTPopup.tsx index 40946cd36..e90504aa5 100644 --- a/src/client/views/pdf/GPTPopup/GPTPopup.tsx +++ b/src/client/views/pdf/GPTPopup/GPTPopup.tsx @@ -1,5 +1,5 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { Button, EditableText, IconButton, Size, Type } from 'browndash-components'; +import { Button, IconButton, Type } from 'browndash-components'; import { action, makeObservable, observable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; @@ -15,7 +15,6 @@ import { DocUtils, Docs } from '../../../documents/Documents'; import { ObservableReactComponent } from '../../ObservableReactComponent'; import { AnchorMenu } from '../AnchorMenu'; import './GPTPopup.scss'; -import { DataVizView } from '../../nodes/DataVizBox/DataVizBox'; export enum GPTPopupMode { SUMMARY, @@ -123,7 +122,7 @@ export class GPTPopup extends ObservableReactComponent { }; public addDoc: (doc: Doc | Doc[], sidebarKey?: string | undefined) => boolean = () => false; - public createFilteredDoc: (axes?: any, type?: DataVizView) => boolean = () => false; + public createFilteredDoc: (axes?: any) => boolean = () => false; public addToCollection: ((doc: Doc | Doc[], annotationKey?: string | undefined) => boolean) | undefined; /** -- cgit v1.2.3-70-g09d2 From 01b0e6d5023ceccbdd3301c165b28d38bf55df94 Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 15 May 2024 07:41:42 -0400 Subject: more cleanup --- src/client/util/DocumentManager.ts | 1 - .../collectionFreeForm/CollectionFreeFormView.tsx | 4 +--- .../views/nodes/formattedText/FormattedTextBox.tsx | 2 -- src/client/views/nodes/trails/PresBox.tsx | 6 +----- src/client/views/pdf/GPTPopup/GPTPopup.tsx | 19 ++++++++++--------- 5 files changed, 12 insertions(+), 20 deletions(-) (limited to 'src/client/views/pdf/GPTPopup') diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 4b4422a40..95e5f004f 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -277,7 +277,6 @@ export class DocumentManager { const childViewIterator = async (docView: DocumentView) => { const innerDoc = docContextPath.shift(); - // point of interest? return { focused: false, viewSpec: innerDoc, childDocView: innerDoc && !innerDoc.layout_unrendered ? (await docView.ComponentView?.getView?.(innerDoc, options)) ?? this.getDocumentView(innerDoc) : undefined }; }; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 45edeafb6..22448c25c 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -26,7 +26,7 @@ import { ReplayMovements } from '../../../util/ReplayMovements'; import { CompileScript } from '../../../util/Scripting'; import { ScriptingGlobals } from '../../../util/ScriptingGlobals'; import { SelectionManager } from '../../../util/SelectionManager'; -import { freeformScrollMode, SettingsManager } from '../../../util/SettingsManager'; +import { freeformScrollMode } from '../../../util/SettingsManager'; import { SnappingManager } from '../../../util/SnappingManager'; import { Transform } from '../../../util/Transform'; import { undoable, undoBatch, UndoManager } from '../../../util/UndoManager'; @@ -1055,7 +1055,6 @@ export class CollectionFreeFormView extends CollectionSubView (this.layoutDoc.layout_scrollTop = relTop * maxScrollTop), 10); newPanY = minPanY; } - // updating pan state !this.Document._verticalScroll && (this.Document[this.panXFieldKey] = this.isAnnotationOverlay ? newPanX : panX); !this.Document._horizontalScroll && (this.Document[this.panYFieldKey] = this.isAnnotationOverlay ? newPanY : panY); } @@ -1687,7 +1686,6 @@ export class CollectionFreeFormView extends CollectionSubView (this._showAnimTimeline = !this._showAnimTimeline)), icon: 'eye' }); this._props.renderDepth && optionItems.push({ description: 'Use Background Color as Default', event: () => (Cast(Doc.UserDoc().emptyCollection, Doc, null).backgroundColor = StrCast(this.layoutDoc.backgroundColor)), icon: 'palette' }); this._props.renderDepth && optionItems.push({ description: 'Fit Content Once', event: this.fitContentOnce, icon: 'object-group' }); - // this._props.renderDepth && optionItems.push({ description: 'Smart Organize', event: this.smartOrganize, icon: 'object-group' }); if (!Doc.noviceMode) { optionItems.push({ description: (!Doc.NativeWidth(this.layoutDoc) || !Doc.NativeHeight(this.layoutDoc) ? 'Freeze' : 'Unfreeze') + ' Aspect', event: this.toggleNativeDimensions, icon: 'snowflake' }); } diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 94227a927..43010b2ed 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -1015,7 +1015,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { - console.log('breakup'); if (this._editorView && this._recordingDictation) { this.stopDictation(true); this._break = true; @@ -1198,7 +1197,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent() { }, { fireImmediately: true } ); - // Casted to viewboxinterface - this._props.setContentViewBox?.(this as ViewBoxInterface); + this._props.setContentViewBox?.(this); this._unmounting = false; this.turnOffEdit(true); this._disposers.selection = reaction( @@ -497,7 +496,6 @@ export class PresBox extends ViewBoxBaseComponent() { // Called when the user activates 'next' - to move to the next part of the pres. trail @action next = () => { - console.log('next slide'); const progressiveReveal = (first: boolean) => { const presIndexed = Cast(this.activeItem?.presentation_indexed, 'number', null); if (presIndexed !== undefined) { @@ -956,7 +954,6 @@ export class PresBox extends ViewBoxBaseComponent() { */ navigateToActiveItem = (afterNav?: () => void) => { const activeItem: Doc = this.activeItem; - const targetDoc: Doc = this.targetDoc; const finished = () => { afterNav?.(); @@ -986,7 +983,6 @@ export class PresBox extends ViewBoxBaseComponent() { (DocumentManager.Instance.getFirstDocumentView(targetDoc)?.ComponentView as ScriptingBox)?.onRun?.(); return; } - console.log('pres_effect_dir', StrCast(activeItem.presentation_effectDirection)); const effect = activeItem.presentation_effect && activeItem.presentation_effect !== PresEffect.None ? activeItem.presentation_effect : undefined; // default with effect: 750ms else 500ms const presTime = NumCast(activeItem.presentation_transition, effect ? 750 : 500); diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.tsx b/src/client/views/pdf/GPTPopup/GPTPopup.tsx index e312441ed..b76d0a3f3 100644 --- a/src/client/views/pdf/GPTPopup/GPTPopup.tsx +++ b/src/client/views/pdf/GPTPopup/GPTPopup.tsx @@ -15,6 +15,7 @@ import { DocUtils, Docs } from '../../../documents/Documents'; import { ObservableReactComponent } from '../../ObservableReactComponent'; import { AnchorMenu } from '../AnchorMenu'; import './GPTPopup.scss'; +import { SettingsManager } from '../../../util/SettingsManager'; export enum GPTPopupMode { SUMMARY, @@ -256,14 +257,14 @@ export class GPTPopup extends ObservableReactComponent { dalle generation
-
))} {!this.loading && ( <> - } color={StrCast(Doc.UserDoc().userVariantColor)} /> + } color={StrCast(SettingsManager.userVariantColor)} /> )} @@ -297,8 +298,8 @@ export class GPTPopup extends ObservableReactComponent {
{this.done ? ( <> - } color={StrCast(Doc.UserDoc().userVariantColor)} /> -
@@ -398,7 +399,7 @@ export class GPTPopup extends ObservableReactComponent { heading = (headingText: string) => (
- {this.loading ? : } onClick={() => this.setVisible(false)} />} + {this.loading ? : } onClick={() => this.setVisible(false)} />}
); -- cgit v1.2.3-70-g09d2 From 5ff0bef5d3c4825aa7210a26c98aae3b24f4a835 Mon Sep 17 00:00:00 2001 From: alyssaf16 Date: Fri, 17 May 2024 13:18:40 -0400 Subject: chatcards, quizcards, and ai flashcards --- src/client/apis/gpt/GPT.ts | 36 ++++-- src/client/util/CurrentUserUtils.ts | 2 - .../views/collections/CollectionCarouselView.tsx | 79 ++++++++++-- src/client/views/collections/CollectionView.tsx | 15 ++- src/client/views/nodes/ComparisonBox.scss | 143 +++++++++++++++++++++ src/client/views/nodes/ComparisonBox.tsx | 109 +++++++++++----- src/client/views/nodes/DocumentView.tsx | 24 +++- .../views/nodes/formattedText/FormattedTextBox.tsx | 5 +- src/client/views/pdf/AnchorMenu.tsx | 27 ++-- src/client/views/pdf/GPTPopup/GPTPopup.tsx | 45 ------- src/client/views/pdf/PDFViewer.tsx | 4 +- 11 files changed, 367 insertions(+), 122 deletions(-) (limited to 'src/client/views/pdf/GPTPopup') diff --git a/src/client/apis/gpt/GPT.ts b/src/client/apis/gpt/GPT.ts index 2757fc830..6600ddab2 100644 --- a/src/client/apis/gpt/GPT.ts +++ b/src/client/apis/gpt/GPT.ts @@ -1,10 +1,13 @@ import { ClientOptions, OpenAI } from 'openai'; +import { ChatCompletionMessageParam } from 'openai/resources'; enum GPTCallType { SUMMARY = 'summary', COMPLETION = 'completion', EDIT = 'edit', + CHATCARD = 'chatcard', FLASHCARD = 'flashcard', + QUIZ = 'quiz', } type GPTCallOpts = { @@ -15,10 +18,17 @@ type GPTCallOpts = { }; const callTypeMap: { [type: string]: GPTCallOpts } = { - summary: { model: 'gpt-3.5-turbo-instruct', maxTokens: 256, temp: 0.5, prompt: 'Summarize this text in simpler terms: ' }, - edit: { model: 'gpt-3.5-turbo-instruct', maxTokens: 256, temp: 0.5, prompt: 'Reword this: ' }, - flashcard: { model: 'gpt-3.5-turbo-instruct', maxTokens: 512, temp: 0.5, prompt: 'Make flashcards out of this text with questions and answers: ' }, - completion: { model: 'gpt-3.5-turbo-instruct', maxTokens: 256, temp: 0.5, prompt: '' }, + summary: { model: 'gpt-4-turbo', maxTokens: 256, temp: 0.5, prompt: 'Summarize this text in simpler terms: ' }, + edit: { model: 'gpt-4-turbo', maxTokens: 256, temp: 0.5, prompt: 'Reword this: ' }, + flashcard: { model: 'gpt-4-turbo', maxTokens: 512, temp: 0.5, prompt: 'Make flashcards out of this text with each question and answer labeled. Do not label each flashcard and do not include asterisks: ' }, + completion: { model: 'gpt-4-turbo', maxTokens: 256, temp: 0.5, prompt: '' }, + 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.' }, + quiz: { + model: 'gpt-4-turbo', + maxTokens: 1024, + temp: 0, + prompt: 'List unique differences between the content of the UserAnswer and Rubric. Before each difference, label it and provide any additional information the UserAnswer missed and explain it in second person without separating it into UserAnswer and Rubric content and additional information. If there are no differences, say correct', + }, }; /** @@ -28,7 +38,8 @@ const callTypeMap: { [type: string]: GPTCallOpts } = { * @returns AI Output */ const gptAPICall = async (inputText: string, callType: GPTCallType) => { - if (callType === GPTCallType.SUMMARY || callType == GPTCallType.FLASHCARD) inputText += '.'; + if (!inputText) return 'Please provide a response.'; + if (callType === GPTCallType.SUMMARY || callType == GPTCallType.FLASHCARD || GPTCallType.QUIZ) inputText += '.'; const opts: GPTCallOpts = callTypeMap[callType]; try { const configuration: ClientOptions = { @@ -36,13 +47,20 @@ const gptAPICall = async (inputText: string, callType: GPTCallType) => { dangerouslyAllowBrowser: true, }; const openai = new OpenAI(configuration); - const response = await openai.completions.create({ + + let messages: ChatCompletionMessageParam[] = [ + { role: 'system', content: opts.prompt }, + { role: 'user', content: inputText }, + ]; + + const response = await openai.chat.completions.create({ model: opts.model, - max_tokens: opts.maxTokens, + messages: messages, temperature: opts.temp, - prompt: `${opts.prompt}${inputText}`, + max_tokens: opts.maxTokens, }); - return response.choices[0].text; + + return response.choices[0].message.content; } catch (err) { console.log(err); return 'Error connecting with API.'; diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index ab0315ba1..b6ba3f187 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -342,8 +342,6 @@ pie title Minerals in my tap water }[] = [ {key: "Note", creator: opts => Docs.Create.TextDocument("", opts), opts: { _width: 200, _layout_autoHeight: true }}, {key: "Flashcard", creator: opts => Docs.Create.ComparisonDocument("", opts), opts: { _layout_isFlashcard: true, _width: 300, _height: 300}}, - // {key: "Flashcard", creator: Docs.Create.ComparisonDocument("", opts), opts: { _layout_isFlashcard: true, _width: 300, _height: 300 }}, - //{key: "Flashcard", creator: opts => Docs.Create.TextDocument("", opts), opts: { _width: 200, _layout_autoHeight: true, _layout_enableAltContentUI: true}}, {key: "Equation", creator: opts => Docs.Create.EquationDocument("",opts), opts: { _width: 300, _height: 35, }}, {key: "Noteboard", creator: opts => Docs.Create.NoteTakingDocument([], opts), opts: { _width: 250, _height: 200, _layout_fitWidth: true}}, {key: "Simulation", creator: opts => Docs.Create.SimulationDocument(opts), opts: { _width: 300, _height: 300, }}, diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx index 7f5176123..d45b0822b 100644 --- a/src/client/views/collections/CollectionCarouselView.tsx +++ b/src/client/views/collections/CollectionCarouselView.tsx @@ -33,29 +33,53 @@ export class CollectionCarouselView extends CollectionSubView() { } }; + /** + * Goes to the next flashcard in the stack and filters + * based on the the currently selected option. + */ advance = (e: React.MouseEvent) => { e.stopPropagation(); this.layoutDoc._carousel_index = (NumCast(this.layoutDoc._carousel_index) + 1) % this.childLayoutPairs.length; var startInd = this.layoutDoc._carousel_index; // if the star filter is selected - if (this.layoutDoc[`_${this._props.fieldKey}_filterOp`] == 'star') { - // go to a new index that is starred, skip the ones that aren't + if (this.layoutDoc[`filterOp`] == 'star') { + // go to a flashcard that is starred, skip the ones that aren't while (!this.childLayoutPairs?.[NumCast(startInd)].layout[`${this.fieldKey}_star`] && (startInd + 1) % this.childLayoutPairs.length != this.layoutDoc._carousel_index) { startInd = (startInd + 1) % this.childLayoutPairs.length; } this.layoutDoc._carousel_index = startInd; + // if there aren't any starred, show all cards + if (!this.childLayoutPairs?.[NumCast(startInd)].layout[`${this.fieldKey}_star`]) { + this.layoutDoc[`filterOp`] = 'all'; + } } // if the practice filter is selected - if (this.layoutDoc[`_${this._props.fieldKey}_filterOp`] == 'practice') { + if (this.layoutDoc[`filterOp`] == 'practice') { // go to a new index that is missed, skip the ones that are correct while (this.childLayoutPairs?.[NumCast(startInd)].layout[`${this.fieldKey}_missed`] == 'correct' && (startInd + 1) % this.childLayoutPairs.length != this.layoutDoc._carousel_index) { startInd = (startInd + 1) % this.childLayoutPairs.length; } this.layoutDoc._carousel_index = startInd; + + // if the user has gone through all of the cards and gotten them all correct, show all cards and exit practice mode + if (this.childLayoutPairs?.[NumCast(startInd)].layout[`${this.fieldKey}_missed`] == 'correct') { + this.layoutDoc[`filterOp`] = 'all'; + + // set all the cards to missed + for (var i = 0; i < this.childLayoutPairs.length; i++) { + const curDoc = this.childLayoutPairs?.[NumCast(i)]; + curDoc.layout[`${this.fieldKey}_missed`] = undefined; + } + } } }; + + /** + * Goes to the previous flashcard in the stack and filters + * based on the the currently selected option. + */ goback = (e: React.MouseEvent) => { e.stopPropagation(); this.layoutDoc._carousel_index = (NumCast(this.layoutDoc._carousel_index) - 1 + this.childLayoutPairs.length) % this.childLayoutPairs.length; @@ -63,38 +87,58 @@ export class CollectionCarouselView extends CollectionSubView() { var startInd = this.layoutDoc._carousel_index; // if the star filter is selected - if (this.layoutDoc[`_${this._props.fieldKey}_filterOp`] == 'star') { + if (this.layoutDoc[`filterOp`] == 'star') { // go to a new index that is starred, skip the ones that aren't while (!this.childLayoutPairs?.[NumCast(startInd)].layout[`${this.fieldKey}_star`] && (startInd - 1 + this.childLayoutPairs.length) % this.childLayoutPairs.length != this.layoutDoc._carousel_index) { startInd = (startInd - 1 + this.childLayoutPairs.length) % this.childLayoutPairs.length; } this.layoutDoc._carousel_index = startInd; + // if there aren't any starred, show all cards + if (!this.childLayoutPairs?.[NumCast(startInd)].layout[`${this.fieldKey}_star`]) { + this.layoutDoc[`filterOp`] = 'all'; + } } // if the practice filter is selected - if (this.layoutDoc[`_${this._props.fieldKey}_filterOp`] == 'practice') { + if (this.layoutDoc[`filterOp`] == 'practice') { // go to a new index that is missed, skip the ones that are correct while (this.childLayoutPairs?.[NumCast(startInd)].layout[`${this.fieldKey}_missed`] == 'correct' && (startInd - 1 + this.childLayoutPairs.length) % this.childLayoutPairs.length != this.layoutDoc._carousel_index) { startInd = (startInd - 1 + this.childLayoutPairs.length) % this.childLayoutPairs.length; } + this.layoutDoc._carousel_index = startInd; + + // See all flashcards when finish going through practice mode and set all of the flashcards back to + if (this.childLayoutPairs?.[NumCast(startInd)].layout[`${this.fieldKey}_missed`] == 'correct') { + this.layoutDoc[`filterOp`] = 'all'; + + for (var i = 0; i < this.childLayoutPairs.length; i++) { + const curDoc = this.childLayoutPairs?.[NumCast(i)]; + curDoc.layout[`${this.fieldKey}_missed`] = undefined; + } + } } }; + /* + * Stars the document when the star button is pressed. + */ star = (e: React.MouseEvent) => { e.stopPropagation(); - // stars the document when the button is pressed const curDoc = this.childLayoutPairs?.[NumCast(this.layoutDoc._carousel_index)]; + if (!curDoc) return; if (curDoc.layout[`${this.fieldKey}_star`] == undefined) curDoc.layout[`${this.fieldKey}_star`] = true; else curDoc.layout[`${this.fieldKey}_star`] = !curDoc.layout[`${this.fieldKey}_star`]; }; + /* + * Sets a flashcard to either missed or correct depending on if they got the question right in practice mode. + */ missed = (e: React.MouseEvent, val: string) => { e.stopPropagation(); const curDoc = this.childLayoutPairs?.[NumCast(this.layoutDoc._carousel_index)]; curDoc.layout[`${this.fieldKey}_missed`] = val; - this.layoutDoc._carousel_index = (NumCast(this.layoutDoc._carousel_index) + 1) % this.childLayoutPairs.length; - this.advance; + this.advance(e); }; captionStyleProvider = (doc: Doc | undefined, captionProps: Opt, property: string): any => { @@ -154,6 +198,7 @@ export class CollectionCarouselView extends CollectionSubView() { ); } @computed get buttons() { + if (!this.childLayoutPairs?.[NumCast(this.layoutDoc._carousel_index)]) return; return ( <>
@@ -165,10 +210,10 @@ export class CollectionCarouselView extends CollectionSubView() {
-
this.missed(e, 'missed')} style={{ visibility: this.layoutDoc[`_${this._props.fieldKey}_filterOp`] == 'practice' ? 'visible' : 'hidden' }}> +
this.missed(e, 'missed')} style={{ visibility: this.layoutDoc[`filterOp`] == 'practice' ? 'visible' : 'hidden' }}>
-
this.missed(e, 'correct')} style={{ visibility: this.layoutDoc[`_${this._props.fieldKey}_filterOp`] == 'practice' ? 'visible' : 'hidden' }}> +
this.missed(e, 'correct')} style={{ visibility: this.layoutDoc[`filterOp`] == 'practice' ? 'visible' : 'hidden' }}>
@@ -185,6 +230,18 @@ export class CollectionCarouselView extends CollectionSubView() { color: this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Color), }}> {this.content} + {/* Displays a message to the user to add more flashcards if they are in practice mode and no flashcards are there. */} +

+ Add flashcards! +

+ {/* Displays a message to the user that a flashcard was recently missed if they had previously gotten it wrong. */}

Recently missed!

diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 168176edf..2f0f2a773 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -80,6 +80,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent (this.layoutDoc[`_${this._props.fieldKey}_filterOp`] = 'all'), icon: 'eye-slash' }); - revealItems.push({ description: 'Star', event: () => (this.layoutDoc[`_${this._props.fieldKey}_filterOp`] = 'star'), icon: 'hand-point-up' }); - revealItems.push({ description: 'Practice Mode', event: () => (this.layoutDoc[`_${this._props.fieldKey}_filterOp`] = 'practice'), icon: 'rotate' }); + revealItems.push({ description: 'All', event: () => (this.layoutDoc[`filterOp`] = 'all'), icon: 'layer-group' }); + revealItems.push({ description: 'Star', event: () => (this.layoutDoc[`filterOp`] = 'star'), icon: 'star' }); + revealItems.push({ description: 'Practice Mode', event: () => (this.layoutDoc[`filterOp`] = 'practice'), icon: 'check' }); + revealItems.push({ description: 'Quiz Cards', event: () => (this.layoutDoc[`filterOp`] = 'quiz'), icon: 'pencil' }); - //revealItems.push({ description: 'Bring to Front', event: () => SelectionManager.Views.forEach(dv => dv._props.bringToFront?.(dv.Document, false)), icon: 'arrow-up' }); - !revealOptions && cm.addItem({ description: 'Filter Flashcards', addDivider: false, noexpand: true, subitems: revealItems, icon: 'layer-group' }); + // only show the filter options if it is a collection of type Carousel view + if (this.Document?._type_collection === CollectionViewType.Carousel) { + !revealOptions && cm.addItem({ description: 'Filter Flashcards', addDivider: false, noexpand: true, subitems: revealItems, icon: 'layer-group' }); + } const options = cm.findByDescription('Options...'); const optionItems = options && 'subitems' in options ? options.subitems : []; diff --git a/src/client/views/nodes/ComparisonBox.scss b/src/client/views/nodes/ComparisonBox.scss index 39c864b2b..093b9c004 100644 --- a/src/client/views/nodes/ComparisonBox.scss +++ b/src/client/views/nodes/ComparisonBox.scss @@ -1,4 +1,5 @@ .comparisonBox-interactive, +.quiz-card, .comparisonBox { border-radius: inherit; width: 100%; @@ -7,6 +8,40 @@ z-index: 0; pointer-events: none; display: flex; + p { + color: rgb(0, 0, 0); + -webkit-text-stroke-color: black; + -webkit-text-stroke-width: 0.2px; + } + + .input-box { + position: relative; + padding: 10px; + width: 100%; + height: 100%; + display: flex; + } + + .submit-button { + position: relative; + padding-bottom: 10px; + padding-left: 5px; + padding-right: 5px; + width: 100%; + height: 15%; + display: flex; + + button { + flex: 1; + position: relative; + } + } + textarea { + flex: 1; + padding: 10px; + position: relative; + resize: none; + } .clip-div { position: absolute; @@ -95,4 +130,112 @@ display: flex; } } + // .input-box { + // position: relative; + // padding: 10px; + // } + // input[type='text'] { + // flex: 1; + // position: relative; + // margin-right: 10px; + // width: 100px; + // } +} + +// .quiz-card { +// position: relative; + +// input[type='text'] { +// flex: 1; +// position: relative; +// margin-right: 10px; +// width: 100px; +// } +// } +.QuizCard { + width: 100%; + height: 100%; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + + .QuizCard-wrapper { + width: 100%; + height: 100%; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + .QuizCardBox { + /* existing code */ + + .DIYNodeBox-iframe { + height: 100%; + width: 100%; + border: none; + } + } + + .search-bar { + display: flex; + justify-content: left; + align-items: left; + width: 100%; + padding: 10px; + + input[type='text'] { + flex: 1; + margin-right: 10px; + } + + button { + padding: 5px 10px; + } + } + + .content { + flex: 1; + display: flex; + justify-content: center; + align-items: center; + width: 100%; + height: 100%; + .diagramBox { + flex: 1; + display: flex; + justify-content: center; + align-items: center; + width: 100%; + height: 100%; + svg { + flex: 1; + display: flex; + justify-content: center; + align-items: center; + width: 100%; + height: 100%; + } + } + } + + .loading-circle { + position: relative; + width: 50px; + height: 50px; + border-radius: 50%; + border: 3px solid #ccc; + border-top-color: #333; + animation: spin 1s infinite linear; + } + + @keyframes spin { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } + } + } } diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx index 19fccce8a..9fd4d696a 100644 --- a/src/client/views/nodes/ComparisonBox.tsx +++ b/src/client/views/nodes/ComparisonBox.tsx @@ -2,7 +2,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, makeObservable, observable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; -import { emptyFunction, returnFalse, returnNone, returnZero, setupMoveUpEvents } from '../../../Utils'; +import { emptyFunction, returnFalse, returnNone, returnZero, setupMoveUpEvents, unimplementedFunction } from '../../../Utils'; import { Doc, Opt, DocListCast } from '../../../fields/Doc'; import { DocCast, NumCast, RTFCast, StrCast } from '../../../fields/Types'; import { DocUtils, Docs } from '../../documents/Documents'; @@ -34,6 +34,17 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent() makeObservable(this); } + @observable inputValue = ''; + @observable outputValue = ''; + @observable loading = false; + @observable errorMessage = ''; + @observable outputMessage = ''; + + @action handleInputChange = (e: React.ChangeEvent) => { + this.inputValue = e.target.value; + console.log(this.inputValue); + }; + @observable _animating = ''; @computed get clipWidth() { @@ -160,7 +171,6 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent() e => { const de = new DragManager.DocumentDragData([DocCast(this.dataDoc[which])], dropActionType.move); de.moveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean): boolean => { - //this.clearDoc(which); return addDocument(doc); }; de.canEmbed = true; @@ -181,15 +191,24 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent() remDoc2 = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((res, doc) => res && this.remDoc(doc, this.fieldKey + '_2'), true); _closeRef = React.createRef(); + /** + * Flips a flashcard to the alternate side for the user to view. + */ flipFlashcard = () => { const usePath = this.layoutDoc[`_${this._props.fieldKey}_usePath`]; - this.layoutDoc[`_${this._props.fieldKey}_usePath`] = usePath === undefined ? 'alternate' : undefined; }; + + /** + * Changes the view option to hover for a flashcard. + */ hoverFlip = (side: string | undefined) => { if (this.layoutDoc[`_${this._props.fieldKey}_revealOp`] == 'hover') this.layoutDoc[`_${this._props.fieldKey}_usePath`] = side; }; + /** + * Creates the button used to flip the flashcards. + */ @computed get overlayAlternateIcon() { const usepath = this.layoutDoc[`_${this._props.fieldKey}_usePath`]; return ( @@ -203,21 +222,10 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent() this.flipFlashcard(); console.log('Print Front of cards: ' + RTFCast(DocCast(this.dataDoc[this.fieldKey + '_0']).text)?.Text); console.log('Print Back of cards: ' + RTFCast(DocCast(this.dataDoc[this.fieldKey + '_1']).text)?.Text); - - //const queryText = RTFCast(DocCast(this.dataDoc[this.fieldKey + '_0']).text)?.Text; - // DocCast(this.dataDoc[this.fieldKey + '_1'])[DocData].text = 'hello'; - // const mes = gptAPICall(queryText, GPTCallType.COMPLETION).trim(); - // const res = await gptAPICall(queryText, GPTCallType.COMPLETION) - // console.log(res); - //.then(value => (DocCast(this.dataDoc[this.fieldKey + '_1']).text = value.trim())); - if (usepath !== 'alternate') { - this.askGPT(); - } } }) } style={{ - //display: this._props.isContentActive() && !SnappingManager.IsDragging ? 'flex' : 'none', background: usepath === 'alternate' ? 'white' : 'black', color: usepath === 'alternate' ? 'black' : 'white', }}> @@ -227,15 +235,34 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent() ); } + @action handleRenderGPTClick = () => { + // Call the GPT model and get the output + this.layoutDoc[`_${this._props.fieldKey}_usePath`] = 'alternate'; + this.outputValue = ''; + if (this.inputValue) this.askGPT(); + }; + + @action handleRenderClick = () => { + // Call the GPT model and get the output + this.layoutDoc[`_${this._props.fieldKey}_usePath`] = undefined; + }; + + /** + * Calls the GPT model to create QuizCards. Evaluates how similar the user's response is to the alternate + * side of the flashcard. + */ askGPT = async (): Promise => { - const queryText = RTFCast(DocCast(this.dataDoc[this.fieldKey + '_1']).text)?.Text; + const questionText = 'Question: ' + StrCast(RTFCast(DocCast(this.dataDoc[this.fieldKey + '_1']).text)?.Text); + const rubricText = ' Rubric: ' + StrCast(RTFCast(DocCast(this.dataDoc[this.fieldKey + '_0']).text)?.Text); + const queryText = questionText + ' UserAnswer: ' + this.inputValue + '. ' + rubricText; + try { - let res = await gptAPICall(StrCast(queryText), GPTCallType.COMPLETION); + let res = await gptAPICall(queryText, GPTCallType.QUIZ); if (!res) { console.error('GPT call failed'); return; } - DocCast(this.dataDoc[this.fieldKey + '_0'])[DocData].text = res; + this.outputValue = res; console.log(res); } catch (err) { console.error('GPT call failed'); @@ -292,8 +319,6 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent() ); }; const displayBox = (which: string, index: number, cover: number) => { - // if (this.layoutDoc[`_${this._props.fieldKey}_revealOp`] == 'hide/reveal') this.layoutDoc[this.clipHeightKey] = 100; - // else this.layoutDoc.height = 300; return (
this.registerSliding(e, cover)} ref={ele => this.createDropTarget(ele, which, index)}> {displayDoc(which)} @@ -326,36 +351,54 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent() if (this.Document._layout_isFlashcard) { const side = this.layoutDoc[`_${this._props.fieldKey}_usePath`] === 'alternate' ? 1 : 0; - // add text box when first created + // add text box to each side when comparison box is first created if (!(this.dataDoc[this.fieldKey + '_0'] || this.dataDoc[this.fieldKey + '_0'] == 'empty')) { const dataSplit = StrCast(this.dataDoc.data).split('Answer'); const newDoc = Docs.Create.TextDocument(dataSplit[1]); + // if there is text from the pdf ai cards, put the question on the front side. newDoc[DocData].text = dataSplit[1]; this.addDoc(newDoc, this.fieldKey + '_0'); } if (!(this.dataDoc[this.fieldKey + '_1'] || this.dataDoc[this.fieldKey + '_1'] == 'empty')) { const dataSplit = StrCast(this.dataDoc.data).split('Answer'); const newDoc = Docs.Create.TextDocument(dataSplit[0]); - newDoc[DocData].text = 'placeholder...'; + // if there is text from the pdf ai cards, put the answer on the alternate side. + newDoc[DocData].text = dataSplit[0]; this.addDoc(newDoc, this.fieldKey + '_1'); } - if (this.layoutDoc[`_${this._props.fieldKey}_revealOp`] == 'hide/reveal') { - { - return ( -
- {displayBox(`${this.fieldKey}_0`, side, this._props.PanelHeight() - 3)} - {displayBox(`${this.fieldKey}_1`, 1, this._props.PanelHeight() - 3)} + // render the QuizCards + if (DocCast(this.Document.embedContainer) && DocCast(this.Document.embedContainer)[`filterOp`] == 'quiz') { + return ( +
+

{StrCast(RTFCast(DocCast(this.dataDoc[this.fieldKey + '_1']).text)?.Text)}

+ {/* {StrCast(RTFCast(DocCast(this.dataDoc[this.fieldKey + '_1']).text)?.Text)} */} +
+ { +