From 585f03bf45df4ac7ed61d22c9dbe10d8e453199d Mon Sep 17 00:00:00 2001
From: alyssaf16
Date: Wed, 5 Jun 2024 15:06:15 -0400
Subject: Flashcards changes
---
src/client/views/nodes/ComparisonBox.scss | 61 +++++++++++++++++++++----------
1 file changed, 42 insertions(+), 19 deletions(-)
(limited to 'src/client/views/nodes/ComparisonBox.scss')
diff --git a/src/client/views/nodes/ComparisonBox.scss b/src/client/views/nodes/ComparisonBox.scss
index 093b9c004..f3dc0f68c 100644
--- a/src/client/views/nodes/ComparisonBox.scss
+++ b/src/client/views/nodes/ComparisonBox.scss
@@ -5,6 +5,7 @@
width: 100%;
height: 100%;
position: relative;
+ background: gray;
z-index: 0;
pointer-events: none;
display: flex;
@@ -28,6 +29,7 @@
padding-left: 5px;
padding-right: 5px;
width: 100%;
+ border-radius: 2px;
height: 15%;
display: flex;
@@ -39,8 +41,12 @@
textarea {
flex: 1;
padding: 10px;
- position: relative;
+ // position: relative;
resize: none;
+ position: 'absolute';
+ width: '91%';
+ height: '80%';
+ z-index: '-1';
}
.clip-div {
@@ -119,6 +125,23 @@
opacity: 0.5;
}
}
+
+ .loading-spinner {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: 90%;
+ width: 93%;
+ font-size: 20px;
+ font-weight: bold;
+ color: #0b0a0a;
+ }
+
+ @keyframes spin {
+ to {
+ transform: rotate(360deg);
+ }
+ }
}
.comparisonBox-interactive {
@@ -131,7 +154,7 @@
}
}
// .input-box {
- // position: relative;
+ // position: absolute;
// padding: 10px;
// }
// input[type='text'] {
@@ -219,23 +242,23 @@
}
}
- .loading-circle {
- position: relative;
- width: 50px;
- height: 50px;
- border-radius: 50%;
- border: 3px solid #ccc;
- border-top-color: #333;
- animation: spin 1s infinite linear;
- }
+ // .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);
- }
- }
+ // @keyframes spin {
+ // 0% {
+ // transform: rotate(0deg);
+ // }
+ // 100% {
+ // transform: rotate(360deg);
+ // }
+ // }
}
}
--
cgit v1.2.3-70-g09d2
From 7594f649890d27d558be35c9d40a0aa8211aec67 Mon Sep 17 00:00:00 2001
From: alyssaf16
Date: Thu, 6 Jun 2024 22:20:24 -0400
Subject: Flashcard changes - menu added
---
src/client/views/nodes/ComparisonBox.scss | 20 +----
src/client/views/nodes/ComparisonBox.tsx | 99 +++++++++++++++++-----
src/client/views/nodes/DocumentView.tsx | 3 +-
src/client/views/nodes/FieldView.tsx | 1 +
.../nodes/formattedText/FormattedTextBox.scss | 6 +-
.../views/nodes/formattedText/FormattedTextBox.tsx | 1 +
6 files changed, 87 insertions(+), 43 deletions(-)
(limited to 'src/client/views/nodes/ComparisonBox.scss')
diff --git a/src/client/views/nodes/ComparisonBox.scss b/src/client/views/nodes/ComparisonBox.scss
index f3dc0f68c..dc107b576 100644
--- a/src/client/views/nodes/ComparisonBox.scss
+++ b/src/client/views/nodes/ComparisonBox.scss
@@ -47,6 +47,7 @@
width: '91%';
height: '80%';
z-index: '-1';
+ overscroll-behavior: contain;
}
.clip-div {
@@ -241,24 +242,5 @@
}
}
}
-
- // .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 6d4af2f3e..f844892c5 100644
--- a/src/client/views/nodes/ComparisonBox.tsx
+++ b/src/client/views/nodes/ComparisonBox.tsx
@@ -1,9 +1,9 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Tooltip } from '@mui/material';
-import { action, computed, makeObservable, observable } from 'mobx';
+import { action, computed, makeObservable, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
-import { returnFalse, returnNone, setupMoveUpEvents } from '../../../ClientUtils';
+import { returnFalse, returnNone, returnTrue, setupMoveUpEvents } from '../../../ClientUtils';
import { emptyFunction } from '../../../Utils';
import { Doc, Opt } from '../../../fields/Doc';
import { DocData } from '../../../fields/DocSymbols';
@@ -27,6 +27,7 @@ import { FormattedTextBox } from './formattedText/FormattedTextBox';
import ReactLoading from 'react-loading';
import { ContextMenu } from '../ContextMenu';
import { ContextMenuProps } from '../ContextMenuItem';
+import { tickStep } from 'd3';
@observer
export class ComparisonBox extends ViewBoxAnnotatableComponent() {
@@ -37,7 +38,6 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
constructor(props: FieldViewProps) {
super(props);
makeObservable(this);
- // this._isAnyChildContentActive = true;
}
@observable private _inputValue = '';
@@ -63,12 +63,19 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
@computed get clipHeight() {
return NumCast(this.layoutDoc[this.clipHeightKey], 200);
}
+ get revealOp() {
+ return this.layoutDoc[`_${this._props.fieldKey}_revealOp`];
+ }
get clipHeightKey() {
return '_' + this._props.fieldKey + '_clipHeight';
}
componentDidMount() {
this._props.setContentViewBox?.(this);
+ reaction(
+ () => this._props.isSelected(),
+ selected => !selected && (this.childActive = false)
+ );
}
protected createDropTarget = (ele: HTMLDivElement | null, fieldKey: string, disposerId: number) => {
this._disposers[disposerId]?.();
@@ -98,7 +105,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
emptyFunction,
action((moveEv, doubleTap) => {
if (doubleTap) {
- this._isAnyChildContentActive = true;
+ this.childActive = true;
if (!this.dataDoc[this.fieldKey + '_1'] && !this.dataDoc[this.fieldKey]) this.dataDoc[this.fieldKey + '_1'] = DocUtils.copyDragFactory(Doc.UserDoc().emptyNote as Doc);
if (!this.dataDoc[this.fieldKey + '_2'] && !this.dataDoc[this.fieldKey + '_alternate']) this.dataDoc[this.fieldKey + '_2'] = DocUtils.copyDragFactory(Doc.UserDoc().emptyNote as Doc);
}
@@ -106,7 +113,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
false,
undefined,
action(() => {
- if (this._isAnyChildContentActive) return;
+ if (this.childActive) return;
this._animating = 'all 200ms';
// on click, animate slider movement to the targetWidth
this.layoutDoc[this.clipWidthKey] = (targetWidth * 100) / this._props.PanelWidth();
@@ -247,7 +254,6 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
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.
*/
@@ -259,7 +265,6 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
className="formattedTextBox-alternateButton"
onPointerDown={e =>
setupMoveUpEvents(e.target, e, returnFalse, emptyFunction, () => {
- console.log(this.layoutDoc[`_${this._props.fieldKey}_revealOp`]);
if (!this.layoutDoc[`_${this._props.fieldKey}_revealOp`] || this.layoutDoc[`_${this._props.fieldKey}_revealOp`] === 'flip') {
this.flipFlashcard();
@@ -271,6 +276,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
style={{
background: usepath === 'alternate' ? 'white' : 'black',
color: usepath === 'alternate' ? 'black' : 'white',
+ display: 'inline-block',
}}>
@@ -280,6 +286,37 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent
()
);
}
+ @computed get flashcardMenu() {
+ return (
+
+ Flip to front side to use GPT
+ ) : (
+ Ask GPT to create an answer on the back side of the flashcard
+ )
+ }>
+ (!this.layoutDoc[`_${this._props.fieldKey}_usePath`] ? this.askGPT(GPTCallType.CHATCARD) : null)}>
+
+
+
+ Hover to reveal }>
+ (this.revealOp === 'hover' ? (this.layoutDoc[`_${this._props.fieldKey}_revealOp`] = 'flip') : (this.layoutDoc[`_${this._props.fieldKey}_revealOp`] = 'hover'))}>
+
+
+
+ {this.overlayAlternateIcon}
+
+ );
+ }
+
+ @action activateContent = () => {
+ this.childActive = true;
+ };
+
@action handleRenderGPTClick = () => {
// Call the GPT model and get the output
this.layoutDoc[`_${this._props.fieldKey}_usePath`] = 'alternate';
@@ -320,8 +357,12 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
const rubricText = ' Rubric: ' + StrCast(RTFCast(DocCast(this.dataDoc[this.fieldKey + '_0']).text)?.Text);
const queryText = questionText + ' UserAnswer: ' + this._inputValue + '. ' + rubricText;
this._loading = true;
+ const doc = DocCast(this.dataDoc[this.props.fieldKey + '_0']);
if (callType == GPTCallType.CHATCARD) {
- DocCast(this.dataDoc[this.props.fieldKey + '_0'])[DocData].text = '';
+ if (StrCast(RTFCast(DocCast(this.dataDoc[this.fieldKey + '_1']).text)?.Text) === '') {
+ this._loading = false;
+ return;
+ }
this.flipFlashcard();
}
try {
@@ -330,7 +371,13 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
console.error('GPT call failed');
return;
}
- this.animateRes(0, res, callType);
+ // this.animateRes(0, res, callType);
+ if (callType == GPTCallType.CHATCARD) {
+ DocCast(this.dataDoc[this.props.fieldKey + '_0'])[DocData].text = res;
+ // this.flipFlashcard();
+ }
+ if (callType == GPTCallType.QUIZ) this._outputValue = res;
+ // DocCast(this.dataDoc[this.props.fieldKey + '_0'])[DocData].text = res;
// this._outputValue = res;
console.log(res);
} catch (err) {
@@ -341,10 +388,11 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
layoutWidth = () => NumCast(this.layoutDoc.width, 200);
layoutHeight = () => NumCast(this.layoutDoc.height, 200);
- specificMenu = (): void => {
- const cm = ContextMenu.Instance;
- cm.addItem({ description: 'Create an Answer on the Back', event: () => this.askGPT(GPTCallType.CHATCARD), icon: 'pencil' });
- };
+ // specificMenu = (): void => {
+ // const cm = ContextMenu.Instance;
+ // cm.addItem({ description: 'Create an Answer on the Back', event: () => this.askGPT(GPTCallType.CHATCARD), icon: 'pencil' });
+ // };
+ @observable childActive = false;
render() {
const clearButton = (which: string) => (
@@ -378,12 +426,13 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
removeDocument={whichSlot.endsWith('1') ? this.remDoc1 : this.remDoc2}
NativeWidth={this.layoutWidth}
NativeHeight={this.layoutHeight}
- isContentActive={emptyFunction}
+ isContentActive={() => this.childActive}
isDocumentActive={returnFalse}
+ dontSelect={returnTrue}
whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
- styleProvider={this._isAnyChildContentActive ? this._props.styleProvider : this.docStyleProvider}
+ styleProvider={this.childActive ? this._props.styleProvider : this.docStyleProvider}
hideLinkButton
- pointerEvents={this._isAnyChildContentActive ? undefined : returnNone}
+ pointerEvents={this.childActive ? undefined : returnNone}
/>
{layoutString ? null : clearButton(whichSlot)}
> // placeholder image if doc is missingleft: `${NumCast(this.layoutDoc.width, 200) - 33}px`
@@ -394,7 +443,15 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
);
};
const displayBox = (which: string, index: number, cover: number) => (
- this.registerSliding(e, cover)} ref={ele => this.createDropTarget(ele, which, index)}>
+
{
+ this.registerSliding(e, cover);
+ this.activateContent();
+ }}
+ ref={ele => this.createDropTarget(ele, which, index)}>
{displayDoc(which)}
);
@@ -431,6 +488,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent
()
{this._loading ? (
@@ -457,7 +515,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
return (
{
this.hoverFlip('alternate');
@@ -473,7 +531,8 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
) : null}
- {this.overlayAlternateIcon}
+ {this.flashcardMenu}
+ {/* {this.overlayAlternateIcon} */}
);
}
@@ -491,7 +550,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
left: `calc(${this.clipWidth + '%'} - 0.5px)`,
cursor: this.clipWidth < 5 ? 'e-resize' : this.clipWidth / 100 > (this._props.PanelWidth() - 5) / this._props.PanelWidth() ? 'w-resize' : undefined,
}}
- onPointerDown={e => !this._isAnyChildContentActive && this.registerSliding(e, this._props.PanelWidth() / 2)} /* if clicked, return slide-bar to center */
+ onPointerDown={e => !this.childActive && this.registerSliding(e, this._props.PanelWidth() / 2)} /* if clicked, return slide-bar to center */
>
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index a25249eac..2f3357791 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -503,6 +503,7 @@ export class DocumentViewInternal extends DocComponent {
+ if (this._props.dontSelect?.()) return;
if (e && this.layoutDoc.layout_hideContextMenu && Doc.noviceMode) {
e.preventDefault();
e.stopPropagation();
@@ -1378,7 +1379,7 @@ export class DocumentView extends DocComponent() {
screenToLocalScale = () => this._props.ScreenToLocalTransform().Scale;
isSelected = () => this.IsSelected;
select = (extendSelection: boolean, focusSelection?: boolean) => {
- DocumentView.SelectView(this, extendSelection);
+ if (!this._props.dontSelect?.()) DocumentView.SelectView(this, extendSelection);
if (focusSelection) {
DocumentView.showDocument(this.Document, {
willZoomCentered: true,
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 818d26956..138f00492 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -50,6 +50,7 @@ export interface FieldViewSharedProps {
PanelHeight: () => number;
isDocumentActive?: () => boolean | undefined; // whether a document should handle pointer events
isContentActive: () => boolean | undefined; // whether document contents should handle pointer events
+ dontSelect: () => boolean | undefined;
childFilters: () => string[];
childFiltersByRanges: () => string[];
styleProvider: Opt;
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.scss b/src/client/views/nodes/formattedText/FormattedTextBox.scss
index 8ac8c2c5f..54643b4a5 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.scss
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.scss
@@ -89,14 +89,14 @@ audiotag:hover {
right: 0;
bottom: 0;
width: 15;
- height: 15;
+ height: 22;
cursor: default;
}
.formattedTextBox-flip {
align-items: center;
position: absolute;
- right: 3px;
- bottom: 1px;
+ right: 2px;
+ bottom: 4px;
}
.formattedTextBox-outer {
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 82133a06e..3e2befb5f 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -816,6 +816,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent BoolCast(anchor.followLinkToggle);
specificContextMenu = (e: React.MouseEvent): void => {
+ if (this._props.dontSelect?.()) return;
const cm = ContextMenu.Instance;
let target = e.target as any; // hrefs are stored on the database of the node that wraps the hyerlink
--
cgit v1.2.3-70-g09d2
From 39784e909c68f139bb537151294d8db56d021158 Mon Sep 17 00:00:00 2001
From: alyssaf16
Date: Wed, 12 Jun 2024 12:31:50 -0400
Subject: flashcard
---
.../views/collections/CollectionCarouselView.scss | 11 +++-
.../views/collections/CollectionCarouselView.tsx | 55 ++++++++++++++-----
src/client/views/nodes/ComparisonBox.scss | 13 ++++-
src/client/views/nodes/ComparisonBox.tsx | 62 +++++++++++++++-------
src/client/views/pdf/AnchorMenu.tsx | 21 ++++++--
src/client/views/pdf/PDFViewer.tsx | 1 +
6 files changed, 124 insertions(+), 39 deletions(-)
(limited to 'src/client/views/nodes/ComparisonBox.scss')
diff --git a/src/client/views/collections/CollectionCarouselView.scss b/src/client/views/collections/CollectionCarouselView.scss
index c4679e888..b402a7a32 100644
--- a/src/client/views/collections/CollectionCarouselView.scss
+++ b/src/client/views/collections/CollectionCarouselView.scss
@@ -27,10 +27,10 @@
.carouselView-fwd,
.carouselView-star,
.carouselView-remove,
-.carouselView-check {
+.carouselView-check,
+.carouselView-add {
position: absolute;
display: flex;
- top: 42.5%;
width: 30;
height: 30;
align-items: center;
@@ -43,15 +43,22 @@
}
}
.carouselView-fwd {
+ top: 42.5%;
right: 20;
}
.carouselView-back {
+ top: 42.5%;
left: 20;
}
.carouselView-star {
top: 0;
left: 0;
}
+.carouselView-add {
+ position: absolute;
+ bottom: 0;
+ left: 0;
+}
.carouselView-remove {
top: 80%;
left: 52%;
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index 9cf7765dd..b5b6e1f4b 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -4,6 +4,7 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { action, computed, makeObservable, observable } from 'mobx';
import { observer } from 'mobx-react';
+import { Docs } from '../../documents/Documents';
import * as React from 'react';
import { StopEvent, returnFalse, returnOne, returnTrue, returnZero } from '../../../ClientUtils';
import { emptyFunction } from '../../../Utils';
@@ -48,7 +49,8 @@ export class CollectionCarouselView extends CollectionSubView() {
constructor(props: any) {
super(props);
makeObservable(this);
- this.layoutDoc.filterOp = cardMode.ALL;
+ // this.layoutDoc.filterOp = cardMode.ALL;
+ Doc.setDocFilter(this.Document, 'data_star', undefined, 'match');
this.layoutDoc.practiceMode = practiceMode.NORMAL;
this.layoutDoc._carousel_index = 0;
}
@@ -177,9 +179,12 @@ export class CollectionCarouselView extends CollectionSubView() {
this.layoutDoc.filterOp = mode;
switch (mode) {
case cardMode.STAR:
+ Doc.setDocFilter(this.Document, 'data_star', true, 'match');
this.move(1);
break;
- default: this.setFilterMessage(undefined); // prettier-ignore
+ default:
+ this.setFilterMessage(undefined); // prettier-ignore
+ Doc.setDocFilter(this.Document, 'data_star', true, 'remove');
}
};
@@ -197,6 +202,7 @@ export class CollectionCarouselView extends CollectionSubView() {
NativeHeight={returnZero}
fitWidth={undefined}
setContentViewBox={undefined}
+ childFilters={this.childDocFilters}
onDoubleClickScript={this.onContentDoubleClick}
onClickScript={this.onContentClick}
isDocumentActive={this._props.childDocumentsActive?.() ? this._props.isDocumentActive : this._props.isContentActive}
@@ -227,6 +233,16 @@ export class CollectionCarouselView extends CollectionSubView() {
>
);
}
+
+ containsDifTypes = (): boolean => {
+ return this.carouselItems.filter(doc => !doc.layout.isFlashcard).length === 0;
+ };
+
+ addFlashcard() {
+ const newDoc = Docs.Create.ComparisonDocument('', { _layout_isFlashcard: true, _width: 300, _height: 300 });
+ this.addDocument?.(newDoc);
+ }
+
@computed get buttons() {
if (!this.carouselItems?.[NumCast(this.layoutDoc._carousel_index)]) return null;
return (
@@ -237,17 +253,32 @@ export class CollectionCarouselView extends CollectionSubView() {
-
-
-
+ {!this.containsDifTypes() ? (
+
+ ) : null}
{this.layoutDoc.practiceMode === practiceMode.PRACTICE ? (
-
this.setPracticeVal(e, practiceVal.MISSED)}>
-
-
-
this.setPracticeVal(e, practiceVal.CORRECT)}>
-
-
+
+ this.setPracticeVal(e, practiceVal.MISSED)}>
+
+
+
+
+ this.setPracticeVal(e, practiceVal.CORRECT)}>
+
+
+
) : null}
>
@@ -320,7 +351,7 @@ export class CollectionCarouselView extends CollectionSubView() {
}}>
Recently missed!
- {this.menu}
+ {!this.containsDifTypes() ? this.menu : null}
{this.Document._chromeHidden || (!this._filterMessage && !this._practiceMessage) ? this.buttons : null}
);
diff --git a/src/client/views/nodes/ComparisonBox.scss b/src/client/views/nodes/ComparisonBox.scss
index dc107b576..0b2c356e5 100644
--- a/src/client/views/nodes/ComparisonBox.scss
+++ b/src/client/views/nodes/ComparisonBox.scss
@@ -14,9 +14,8 @@
-webkit-text-stroke-color: black;
-webkit-text-stroke-width: 0.2px;
}
-
.input-box {
- position: relative;
+ position: absolute;
padding: 10px;
width: 100%;
height: 100%;
@@ -145,6 +144,15 @@
}
}
+.explain {
+ position: absolute;
+ top: 10px;
+ left: 10px;
+ z-index: 200;
+ padding: 5px;
+ background: #dfdfdf;
+}
+
.comparisonBox-interactive {
pointer-events: unset;
cursor: ew-resize;
@@ -154,6 +162,7 @@
display: flex;
}
}
+
// .input-box {
// position: absolute;
// padding: 10px;
diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx
index 02ab76c2a..9eb5f6ca2 100644
--- a/src/client/views/nodes/ComparisonBox.tsx
+++ b/src/client/views/nodes/ComparisonBox.tsx
@@ -43,9 +43,8 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
@observable private _inputValue = '';
@observable private _outputValue = '';
@observable private _loading = false;
- @observable private _errorMessage = '';
- @observable private _outputMessage = '';
@observable private _isEmpty = false;
+ @observable _yRelativeToTop: boolean = true;
public addToCollection: ((doc: Doc | Doc[], annotationKey?: string | undefined) => boolean) | undefined;
@@ -76,8 +75,8 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
componentDidMount() {
this._props.setContentViewBox?.(this);
reaction(
- () => this._props.isSelected(),
- selected => !selected && (this.childActive = false)
+ () => this._props.isSelected(), // when this reaction should update
+ selected => !selected && (this.childActive = false) // what it should update to
);
}
protected createDropTarget = (ele: HTMLDivElement | null, fieldKey: string, disposerId: number) => {
@@ -283,8 +282,8 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
})
}
style={{
- background: usepath === 'alternate' ? 'white' : 'black',
- color: usepath === 'alternate' ? 'black' : 'white',
+ background: this.revealOp === 'hover' ? 'gray' : usepath === 'alternate' ? 'white' : 'black',
+ color: this.revealOp === 'hover' ? 'black' : usepath === 'alternate' ? 'black' : 'white',
display: 'inline-block',
}}>
@@ -303,7 +302,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent
()
this.layoutDoc[`_${this._props.fieldKey}_usePath`] === 'alternate' ? (
Flip to front side to use GPT
) : (
- Ask GPT to create an answer on the back side of the flashcard
+ Ask GPT to create an answer on the back side of the flashcard based on your question on the front
)
}>
(!this.layoutDoc[`_${this._props.fieldKey}_usePath`] ? this.askGPT(GPTCallType.CHATCARD) : null)}>
@@ -314,16 +313,17 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent
()
{
+ // this.displayLabelHandler(e.target.value, e.clientY);
const collectionArr: Doc[] = [];
collectionArr.push(this.Document);
const newCol = Docs.Create.CarouselDocument(collectionArr, {
- _width: NumCast(this.layoutDoc['_' + this._props.fieldKey + '_width'], 250),
- _height: NumCast(this.layoutDoc['_' + this._props.fieldKey + '_width'], 200),
+ _width: NumCast(this.layoutDoc['_' + this._props.fieldKey + '_width'], 250) + 50,
+ _height: NumCast(this.layoutDoc['_' + this._props.fieldKey + '_width'], 200) + 50,
_layout_fitWidth: false,
_layout_autoHeight: true,
});
- newCol['x'] = e.clientX - 820;
- newCol['y'] = e.clientY - 640;
+ newCol['x'] = this.layoutDoc['x'];
+ newCol['y'] = NumCast(this.layoutDoc['y']) + 50;
this._props.addDocument?.(newCol);
this._props.removeDocument?.(this.Document);
this.Document.embedContainer = newCol;
@@ -332,9 +332,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
Hover to reveal }>
- (this.revealOp === 'hover' ? (this.layoutDoc[`_${this._props.fieldKey}_revealOp`] = 'flip') : (this.layoutDoc[`_${this._props.fieldKey}_revealOp`] = 'hover'))}>
+
this.handleHover()}>
@@ -361,6 +359,17 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent
()
if (this._inputValue) this.askGPT(GPTCallType.QUIZ);
};
+ @action handleHover = () => {
+ if (this.revealOp === 'hover') {
+ this.layoutDoc[`_${this._props.fieldKey}_revealOp`] = 'flip';
+ this.Document.forceActive = false;
+ } else {
+ this.layoutDoc[`_${this._props.fieldKey}_revealOp`] = 'hover';
+ this.Document.forceActive = true;
+ }
+ //this.revealOp === 'hover' ? (this.layoutDoc[`_${this._props.fieldKey}_revealOp`] = 'flip') : (this.layoutDoc[`_${this._props.fieldKey}_revealOp`] = 'hover');
+ };
+
@action handleRenderClick = () => {
// Call the GPT model and get the output
this.layoutDoc[`_${this._props.fieldKey}_usePath`] = undefined;
@@ -448,6 +457,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
const whichDoc = DocCast(this.dataDoc[whichSlot]);
const targetDoc = DocCast(whichDoc?.annotationOn, whichDoc);
const layoutString = targetDoc ? '' : this.testForTextFields(whichSlot);
+ // whichDoc['backgroundColor'] = this.layoutDoc['backgroundColor'];
return targetDoc || layoutString ? (
<>
@@ -500,20 +510,32 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
// add text box to each side when comparison box is first created
// (!this.dataDoc[this.fieldKey + '_0'] && this.dataDoc[this._props.fieldKey + '_0'] !== 'empty')
if (!this.dataDoc[this.fieldKey + '_0'] && !this._isEmpty) {
- const dataSplit = StrCast(this.dataDoc.data).split('Answer');
+ 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.
// eslint-disable-next-line prefer-destructuring
- newDoc[DocData].text = dataSplit[1];
+ // newDoc.text = dataSplit[1];
+ newDoc['backgroundColor'] = 'lightgray';
this.addDoc(newDoc, this.fieldKey + '_0');
+ // DocCast(this.dataDoc[this.fieldKey + '_0'])[DocData].text = dataSplit[1];
+ // DocCast(this.dataDoc[this.fieldKey + '_0']).text = dataSplit[1];
+ // console.log('HI' + DocCast(this.dataDoc[this.fieldKey + '_0']).text);
+ console.log('HEREEE' + StrCast(RTFCast(DocCast(this.dataDoc[this.fieldKey + '_0']).text)?.Text));
}
+
if (!this.dataDoc[this.fieldKey + '_1'] && !this._isEmpty) {
- const dataSplit = StrCast(this.dataDoc.data).split('Answer');
+ const dataSplit = StrCast(this.dataDoc.data).split('Answer: ');
const newDoc = Docs.Create.TextDocument(dataSplit[0]);
+ this.addDoc(newDoc, this.fieldKey + '_1');
// if there is text from the pdf ai cards, put the answer on the alternate side.
// eslint-disable-next-line prefer-destructuring
- newDoc[DocData].text = dataSplit[0];
- this.addDoc(newDoc, this.fieldKey + '_1');
+
+ // newDoc[DocData].text = dataSplit[0];
+ // console.log('HEREEE' + StrCast(RTFCast(DocCast(this.dataDoc[this.fieldKey + '_1']).text)?.Text));
+ // console.log('HI' + DocCast(this.dataDoc[this.fieldKey + '_1']).text);
+ // DocCast(this.dataDoc[this.props.fieldKey + '_1'])[DocData].text = dataSplit[0];
+ // console.log('HEREEE' + StrCast(RTFCast(DocCast(this.dataDoc[this.fieldKey + '_0']).text)?.Text));
+ // DocCast(this.dataDoc[this.fieldKey + '_1'])[DocData].text = dataSplit[0];
}
// render the QuizCards
@@ -552,6 +574,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
);
}
+ console.log('HEREEE2' + StrCast(RTFCast(DocCast(this.dataDoc[this.fieldKey + '_1']).text)?.Text));
// render a normal flashcard when not a QuizCard
return (
()
}}
// onPointerUp={() => (this._isAnyChildContentActive = true)}
>
+ {StrCast(RTFCast(DocCast(this.dataDoc[this.fieldKey + '_1']).text)?.Text) === '' && !this.childActive ?
Enter text in the flashcard.
: null}
{displayBox(`${this.fieldKey}_${side === 0 ? 1 : 0}`, side, this._props.PanelWidth() - 3)}
{this._loading ? (
diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx
index 2f6824466..cedd3c7c3 100644
--- a/src/client/views/pdf/AnchorMenu.tsx
+++ b/src/client/views/pdf/AnchorMenu.tsx
@@ -24,6 +24,8 @@ export class AnchorMenu extends AntimodeMenu
{
private _disposer: IReactionDisposer | undefined;
private _commentRef = React.createRef();
private _cropRef = React.createRef();
+ // @observable protected _top: number = -300;
+ // @observable protected _left: number = -300;
constructor(props: any) {
super(props);
@@ -38,10 +40,17 @@ export class AnchorMenu extends AntimodeMenu {
// GPT additions
@observable private _selectedText: string = '';
+ @observable private _x: number = 0;
+ @observable private _y: number = 0;
@action
public setSelectedText = (txt: string) => {
this._selectedText = txt.trim();
};
+ @action
+ public setLocation = (x: number, y: number) => {
+ this._x = x;
+ this._y = y;
+ };
public onMakeAnchor: () => Opt = () => undefined; // Method to get anchor from text search
@@ -99,7 +108,7 @@ export class AnchorMenu extends AntimodeMenu {
* Invokes the API with the selected text and stores it in the selected text.
* @param e pointer down event
*/
- gptFlashcards = async () => {
+ gptFlashcards = async (e: React.PointerEvent) => {
const queryText = this._selectedText;
try {
const res = await gptAPICall(queryText, GPTCallType.FLASHCARD);
@@ -117,8 +126,8 @@ export class AnchorMenu extends AntimodeMenu {
*/
transferToFlashcard = (text: string) => {
// put each question generated by GPT on the front of the flashcard
- const senArr = text.split('Question');
- const collectionArr: Doc[] = [];
+ var senArr = text.trim().split('Question: ');
+ var collectionArr: Doc[] = [];
for (let 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 });
@@ -133,6 +142,10 @@ export class AnchorMenu extends AntimodeMenu {
_layout_autoHeight: true,
});
+ newCol.x = this._x;
+ newCol.y = this._y;
+ newCol.zIndex = 100;
+
this.addToCollection?.(newCol);
};
@@ -221,7 +234,7 @@ export class AnchorMenu extends AntimodeMenu {
{/* Adds a create flashcards option to the anchor menu, which calls the gptFlashcard method. */}
this.gptFlashcards(e)}
icon={ }
color={SettingsManager.userColor}
/>
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 6c1617c38..92f890e70 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -425,6 +425,7 @@ export class PDFViewer extends ObservableReactComponent {
const sel = window.getSelection();
if (sel) {
AnchorMenu.Instance.setSelectedText(sel.toString());
+ AnchorMenu.Instance.setLocation(NumCast(this._props.layoutDoc['x']), NumCast(this._props.layoutDoc['y']));
}
if (sel?.type === 'Range') {
--
cgit v1.2.3-70-g09d2
From 91e465c9ba542b637e66c7091c444a44fdbe4f0c Mon Sep 17 00:00:00 2001
From: alyssaf16
Date: Thu, 13 Jun 2024 14:37:37 -0400
Subject: create flashcard stack and ui fixes
---
.../views/collections/CollectionCarouselView.tsx | 51 +++++++++---
src/client/views/nodes/ComparisonBox.scss | 8 +-
src/client/views/nodes/ComparisonBox.tsx | 90 +++++++++++++++-------
src/client/views/pdf/AnchorMenu.tsx | 13 +++-
src/client/views/pdf/Annotation.scss | 19 ++++-
src/client/views/pdf/PDFViewer.tsx | 1 +
6 files changed, 136 insertions(+), 46 deletions(-)
(limited to 'src/client/views/nodes/ComparisonBox.scss')
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index b5b6e1f4b..f2d634eaa 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -21,6 +21,8 @@ import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
import './CollectionCarouselView.scss';
import { CollectionSubView } from './CollectionSubView';
import { Tooltip } from '@mui/material';
+import { DocUtils } from '../../documents/DocUtils';
+import { Any } from '@react-spring/web';
enum cardMode {
// PRACTICE = 'practice',
@@ -49,7 +51,8 @@ export class CollectionCarouselView extends CollectionSubView() {
constructor(props: any) {
super(props);
makeObservable(this);
- // this.layoutDoc.filterOp = cardMode.ALL;
+ // this.setModes();
+ this.layoutDoc.filterOp = cardMode.ALL;
Doc.setDocFilter(this.Document, 'data_star', undefined, 'match');
this.layoutDoc.practiceMode = practiceMode.NORMAL;
this.layoutDoc._carousel_index = 0;
@@ -67,6 +70,9 @@ export class CollectionCarouselView extends CollectionSubView() {
};
@computed get carouselItems() {
+ this.childLayoutPairs.map(pair => {
+ pair.layout.embedContainer = this.Document;
+ });
return this.childLayoutPairs.filter(pair => pair.layout.type !== DocumentType.LINK);
}
@computed get marginX() {
@@ -80,6 +86,13 @@ export class CollectionCarouselView extends CollectionSubView() {
this._filterMessage = mes;
};
+ setModes = () => {
+ this.layoutDoc.filterOp = cardMode.ALL;
+ Doc.setDocFilter(this.Document, 'data_star', undefined, 'match');
+ this.layoutDoc.practiceMode = practiceMode.NORMAL;
+ this.layoutDoc._carousel_index = 0;
+ };
+
move = (dir: number) => {
const moveToCardWithField = (match: (doc: Doc) => boolean): boolean => {
let startInd = (NumCast(this.layoutDoc._carousel_index) + dir) % this.carouselItems.length;
@@ -107,8 +120,8 @@ export class CollectionCarouselView extends CollectionSubView() {
break;
case practiceMode.PRACTICE && cardMode.STAR:
if (!moveToCardWithField((doc: Doc) => doc[this.practiceField] !== practiceVal.CORRECT && doc[this.starField] === true)) {
- this._filterMessage = 'No flashcards to show! Unselect star mode to view all flashcards.';
- this._practiceMessage = 'Unselect practice mode to view all flashcards.';
+ this._filterMessage = 'No flashcards to show! Unselect mode to view all flashcards.';
+ this._practiceMessage = undefined;
}
break;
default:
@@ -179,20 +192,24 @@ export class CollectionCarouselView extends CollectionSubView() {
this.layoutDoc.filterOp = mode;
switch (mode) {
case cardMode.STAR:
- Doc.setDocFilter(this.Document, 'data_star', true, 'match');
+ // Doc.setDocFilter(this.Document, 'data_star', true, 'match');
this.move(1);
break;
default:
this.setFilterMessage(undefined); // prettier-ignore
- Doc.setDocFilter(this.Document, 'data_star', true, 'remove');
+ // Doc.setDocFilter(this.Document, 'data_star', true, 'remove');
}
};
@computed get content() {
+ if (this.layoutDoc._carousel_index === this.carouselItems.length && this.layoutDoc._carousel_index !== 0) {
+ this.move(1);
+ }
const index = NumCast(this.layoutDoc._carousel_index);
const curDoc = this.carouselItems?.[index];
const captionProps = { ...this._props, NativeScaling: returnOne, PanelWidth: this.captionWidth, fieldKey: 'caption', setHeight: undefined, setContentView: undefined };
const carouselShowsCaptions = StrCast(this.layoutDoc._layout_showCaption);
+
return !(curDoc?.layout instanceof Doc) ? null : (
<>
@@ -207,6 +224,7 @@ export class CollectionCarouselView extends CollectionSubView() {
onClickScript={this.onContentClick}
isDocumentActive={this._props.childDocumentsActive?.() ? this._props.isDocumentActive : this._props.isContentActive}
isContentActive={this._props.childContentsActive ?? this._props.isContentActive() === false ? returnFalse : emptyFunction}
+ addDocument={this._props.addDocument}
hideCaptions={!!carouselShowsCaptions} // hide captions if the carousel is configured to show the captions
renderDepth={this._props.renderDepth + 1}
LayoutTemplate={this._props.childLayoutTemplate}
@@ -235,12 +253,21 @@ export class CollectionCarouselView extends CollectionSubView() {
}
containsDifTypes = (): boolean => {
- return this.carouselItems.filter(doc => !doc.layout.isFlashcard).length === 0;
+ return this.carouselItems.filter(doc => !doc.layout._layout_isFlashcard).length !== 0;
};
addFlashcard() {
const newDoc = Docs.Create.ComparisonDocument('', { _layout_isFlashcard: true, _width: 300, _height: 300 });
this.addDocument?.(newDoc);
+ // DocUtils.copyDragFactory(newDoc);
+ // this._props.addDocument?.();
+ // newDoc.layout = this.layoutDoc;
+ // newDoc.data = this.dataDoc;
+ // Doc.AddDocToList()
+ // this._props.parent._props.addDocument();
+ // this.childLayoutPairs.push({ newDoc.layout, newDoc.data});
+ // this._props.addDocument?.(newDoc);
+ // console.log('HERE');
}
@computed get buttons() {
@@ -260,11 +287,11 @@ export class CollectionCarouselView extends CollectionSubView() {
-
+ {/*
-
+ */}
) : null}
{this.layoutDoc.practiceMode === practiceMode.PRACTICE ? (
@@ -286,8 +313,10 @@ export class CollectionCarouselView extends CollectionSubView() {
}
togglePracticeMode = (mode: practiceMode) => {
- if (mode === this.layoutDoc.practiceMode) this.setPracticeMode(practiceMode.NORMAL);
- else this.setPracticeMode(mode);
+ if (mode === this.layoutDoc.practiceMode) {
+ this.setPracticeMode(practiceMode.NORMAL);
+ // this.setPracticeMessage("undefined");
+ } else this.setPracticeMode(mode);
};
toggleFilterMode = () => { this.layoutDoc.filterOp === cardMode.STAR ? this.setFilterMode(cardMode.ALL) : this.setFilterMode(cardMode.STAR)}; //prettier-ignore
setColor = (mode: practiceMode | cardMode, which: string) => { return which === mode ? 'white' : 'light gray'}; //prettier-ignore
@@ -351,7 +380,7 @@ export class CollectionCarouselView extends CollectionSubView() {
}}>
Recently missed!
- {!this.containsDifTypes() ? this.menu : null}
+ {!this.containsDifTypes() && this.carouselItems.length !== 0 ? this.menu : null}
{this.Document._chromeHidden || (!this._filterMessage && !this._practiceMessage) ? this.buttons : null}
);
diff --git a/src/client/views/nodes/ComparisonBox.scss b/src/client/views/nodes/ComparisonBox.scss
index 0b2c356e5..f7389e39b 100644
--- a/src/client/views/nodes/ComparisonBox.scss
+++ b/src/client/views/nodes/ComparisonBox.scss
@@ -16,14 +16,15 @@
}
.input-box {
position: absolute;
+ top: 50;
padding: 10px;
width: 100%;
- height: 100%;
+ height: 70%;
display: flex;
}
.submit-button {
- position: relative;
+ position: absolute;
padding-bottom: 10px;
padding-left: 5px;
padding-right: 5px;
@@ -31,6 +32,7 @@
border-radius: 2px;
height: 15%;
display: flex;
+ bottom: 0;
button {
flex: 1;
@@ -149,7 +151,7 @@
top: 10px;
left: 10px;
z-index: 200;
- padding: 5px;
+ // padding: 5px;
background: #dfdfdf;
}
diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx
index 9eb5f6ca2..2fc297bec 100644
--- a/src/client/views/nodes/ComparisonBox.tsx
+++ b/src/client/views/nodes/ComparisonBox.tsx
@@ -28,6 +28,7 @@ import ReactLoading from 'react-loading';
import { ContextMenu } from '../ContextMenu';
import { ContextMenuProps } from '../ContextMenuItem';
import { tickStep } from 'd3';
+import { CollectionCarouselView } from '../collections/CollectionCarouselView';
@observer
export class ComparisonBox extends ViewBoxAnnotatableComponent() {
@@ -46,8 +47,6 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
@observable private _isEmpty = false;
@observable _yRelativeToTop: boolean = true;
- public addToCollection: ((doc: Doc | Doc[], annotationKey?: string | undefined) => boolean) | undefined;
-
@action handleInputChange = (e: React.ChangeEvent) => {
this._inputValue = e.target.value;
console.log(this._inputValue);
@@ -248,6 +247,41 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
_closeRef = React.createRef();
+ createFlashcardPile(collectionArr: Doc[], gpt: boolean) {
+ const newCol = Docs.Create.CarouselDocument(collectionArr, {
+ _width: NumCast(this.layoutDoc['_' + this._props.fieldKey + '_width'], 250) + 50,
+ _height: NumCast(this.layoutDoc['_' + this._props.fieldKey + '_width'], 200) + 50,
+ _layout_fitWidth: false,
+ _layout_autoHeight: true,
+ });
+ newCol['x'] = this.layoutDoc['x'];
+ newCol['y'] = NumCast(this.layoutDoc['y']) + 50;
+ newCol.type_collection = 'carousel';
+ console.log(newCol.data);
+
+ if (gpt) {
+ this._props.DocumentView?.()._props.addDocument?.(newCol);
+ this._props.removeDocument?.(this.Document);
+ } else {
+ this._props.addDocument?.(newCol);
+ this._props.removeDocument?.(this.Document);
+ this.Document.embedContainer = newCol;
+ }
+ }
+
+ gptFlashcardPile = async () => {
+ var text = (await this.askGPT(GPTCallType.FLASHCARD)) || '';
+
+ var senArr = text.trim().split('Question: ');
+ var collectionArr: Doc[] = [];
+ for (let i = 1; i < senArr.length; i++) {
+ const newDoc = Docs.Create.ComparisonDocument(senArr[i].trim(), { _layout_isFlashcard: true, _width: 300, _height: 300 });
+ newDoc.text = senArr[i].trim();
+ collectionArr.push(newDoc);
+ }
+ this.createFlashcardPile(collectionArr, true);
+ };
+
/**
* Flips a flashcard to the alternate side for the user to view.
*/
@@ -305,35 +339,27 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
Ask GPT to create an answer on the back side of the flashcard based on your question on the front
)
}>
- (!this.layoutDoc[`_${this._props.fieldKey}_usePath`] ? this.askGPT(GPTCallType.CHATCARD) : null)}>
+
(!this.layoutDoc[`_${this._props.fieldKey}_usePath`] ? this.askGPT(GPTCallType.CHATCARD) : null)}>
-
Create a flashcard pile }>
- {
- // this.displayLabelHandler(e.target.value, e.clientY);
- const collectionArr: Doc[] = [];
- collectionArr.push(this.Document);
- const newCol = Docs.Create.CarouselDocument(collectionArr, {
- _width: NumCast(this.layoutDoc['_' + this._props.fieldKey + '_width'], 250) + 50,
- _height: NumCast(this.layoutDoc['_' + this._props.fieldKey + '_width'], 200) + 50,
- _layout_fitWidth: false,
- _layout_autoHeight: true,
- });
- newCol['x'] = this.layoutDoc['x'];
- newCol['y'] = NumCast(this.layoutDoc['y']) + 50;
- this._props.addDocument?.(newCol);
- this._props.removeDocument?.(this.Document);
- this.Document.embedContainer = newCol;
- }}>
-
+ {DocCast(this.Document.embedContainer).type_collection === 'carousel' ? null : (
+
+ Create a flashcard pile
}>
+
this.createFlashcardPile([this.Document], false)}>
+
+
+
+
Create new flashcard stack based on text }>
+ this.gptFlashcardPile()}>
+
+
+
-
+ )}
Hover to reveal }>
this.handleHover()}>
-
+
{/* Remove this side of the flashcard}>
@@ -425,6 +451,12 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
if (callType == GPTCallType.QUIZ) this._outputValue = res;
// DocCast(this.dataDoc[this.props.fieldKey + '_0'])[DocData].text = res;
// this._outputValue = res;
+
+ if (callType === GPTCallType.FLASHCARD) {
+ // console.log(res);
+ this._loading = false;
+ return res;
+ }
console.log(res);
} catch (err) {
console.error('GPT call failed');
@@ -520,7 +552,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
// DocCast(this.dataDoc[this.fieldKey + '_0'])[DocData].text = dataSplit[1];
// DocCast(this.dataDoc[this.fieldKey + '_0']).text = dataSplit[1];
// console.log('HI' + DocCast(this.dataDoc[this.fieldKey + '_0']).text);
- console.log('HEREEE' + StrCast(RTFCast(DocCast(this.dataDoc[this.fieldKey + '_0']).text)?.Text));
+ //console.log('HEREEE' + StrCast(RTFCast(DocCast(this.dataDoc[this.fieldKey + '_0']).text)?.Text));
}
if (!this.dataDoc[this.fieldKey + '_1'] && !this._isEmpty) {
@@ -545,7 +577,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
return (
{text}
-
Return to all flashcards and add text to both sides.
+
Return to all flashcards and add text to both sides.
-
-
-
+
+
+
this.openContextMenu(e.clientX, e.clientY)}
+ style={{ position: 'absolute', top: '1px', left: '11px', zIndex: '100', width: '5px', height: '5px', cursor: 'pointer' }}>
+
+
+
{ }
+
{this.layoutDoc[`_${this._props.fieldKey}_usePath`] !== 'alternate' ? (
-
+
Submit
) : (
-
+
Redo the Question
)}
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 65a645560..93c07f3a8 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -377,7 +377,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent() {
const hrefBase64 = await this.createCanvas();
const response = await gptImageLabel(hrefBase64, 'Make flashcards out of this image with each question and answer labeled as "question" and "answer". Do not label each flashcard and do not include asterisks: ');
console.log(response);
- AnchorMenu.Instance.transferToFlashcard(response);
+ AnchorMenu.Instance.transferToFlashcard(response, NumCast(this.layoutDoc['x']), NumCast(this.layoutDoc['y']));
} catch (error) {
console.log('Error');
}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 0be51816f..3c12db965 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1046,7 +1046,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent {
* Transfers the flashcard text generated by GPT on flashcards and creates a collection out them.
*/
- transferToFlashcard = (text: string) => {
+ transferToFlashcard = (text: string, x: number, y: number) => {
// put each question generated by GPT on the front of the flashcard
var senArr = text.trim().split('Question: ');
var collectionArr: Doc[] = [];
@@ -154,9 +154,10 @@ export class AnchorMenu extends AntimodeMenu {
});
console.log(collectionArr);
- newCol.x = this._x;
- newCol.y = this._y;
- newCol.zIndex = 100;
+ newCol.x = x;
+ newCol.y = y;
+ console.log(this._x);
+ newCol.zIndex = 1000;
this.addToCollection?.(newCol);
this._loading = false;
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 1af7b1fc0..9d06f81ed 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -556,7 +556,7 @@ export class PDFViewer extends ObservableReactComponent {
const res = await gptAPICall(queryText, GPTCallType.FLASHCARD);
console.log(res);
- AnchorMenu.Instance.transferToFlashcard(res || 'Something went wrong');
+ AnchorMenu.Instance.transferToFlashcard(res || 'Something went wrong', NumCast(this._props.layoutDoc['x']), NumCast(this._props.layoutDoc['y']));
this._selectionText = '';
} catch (err) {
console.error(err);
--
cgit v1.2.3-70-g09d2
From 6131fd8fc6cb1245ccfbc5d03d33ffd1498e83f4 Mon Sep 17 00:00:00 2001
From: alyssaf16
Date: Mon, 8 Jul 2024 15:12:16 -0400
Subject: Speech to phonemes progress
---
report.20231129.000028.55430.0.001.json | 1343 ++++++++++++++++++++
src/client/apis/gpt/GPT.ts | 9 +-
src/client/util/CurrentUserUtils.ts | 1 +
src/client/util/DictationManager.ts | 2 +-
src/client/views/.MetadataEntryMenu.tsx.icloud | Bin 0 -> 171 bytes
src/client/views/.Touchable.tsx.icloud | Bin 0 -> 161 bytes
.../views/collections/CollectionCarouselView.tsx | 5 +-
src/client/views/nodes/.LinkAnchorBox.scss.icloud | Bin 0 -> 168 bytes
src/client/views/nodes/AudioBox.tsx | 19 +-
src/client/views/nodes/ComparisonBox.scss | 10 +-
src/client/views/nodes/ComparisonBox.tsx | 258 ++--
src/client/views/nodes/FieldView.tsx | 2 +-
src/client/views/nodes/ImageBox.tsx | 78 +-
src/client/views/nodes/LinkAnchorBox.tsx | 115 ++
.../nodes/ae6d-ba67-4ace-93aa-0f9e0bd96b88.wav | Bin 0 -> 765006 bytes
.../views/nodes/formattedText/FormattedTextBox.tsx | 7 +-
src/fields/RichTextField.ts | 9 +-
17 files changed, 1743 insertions(+), 115 deletions(-)
create mode 100644 report.20231129.000028.55430.0.001.json
create mode 100644 src/client/views/.MetadataEntryMenu.tsx.icloud
create mode 100644 src/client/views/.Touchable.tsx.icloud
create mode 100644 src/client/views/nodes/.LinkAnchorBox.scss.icloud
create mode 100644 src/client/views/nodes/LinkAnchorBox.tsx
create mode 100644 src/client/views/nodes/ae6d-ba67-4ace-93aa-0f9e0bd96b88.wav
(limited to 'src/client/views/nodes/ComparisonBox.scss')
diff --git a/report.20231129.000028.55430.0.001.json b/report.20231129.000028.55430.0.001.json
new file mode 100644
index 000000000..a29d550ae
--- /dev/null
+++ b/report.20231129.000028.55430.0.001.json
@@ -0,0 +1,1343 @@
+
+{
+ "header": {
+ "reportVersion": 1,
+ "event": "Allocation failed - JavaScript heap out of memory",
+ "trigger": "FatalError",
+ "filename": "report.20231129.000028.55430.0.001.json",
+ "dumpEventTime": "2023-11-29T00:00:28Z",
+ "dumpEventTimeStamp": "1701234028056",
+ "processId": 55430,
+ "cwd": "/Users/sarah/Desktop/dash/Dash-Web",
+ "commandLine": [
+ "/Users/sarah/.nvm/versions/node/v12.16.0/bin/node",
+ "--max-old-space-size=2048",
+ "/Users/sarah/Desktop/dash/Dash-Web/node_modules/ts-node-dev/lib/wrap.js",
+ "/Users/sarah/Desktop/dash/Dash-Web/node_modules/fork-ts-checker-webpack-plugin/lib/service.js"
+ ],
+ "nodejsVersion": "v12.16.0",
+ "wordSize": 64,
+ "arch": "x64",
+ "platform": "darwin",
+ "componentVersions": {
+ "node": "12.16.0",
+ "v8": "7.8.279.23-node.31",
+ "uv": "1.34.0",
+ "zlib": "1.2.11",
+ "brotli": "1.0.7",
+ "ares": "1.15.0",
+ "modules": "72",
+ "nghttp2": "1.40.0",
+ "napi": "5",
+ "llhttp": "2.0.4",
+ "http_parser": "2.9.3",
+ "openssl": "1.1.1d",
+ "cldr": "35.1",
+ "icu": "64.2",
+ "tz": "2019c",
+ "unicode": "12.1"
+ },
+ "release": {
+ "name": "node",
+ "lts": "Erbium",
+ "headersUrl": "https://nodejs.org/download/release/v12.16.0/node-v12.16.0-headers.tar.gz",
+ "sourceUrl": "https://nodejs.org/download/release/v12.16.0/node-v12.16.0.tar.gz"
+ },
+ "osName": "Darwin",
+ "osRelease": "22.6.0",
+ "osVersion": "Darwin Kernel Version 22.6.0: Wed Jul 5 22:21:56 PDT 2023; root:xnu-8796.141.3~6/RELEASE_X86_64",
+ "osMachine": "x86_64",
+ "cpus": [
+ {
+ "model": "Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz",
+ "speed": 2300,
+ "user": 19491620,
+ "nice": 0,
+ "sys": 10530730,
+ "idle": 128029940,
+ "irq": 0
+ },
+ {
+ "model": "Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz",
+ "speed": 2300,
+ "user": 1041080,
+ "nice": 0,
+ "sys": 832510,
+ "idle": 156711500,
+ "irq": 0
+ },
+ {
+ "model": "Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz",
+ "speed": 2300,
+ "user": 17230140,
+ "nice": 0,
+ "sys": 7308720,
+ "idle": 133550270,
+ "irq": 0
+ },
+ {
+ "model": "Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz",
+ "speed": 2300,
+ "user": 1101080,
+ "nice": 0,
+ "sys": 813690,
+ "idle": 156678120,
+ "irq": 0
+ },
+ {
+ "model": "Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz",
+ "speed": 2300,
+ "user": 14246350,
+ "nice": 0,
+ "sys": 5654800,
+ "idle": 138314740,
+ "irq": 0
+ },
+ {
+ "model": "Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz",
+ "speed": 2300,
+ "user": 1142930,
+ "nice": 0,
+ "sys": 790770,
+ "idle": 156668310,
+ "irq": 0
+ },
+ {
+ "model": "Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz",
+ "speed": 2300,
+ "user": 12228700,
+ "nice": 0,
+ "sys": 4533960,
+ "idle": 141551990,
+ "irq": 0
+ },
+ {
+ "model": "Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz",
+ "speed": 2300,
+ "user": 1165420,
+ "nice": 0,
+ "sys": 773220,
+ "idle": 156669740,
+ "irq": 0
+ },
+ {
+ "model": "Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz",
+ "speed": 2300,
+ "user": 9805920,
+ "nice": 0,
+ "sys": 3386750,
+ "idle": 145216650,
+ "irq": 0
+ },
+ {
+ "model": "Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz",
+ "speed": 2300,
+ "user": 1177080,
+ "nice": 0,
+ "sys": 759980,
+ "idle": 156674610,
+ "irq": 0
+ },
+ {
+ "model": "Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz",
+ "speed": 2300,
+ "user": 8806950,
+ "nice": 0,
+ "sys": 2889820,
+ "idle": 146774530,
+ "irq": 0
+ },
+ {
+ "model": "Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz",
+ "speed": 2300,
+ "user": 1166250,
+ "nice": 0,
+ "sys": 737790,
+ "idle": 156711830,
+ "irq": 0
+ },
+ {
+ "model": "Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz",
+ "speed": 2300,
+ "user": 7865920,
+ "nice": 0,
+ "sys": 2438950,
+ "idle": 148205800,
+ "irq": 0
+ },
+ {
+ "model": "Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz",
+ "speed": 2300,
+ "user": 1155370,
+ "nice": 0,
+ "sys": 719580,
+ "idle": 156743860,
+ "irq": 0
+ },
+ {
+ "model": "Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz",
+ "speed": 2300,
+ "user": 7478040,
+ "nice": 0,
+ "sys": 2248690,
+ "idle": 148810130,
+ "irq": 0
+ },
+ {
+ "model": "Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz",
+ "speed": 2300,
+ "user": 1131070,
+ "nice": 0,
+ "sys": 695000,
+ "idle": 156797450,
+ "irq": 0
+ }
+ ],
+ "networkInterfaces": [
+ {
+ "name": "lo0",
+ "internal": true,
+ "mac": "00:00:00:00:00:00",
+ "address": "127.0.0.1",
+ "netmask": "255.0.0.0",
+ "family": "IPv4"
+ },
+ {
+ "name": "lo0",
+ "internal": true,
+ "mac": "00:00:00:00:00:00",
+ "address": "::1",
+ "netmask": "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
+ "family": "IPv6",
+ "scopeid": 0
+ },
+ {
+ "name": "lo0",
+ "internal": true,
+ "mac": "00:00:00:00:00:00",
+ "address": "fe80::1",
+ "netmask": "ffff:ffff:ffff:ffff::",
+ "family": "IPv6",
+ "scopeid": 1
+ },
+ {
+ "name": "en5",
+ "internal": false,
+ "mac": "ac:de:48:00:11:22",
+ "address": "fe80::aede:48ff:fe00:1122",
+ "netmask": "ffff:ffff:ffff:ffff::",
+ "family": "IPv6",
+ "scopeid": 4
+ },
+ {
+ "name": "en0",
+ "internal": false,
+ "mac": "88:66:5a:29:28:77",
+ "address": "fe80::1467:9dad:c3d1:73a7",
+ "netmask": "ffff:ffff:ffff:ffff::",
+ "family": "IPv6",
+ "scopeid": 6
+ },
+ {
+ "name": "en0",
+ "internal": false,
+ "mac": "88:66:5a:29:28:77",
+ "address": "10.38.21.14",
+ "netmask": "255.255.192.0",
+ "family": "IPv4"
+ },
+ {
+ "name": "en0",
+ "internal": false,
+ "mac": "88:66:5a:29:28:77",
+ "address": "fde6:e673:d766:4b72:1481:e813:77a2:da34",
+ "netmask": "ffff:ffff:ffff:ffff::",
+ "family": "IPv6",
+ "scopeid": 0
+ },
+ {
+ "name": "en0",
+ "internal": false,
+ "mac": "88:66:5a:29:28:77",
+ "address": "2620:6e:6000:3100:148e:201a:1a33:145d",
+ "netmask": "ffff:ffff:ffff:ffff::",
+ "family": "IPv6",
+ "scopeid": 0
+ },
+ {
+ "name": "en0",
+ "internal": false,
+ "mac": "88:66:5a:29:28:77",
+ "address": "2620:6e:6000:3100:5c7f:8d3f:76ba:694b",
+ "netmask": "ffff:ffff:ffff:ffff::",
+ "family": "IPv6",
+ "scopeid": 0
+ },
+ {
+ "name": "awdl0",
+ "internal": false,
+ "mac": "12:df:77:23:4c:4e",
+ "address": "fe80::10df:77ff:fe23:4c4e",
+ "netmask": "ffff:ffff:ffff:ffff::",
+ "family": "IPv6",
+ "scopeid": 9
+ },
+ {
+ "name": "llw0",
+ "internal": false,
+ "mac": "12:df:77:23:4c:4e",
+ "address": "fe80::10df:77ff:fe23:4c4e",
+ "netmask": "ffff:ffff:ffff:ffff::",
+ "family": "IPv6",
+ "scopeid": 12
+ },
+ {
+ "name": "utun0",
+ "internal": false,
+ "mac": "00:00:00:00:00:00",
+ "address": "fe80::6616:29f1:d83d:2f8d",
+ "netmask": "ffff:ffff:ffff:ffff::",
+ "family": "IPv6",
+ "scopeid": 14
+ },
+ {
+ "name": "utun1",
+ "internal": false,
+ "mac": "00:00:00:00:00:00",
+ "address": "fe80::b4dc:ec9a:5d3d:18ab",
+ "netmask": "ffff:ffff:ffff:ffff::",
+ "family": "IPv6",
+ "scopeid": 15
+ },
+ {
+ "name": "utun2",
+ "internal": false,
+ "mac": "00:00:00:00:00:00",
+ "address": "fe80::ce81:b1c:bd2c:69e",
+ "netmask": "ffff:ffff:ffff:ffff::",
+ "family": "IPv6",
+ "scopeid": 16
+ },
+ {
+ "name": "utun3",
+ "internal": false,
+ "mac": "00:00:00:00:00:00",
+ "address": "fe80::bd9f:d9d8:700d:2011",
+ "netmask": "ffff:ffff:ffff:ffff::",
+ "family": "IPv6",
+ "scopeid": 17
+ },
+ {
+ "name": "utun4",
+ "internal": false,
+ "mac": "00:00:00:00:00:00",
+ "address": "fe80::9a49:1de7:e220:2bb",
+ "netmask": "ffff:ffff:ffff:ffff::",
+ "family": "IPv6",
+ "scopeid": 18
+ },
+ {
+ "name": "utun5",
+ "internal": false,
+ "mac": "00:00:00:00:00:00",
+ "address": "fe80::93fe:38cf:1393:3191",
+ "netmask": "ffff:ffff:ffff:ffff::",
+ "family": "IPv6",
+ "scopeid": 19
+ },
+ {
+ "name": "utun6",
+ "internal": false,
+ "mac": "00:00:00:00:00:00",
+ "address": "fe80::781d:651c:69a7:3d42",
+ "netmask": "ffff:ffff:ffff:ffff::",
+ "family": "IPv6",
+ "scopeid": 20
+ },
+ {
+ "name": "utun7",
+ "internal": false,
+ "mac": "00:00:00:00:00:00",
+ "address": "fe80::b39:d094:2a6d:e0c5",
+ "netmask": "ffff:ffff:ffff:ffff::",
+ "family": "IPv6",
+ "scopeid": 21
+ }
+ ],
+ "host": "sarahs-mbp-2.devices.brown.edu"
+ },
+ "javascriptStack": {
+ "message": "No stack.",
+ "stack": [
+ "Unavailable."
+ ]
+ },
+ "nativeStack": [
+ {
+ "pc": "0x000000010015c8ca",
+ "symbol": "report::TriggerNodeReport(v8::Isolate*, node::Environment*, char const*, char const*, std::__1::basic_string, std::__1::allocator> const&, v8::Local) [/Users/sarah/.nvm/versions/node/v12.16.0/bin/node]"
+ },
+ {
+ "pc": "0x0000000100080f3e",
+ "symbol": "node::OnFatalError(char const*, char const*) [/Users/sarah/.nvm/versions/node/v12.16.0/bin/node]"
+ },
+ {
+ "pc": "0x0000000100185467",
+ "symbol": "v8::Utils::ReportOOMFailure(v8::internal::Isolate*, char const*, bool) [/Users/sarah/.nvm/versions/node/v12.16.0/bin/node]"
+ },
+ {
+ "pc": "0x0000000100185403",
+ "symbol": "v8::internal::V8::FatalProcessOutOfMemory(v8::internal::Isolate*, char const*, bool) [/Users/sarah/.nvm/versions/node/v12.16.0/bin/node]"
+ },
+ {
+ "pc": "0x000000010030b5f5",
+ "symbol": "v8::internal::Heap::FatalProcessOutOfMemory(char const*) [/Users/sarah/.nvm/versions/node/v12.16.0/bin/node]"
+ },
+ {
+ "pc": "0x000000010030ccc4",
+ "symbol": "v8::internal::Heap::RecomputeLimits(v8::internal::GarbageCollector) [/Users/sarah/.nvm/versions/node/v12.16.0/bin/node]"
+ },
+ {
+ "pc": "0x0000000100309b37",
+ "symbol": "v8::internal::Heap::PerformGarbageCollection(v8::internal::GarbageCollector, v8::GCCallbackFlags) [/Users/sarah/.nvm/versions/node/v12.16.0/bin/node]"
+ },
+ {
+ "pc": "0x0000000100307afd",
+ "symbol": "v8::internal::Heap::CollectGarbage(v8::internal::AllocationSpace, v8::internal::GarbageCollectionReason, v8::GCCallbackFlags) [/Users/sarah/.nvm/versions/node/v12.16.0/bin/node]"
+ },
+ {
+ "pc": "0x00000001003132ba",
+ "symbol": "v8::internal::Heap::AllocateRawWithLightRetry(int, v8::internal::AllocationType, v8::internal::AllocationOrigin, v8::internal::AllocationAlignment) [/Users/sarah/.nvm/versions/node/v12.16.0/bin/node]"
+ },
+ {
+ "pc": "0x0000000100313341",
+ "symbol": "v8::internal::Heap::AllocateRawWithRetryOrFail(int, v8::internal::AllocationType, v8::internal::AllocationOrigin, v8::internal::AllocationAlignment) [/Users/sarah/.nvm/versions/node/v12.16.0/bin/node]"
+ },
+ {
+ "pc": "0x00000001002e065b",
+ "symbol": "v8::internal::Factory::NewFillerObject(int, bool, v8::internal::AllocationType, v8::internal::AllocationOrigin) [/Users/sarah/.nvm/versions/node/v12.16.0/bin/node]"
+ },
+ {
+ "pc": "0x0000000100618a18",
+ "symbol": "v8::internal::Runtime_AllocateInYoungGeneration(int, unsigned long*, v8::internal::Isolate*) [/Users/sarah/.nvm/versions/node/v12.16.0/bin/node]"
+ },
+ {
+ "pc": "0x0000000100950c19",
+ "symbol": "Builtins_CEntry_Return1_DontSaveFPRegs_ArgvOnStack_NoBuiltinExit [/Users/sarah/.nvm/versions/node/v12.16.0/bin/node]"
+ }
+ ],
+ "javascriptHeap": {
+ "totalMemory": 2154991616,
+ "totalCommittedMemory": 2153815368,
+ "usedMemory": 2136222480,
+ "availableMemory": 51343040,
+ "memoryLimit": 2197815296,
+ "heapSpaces": {
+ "read_only_space": {
+ "memorySize": 262144,
+ "committedMemory": 33088,
+ "capacity": 32808,
+ "used": 32808,
+ "available": 0
+ },
+ "new_space": {
+ "memorySize": 8388608,
+ "committedMemory": 8388528,
+ "capacity": 4189824,
+ "used": 69544,
+ "available": 4120280
+ },
+ "old_space": {
+ "memorySize": 1946566656,
+ "committedMemory": 1946346224,
+ "capacity": 1939183704,
+ "used": 1938974448,
+ "available": 209256
+ },
+ "code_space": {
+ "memorySize": 14323712,
+ "committedMemory": 13779552,
+ "capacity": 12481792,
+ "used": 12481792,
+ "available": 0
+ },
+ "map_space": {
+ "memorySize": 1576960,
+ "committedMemory": 1394440,
+ "capacity": 1280720,
+ "used": 1280720,
+ "available": 0
+ },
+ "large_object_space": {
+ "memorySize": 183824384,
+ "committedMemory": 183824384,
+ "capacity": 183380384,
+ "used": 183380384,
+ "available": 0
+ },
+ "code_large_object_space": {
+ "memorySize": 49152,
+ "committedMemory": 49152,
+ "capacity": 2784,
+ "used": 2784,
+ "available": 0
+ },
+ "new_large_object_space": {
+ "memorySize": 0,
+ "committedMemory": 0,
+ "capacity": 4189824,
+ "used": 0,
+ "available": 4189824
+ }
+ }
+ },
+ "resourceUsage": {
+ "userCpuSeconds": 306.691,
+ "kernelCpuSeconds": 15.3869,
+ "cpuConsumptionPercent": 11.2772,
+ "maxRss": 2231285841920,
+ "pageFaults": {
+ "IORequired": 110,
+ "IONotRequired": 2487718
+ },
+ "fsActivity": {
+ "reads": 0,
+ "writes": 0
+ }
+ },
+ "libuv": [
+ ],
+ "environmentVariables": {
+ "npm_config_save_dev": "",
+ "npm_config_legacy_bundling": "",
+ "npm_config_dry_run": "",
+ "npm_package_dependencies_request": "^2.88.2",
+ "npm_package_dependencies_express_flash": "0.0.2",
+ "npm_package_dependencies__fortawesome_fontawesome_svg_core": "^6.3.0",
+ "NVM_INC": "/Users/sarah/.nvm/versions/node/v12.16.0/include/node",
+ "npm_config_viewer": "man",
+ "npm_config_only": "",
+ "npm_config_commit_hooks": "true",
+ "npm_config_browser": "",
+ "npm_package_gitHead": "5754c1c056ec62e17b5d7badddc4a8fc3637e09f",
+ "npm_package_dependencies_webpack_dev_middleware": "^5.3.1",
+ "npm_package_dependencies_webpack_cli": "^4.10.0",
+ "npm_package_devDependencies_prettier": "^2.7.1",
+ "npm_package_devDependencies_awesome_typescript_loader": "^5.2.1",
+ "npm_package_devDependencies__types_archiver": "^3.1.1",
+ "npm_config_also": "",
+ "npm_package_dependencies_react_jsx_parser": "^1.29.0",
+ "npm_package_dependencies_mongoose": "^5.13.14",
+ "npm_package_dependencies_connect_flash": "^0.1.1",
+ "npm_package_browser_child_process": "false",
+ "npm_config_sign_git_commit": "",
+ "npm_config_rollback": "true",
+ "npm_package_dependencies_material_ui": "^0.20.2",
+ "npm_package_devDependencies__types_sharp": "^0.23.1",
+ "npm_package_devDependencies__types_passport_local": "^1.0.34",
+ "npm_package_devDependencies__types_dotenv": "^6.1.1",
+ "npm_package_devDependencies__types_cookie_parser": "^1.4.2",
+ "TERM_PROGRAM": "Apple_Terminal",
+ "NODE": "/Users/sarah/.nvm/versions/node/v12.16.0/bin/node",
+ "npm_config_usage": "",
+ "npm_config_audit": "true",
+ "npm_package_dependencies_reveal_js": "^4.3.0",
+ "npm_package_dependencies_process": "^0.11.10",
+ "npm_package_dependencies_pdfjs": "^2.4.7",
+ "npm_package_dependencies_html_to_image": "^0.1.3",
+ "npm_package_devDependencies_file_loader": "^3.0.1",
+ "npm_package_devDependencies__types_express_flash": "0.0.0",
+ "npm_package_scripts_monitor": "cross-env MONITORED=true NODE_OPTIONS=--max_old_space_size=4096 ts-node src/server/index.ts",
+ "INIT_CWD": "/Users/sarah/Desktop/dash/Dash-Web",
+ "npm_package_dependencies_rehype_raw": "^6.1.1",
+ "npm_package_dependencies_react_audio_waveform": "0.0.5",
+ "npm_package_dependencies_path_browserify": "^1.0.1",
+ "npm_package_dependencies_nodemailer": "^5.1.1",
+ "npm_package_dependencies_axios": "^0.19.2",
+ "npm_package_devDependencies_typescript": "^4.7.4",
+ "NVM_CD_FLAGS": "-q",
+ "npm_config_globalignorefile": "/Users/sarah/.nvm/versions/node/v12.16.0/etc/npmignore",
+ "npm_package_dependencies_react_grid_layout": "^1.3.4",
+ "npm_package_dependencies_prosemirror_find_replace": "^0.9.0",
+ "npm_package_dependencies_normalize_css": "^8.0.1",
+ "npm_package_devDependencies_mocha": "^5.2.0",
+ "npm_package_devDependencies__types_express_session": "^1.17.5",
+ "SHELL": "/bin/zsh",
+ "TERM": "xterm-256color",
+ "npm_config_shell": "/bin/zsh",
+ "npm_config_maxsockets": "50",
+ "npm_config_init_author_url": "",
+ "npm_package_dependencies_prosemirror_dev_tools": "^3.1.0",
+ "npm_package_dependencies_p_limit": "^2.2.0",
+ "npm_package_dependencies_bson": "^4.6.1",
+ "npm_package_dependencies__types_dom_speech_recognition": "0.0.1",
+ "npm_package_dependencies__emotion_styled": "^11.11.0",
+ "npm_package_devDependencies_style_loader": "^0.23.1",
+ "npm_package_devDependencies__types_react_datepicker": "^3.1.8",
+ "npm_config_shrinkwrap": "true",
+ "npm_config_parseable": "",
+ "npm_config_metrics_registry": "https://registry.npmjs.org/",
+ "npm_package_dependencies_xregexp": "^4.4.1",
+ "npm_package_dependencies_shelljs": "^0.8.5",
+ "npm_package_dependencies_bezier_curve": "^1.0.0",
+ "npm_package_dependencies__mui_icons_material": "^5.11.16",
+ "npm_package_devDependencies_tslint": "^5.20.1",
+ "npm_package_devDependencies__types_react_transition_group": "^4.4.5",
+ "npm_package_scripts_tsc": "tsc",
+ "TMPDIR": "/var/folders/yk/p_39q8jn673c5p8_66mcxm7r0000gn/T/",
+ "npm_config_timing": "",
+ "npm_config_init_license": "ISC",
+ "npm_package_dependencies_socket_io": "^2.5.0",
+ "npm_package_dependencies_probe_image_size": "^4.0.0",
+ "npm_package_dependencies_canvas": "^2.9.3",
+ "npm_package_dependencies__hig_theme_data": "^2.23.1",
+ "npm_package_devDependencies__types_react_select": "^3.1.2",
+ "npm_package_devDependencies__types_prosemirror_model": "^1.16.1",
+ "CONDA_SHLVL": "1",
+ "npm_config_if_present": "",
+ "npm_package_dependencies_typescript_collections": "^1.3.3",
+ "npm_package_dependencies_rimraf": "^3.0.0",
+ "npm_package_dependencies_react_autosuggest": "^9.4.3",
+ "npm_package_dependencies_flexlayout_react": "^0.3.11",
+ "npm_package_dependencies_find_in_files": "^0.5.0",
+ "npm_package_devDependencies__types_chai": "^4.3.0",
+ "CONDA_PROMPT_MODIFIER": "(base) ",
+ "TERM_PROGRAM_VERSION": "447",
+ "npm_package_dependencies_prosemirror_inputrules": "^1.1.3",
+ "npm_package_dependencies_bcrypt_nodejs": "0.0.3",
+ "npm_package_dependencies_async": "^2.6.2",
+ "npm_config_sign_git_tag": "",
+ "npm_config_init_author_email": "",
+ "npm_config_cache_max": "Infinity",
+ "npm_package_dependencies_uuid": "^3.4.0",
+ "npm_package_dependencies_supercluster": "^7.1.4",
+ "npm_package_dependencies_remark_gfm": "^3.0.1",
+ "npm_package_dependencies_connect_mongo": "^2.0.3",
+ "npm_package_dependencies_browser_assert": "^1.2.1",
+ "npm_package_devDependencies_sass_loader": "^7.3.1",
+ "npm_package_scripts_start_release_debug": "cross-env RELEASE=true USE_AZURE=true NODE_OPTIONS=--max_old_space_size=4096 ts-node-dev --inspect -- src/server/index.ts",
+ "npm_config_preid": "",
+ "npm_config_long": "",
+ "npm_config_local_address": "",
+ "npm_config_git_tag_version": "true",
+ "npm_config_cert": "",
+ "npm_package_dependencies_js_datepicker": "^4.6.6",
+ "npm_package_devDependencies__types_webpack_hot_middleware": "^2.25.6",
+ "npm_package_devDependencies__types_mongodb": "^3.6.20",
+ "npm_package_devDependencies__types_mocha": "^5.2.6",
+ "TERM_SESSION_ID": "4AACD142-8780-44F4-A4FA-BB53E1BF6081",
+ "npm_config_registry": "https://registry.npmjs.org/",
+ "npm_config_noproxy": "",
+ "npm_config_fetch_retries": "2",
+ "npm_package_dependencies_react_compound_slider": "^2.5.0",
+ "npm_package_dependencies_prosemirror_history": "^1.2.0",
+ "npm_package_devDependencies__types_react_color": "^2.17.6",
+ "npm_package_devDependencies__types_google_maps_react": "^2.0.5",
+ "npm_package_devDependencies__types_color": "^3.0.3",
+ "npm_package_dependencies_react_dom": "^18.2.0",
+ "npm_package_dependencies_passport_local": "^1.0.0",
+ "npm_package_dependencies__octokit_core": "^4.0.4",
+ "npm_package_devDependencies__types_async": "^2.4.1",
+ "npm_package_scripts_debug": "cross-env NODE_OPTIONS=--max_old_space_size=8192 ts-node-dev --transpile-only --inspect -- src/server/index.ts",
+ "npm_package_scripts_oldstart": "cross-env NODE_OPTIONS=--max_old_space_size=4096 ts-node-dev --debug -- src/server/index.ts",
+ "npm_config_versions": "",
+ "npm_config_message": "%s",
+ "npm_config_key": "",
+ "npm_package_readmeFilename": "README.md",
+ "npm_package_dependencies_react_refresh_typescript": "^2.0.7",
+ "npm_package_dependencies_react_dropzone": "^14.2.3",
+ "npm_package_dependencies_image_size": "^0.7.5",
+ "npm_package_dependencies_html_to_text": "^5.1.1",
+ "npm_package_dependencies_express_validator": "^5.3.1",
+ "npm_package_devDependencies_eslint_plugin_jsx_a11y": "^6.6.0",
+ "npm_package_node_child_process": "empty",
+ "npm_package_dependencies_react_resizable_rotatable_draggable": "^0.2.0",
+ "npm_package_dependencies_got": "^12.0.1",
+ "npm_package_dependencies__types_d3_color": "^2.0.3",
+ "npm_package_devDependencies_webpack": "^5.69.1",
+ "npm_package_devDependencies__types_nodemailer": "^4.6.6",
+ "npm_package_description": "Install Node.js, then, from the project directory, run",
+ "NVM_DIR": "/Users/sarah/.nvm",
+ "USER": "sarah",
+ "npm_package_dependencies__types_d3_scale": "^3.3.2",
+ "npm_package_devDependencies_dotenv": "^8.6.0",
+ "npm_package_devDependencies__types_react": "^18.0.15",
+ "npm_package_devDependencies__types_prosemirror_transform": "^1.1.5",
+ "npm_package_devDependencies__types_prosemirror_history": "^1.0.3",
+ "npm_package_dependencies_readline": "^1.3.0",
+ "npm_package_dependencies__types_supercluster": "^7.1.0",
+ "npm_package_dependencies__azure_storage_blob": "^12.14.0",
+ "npm_config_globalconfig": "/Users/sarah/.nvm/versions/node/v12.16.0/etc/npmrc",
+ "npm_package_dependencies_depcheck": "^0.9.2",
+ "npm_package_dependencies__types_web": "0.0.53",
+ "CONDA_EXE": "/Users/sarah/miniconda3/bin/conda",
+ "npm_config_prefer_online": "",
+ "npm_config_logs_max": "10",
+ "npm_config_always_auth": "",
+ "npm_package_dependencies_react_icons": "^4.3.1",
+ "npm_package_dependencies_passport_google_oauth20": "^2.0.0",
+ "npm_package_devDependencies_webpack_dev_server": "^3.11.3",
+ "npm_package_devDependencies__types_brotli": "^1.3.1",
+ "npm_package_dependencies_url_loader": "^1.1.2",
+ "npm_package_dependencies_stream_browserify": "^3.0.0",
+ "npm_package_dependencies_prosemirror_transform": "^1.3.4",
+ "npm_package_dependencies_lodash": "^4.17.21",
+ "npm_package_dependencies_i": "^0.3.7",
+ "npm_package_devDependencies_tslint_loader": "^3.6.0",
+ "SSH_AUTH_SOCK": "/private/tmp/com.apple.launchd.dBaRxB6a53/Listeners",
+ "npm_package_dependencies_words_to_numbers": "^1.5.1",
+ "npm_package_dependencies_valid_url": "^1.0.9",
+ "npm_package_dependencies_styled_components": "^4.4.1",
+ "npm_package_dependencies_csv_parser": "^3.0.0",
+ "npm_package_dependencies_class_transformer": "^0.2.0",
+ "npm_package_devDependencies_eslint": "^8.36.0",
+ "npm_package_devDependencies__types_prosemirror_inputrules": "^1.0.4",
+ "npm_package_devDependencies__types_express": "^4.17.13",
+ "__CF_USER_TEXT_ENCODING": "0x1F5:0x0:0x0",
+ "npm_execpath": "/Users/sarah/.nvm/versions/node/v12.16.0/lib/node_modules/npm/bin/npm-cli.js",
+ "npm_config_global_style": "",
+ "npm_config_cache_lock_retries": "10",
+ "npm_package_dependencies_wikijs": "^6.3.3",
+ "npm_package_dependencies_bluebird": "^3.7.2",
+ "npm_package_devDependencies__types_react_typist": "^2.0.3",
+ "npm_config_update_notifier": "true",
+ "npm_config_cafile": "",
+ "npm_package_dependencies_util": "^0.12.4",
+ "npm_package_dependencies_raw_loader": "^1.0.0",
+ "npm_package_dependencies_https_browserify": "^1.0.0",
+ "npm_package_dependencies_brotli": "^1.3.3",
+ "npm_package_dependencies__mui_material": "^5.13.1",
+ "npm_package_dependencies__fortawesome_react_fontawesome": "^0.2.0",
+ "npm_package_devDependencies__types_passport_google_oauth20": "^2.0.11",
+ "npm_package_dependencies_cors": "^2.8.5",
+ "npm_package_dependencies_bezier_js": "^4.1.1",
+ "npm_package_dependencies__fortawesome_free_brands_svg_icons": "^6.3.0",
+ "npm_config_heading": "npm",
+ "npm_config_audit_level": "low",
+ "npm_package_dependencies_chrome": "^0.1.0",
+ "npm_package_dependencies__react_three_fiber": "^6.2.3",
+ "npm_package_devDependencies_eslint_plugin_prettier": "^4.2.1",
+ "npm_package_devDependencies_copy_webpack_plugin": "^4.6.0",
+ "npm_package_devDependencies__types_react_measure": "^2.0.8",
+ "npm_package_devDependencies__types_react_dom": "^18.0.6",
+ "npm_package_devDependencies__types_mobile_detect": "^1.3.4",
+ "_CE_CONDA": "",
+ "npm_config_searchlimit": "20",
+ "npm_config_read_only": "",
+ "npm_config_offline": "",
+ "npm_config_fetch_retry_mintimeout": "10000",
+ "npm_package_dependencies_react_typist": "^2.0.5",
+ "npm_package_dependencies_mobx_react_devtools": "^6.1.1",
+ "npm_package_dependencies_md5_file": "^5.0.0",
+ "npm_package_dependencies_forever_agent": "^0.6.1",
+ "npm_package_devDependencies__types_xregexp": "^4.4.0",
+ "npm_package_devDependencies__types_typescript": "^2.0.0",
+ "npm_package_devDependencies__types_request": "^2.48.8",
+ "npm_package_devDependencies__types_prosemirror_commands": "^1.0.4",
+ "npm_config_json": "",
+ "npm_config_access": "",
+ "npm_config_argv": "{\"remain\":[],\"cooked\":[\"start\"],\"original\":[\"start\"]}",
+ "npm_package_dependencies__fortawesome_free_solid_svg_icons": "^6.3.0",
+ "npm_package_devDependencies__types_socket_io": "^2.1.13",
+ "PATH": "/Users/sarah/.nvm/versions/node/v12.16.0/lib/node_modules/npm/node_modules/npm-lifecycle/node-gyp-bin:/Users/sarah/Desktop/dash/Dash-Web/node_modules/.bin:/Users/sarah/.nvm/versions/node/v12.16.0/bin:/Users/sarah/miniconda3/bin:/Users/sarah/miniconda3/condabin:/Users/sarah/.elan/bin:/Library/Frameworks/Python.framework/Versions/3.9/bin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Library/Apple/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin",
+ "npm_config_allow_same_version": "",
+ "npm_package_dependencies_webrtc_adapter": "^7.7.1",
+ "npm_package_dependencies_react_reveal": "^1.2.2",
+ "npm_package_dependencies_prosemirror_schema_list": "^1.1.6",
+ "npm_package_dependencies__material_ui_core": "^4.12.3",
+ "npm_package_devDependencies__types_rimraf": "^2.0.5",
+ "npm_package_devDependencies__types_connect_flash": "0.0.34",
+ "npm_config_https_proxy": "",
+ "npm_config_engine_strict": "",
+ "npm_config_description": "true",
+ "npm_package_dependencies_pug": "^2.0.4",
+ "npm_package_dependencies_prosemirror_keymap": "^1.1.5",
+ "npm_package_dependencies_pdfjs_dist": "^2.14.305",
+ "npm_package_dependencies_mobile_detect": "^1.4.5",
+ "npm_package_dependencies_image_size_stream": "^1.1.0",
+ "npm_package_dependencies_golden_layout": "^1.5.9",
+ "npm_package_dependencies_child_process": "^1.0.2",
+ "npm_package_dependencies__types_d3_axis": "^2.1.3",
+ "_": "/Users/sarah/Desktop/dash/Dash-Web/node_modules/.bin/cross-env",
+ "LaunchInstanceID": "16109DB5-CDB2-454A-9613-8F6F534702AC",
+ "npm_config_userconfig": "/Users/sarah/.npmrc",
+ "npm_config_init_module": "/Users/sarah/.npm-init.js",
+ "npm_package_dependencies__react_google_maps_api": "^2.7.0",
+ "CONDA_PREFIX": "/Users/sarah/miniconda3",
+ "__CFBundleIdentifier": "com.apple.Terminal",
+ "npm_config_cidr": "",
+ "npm_package_dependencies_puppeteer": "^3.3.0",
+ "npm_package_dependencies_prosemirror_view": "^1.26.5",
+ "npm_package_dependencies_mongodb": "^3.7.3",
+ "npm_package_dependencies_google_auth_library": "^4.2.4",
+ "npm_package_dependencies_bootstrap": "^4.6.1",
+ "npm_package_devDependencies_eslint_config_airbnb": "^19.0.4",
+ "PWD": "/Users/sarah/desktop/dash/dash-web",
+ "npm_config_user": "501",
+ "npm_config_node_version": "12.16.0",
+ "npm_package_dependencies_node_sass": "^4.14.1",
+ "npm_package_dependencies_howler": "^2.2.3",
+ "npm_package_dependencies_expressjs": "^1.0.1",
+ "npm_package_dependencies_core_js": "^3.28.0",
+ "npm_package_dependencies_browndash_components": "^0.1.36",
+ "npm_package_devDependencies_eslint_plugin_react_hooks": "^4.6.0",
+ "npm_package_devDependencies__types_lodash": "^4.14.179",
+ "JAVA_HOME": "/Library/Java/JavaVirtualMachines/jdk1.8.0_341.jdk/Contents/Home",
+ "npm_lifecycle_event": "start",
+ "npm_package_dependencies_react_table": "^6.11.5",
+ "npm_package_dependencies_react_loading": "^2.0.3",
+ "npm_package_dependencies_mobx": "^5.15.7",
+ "npm_package_dependencies_babel": "^6.23.0",
+ "npm_package_devDependencies_jsdom": "^15.2.1",
+ "npm_package_devDependencies_chai": "^4.3.6",
+ "npm_config_save": "true",
+ "npm_config_ignore_prepublish": "",
+ "npm_config_editor": "vi",
+ "npm_config_auth_type": "legacy",
+ "npm_package_dependencies_npm": "^6.14.18",
+ "npm_package_dependencies_node_stream_zip": "^1.15.0",
+ "npm_package_dependencies_image_data_uri": "^2.0.1",
+ "npm_package_scripts_start_release": "cross-env RELEASE=true USE_AZURE=false NODE_OPTIONS=--max_old_space_size=4096 ts-node-dev -- src/server/index.ts",
+ "npm_package_name": "dash",
+ "LANG": "en_US.UTF-8",
+ "npm_config_tag": "latest",
+ "npm_config_script_shell": "",
+ "npm_package_dependencies_query_string": "^6.14.1",
+ "npm_package_dependencies_mobx_utils": "^5.6.2",
+ "npm_package_dependencies_file_saver": "^2.0.5",
+ "npm_package_dependencies_body_parser": "^1.19.2",
+ "npm_package_dependencies__types_reveal": "^3.3.33",
+ "npm_package_devDependencies_eslint_plugin_import": "^2.26.0",
+ "npm_package_devDependencies__types_prosemirror_view": "^1.23.1",
+ "npm_config_progress": "true",
+ "npm_config_global": "",
+ "npm_config_before": "",
+ "npm_package_dependencies_xoauth2": "^1.2.0",
+ "npm_package_dependencies_standard_http_error": "^2.0.1",
+ "npm_package_dependencies_react_loader_spinner": "^5.3.4",
+ "npm_package_dependencies_http_browserify": "^1.7.0",
+ "npm_package_dependencies__types_d3_selection": "^2.0.1",
+ "npm_package_dependencies__hig_flyout": "^1.3.1",
+ "npm_package_devDependencies_fork_ts_checker_webpack_plugin": "^1.6.0",
+ "npm_package_scripts_build": "cross-env NODE_OPTIONS=--max_old_space_size=8192 webpack --env production",
+ "npm_package_scripts_start": "cross-env NODE_OPTIONS=--max_old_space_size=4096 ts-node-dev --debug --transpile-only -- src/server/index.ts",
+ "npm_config_searchstaleness": "900",
+ "npm_config_optional": "true",
+ "npm_config_ham_it_up": "",
+ "npm_package_dependencies_sharp": "^0.23.4",
+ "npm_package_dependencies_rc_switch": "^1.9.2",
+ "npm_package_dependencies_googlephotos": "^0.2.5",
+ "npm_package_dependencies_exifr": "^7.1.3",
+ "npm_package_dependencies__types_google_maps": "^3.2.3",
+ "npm_package_dependencies__types_bezier_js": "^4.1.0",
+ "npm_package_dependencies__ffmpeg_core": "0.10.0",
+ "npm_package_devDependencies_ts_loader": "^5.3.3",
+ "npm_package_devDependencies__types_bcrypt_nodejs": "0.0.30",
+ "XPC_FLAGS": "0x0",
+ "npm_config_save_prod": "",
+ "npm_config_force": "",
+ "npm_config_bin_links": "true",
+ "npm_package_devDependencies__types_youtube": "0.0.39",
+ "npm_config_searchopts": "",
+ "npm_package_dependencies_react_beautiful_dnd": "^13.1.0",
+ "npm_package_dependencies_jszip": "^3.7.1",
+ "npm_package_dependencies_csv_stringify": "^6.3.0",
+ "npm_package_devDependencies__types_react_icons": "^3.0.0",
+ "npm_config_node_gyp": "/Users/sarah/.nvm/versions/node/v12.16.0/lib/node_modules/npm/node_modules/node-gyp/bin/node-gyp.js",
+ "npm_config_depth": "Infinity",
+ "npm_package_dependencies_google_maps_react": "^2.0.6",
+ "npm_package_dependencies_express_session": "^1.17.2",
+ "npm_package_devDependencies_eslint_plugin_node": "^11.1.0",
+ "npm_package_devDependencies_eslint_config_prettier": "^8.5.0",
+ "npm_package_main": "index.js",
+ "npm_config_sso_poll_frequency": "500",
+ "npm_config_rebuild_bundle": "true",
+ "npm_package_dependencies_chart_js": "^3.8.0",
+ "npm_package_dependencies__emotion_react": "^11.11.0",
+ "npm_package_devDependencies__types_prosemirror_menu": "^1.0.6",
+ "npm_package_devDependencies__types_prosemirror_keymap": "^1.0.4",
+ "npm_package_devDependencies__types_pdfjs_dist": "^2.10.378",
+ "npm_package_devDependencies__types_exif": "^0.6.3",
+ "npm_package_version": "1.0.0",
+ "_CE_M": "",
+ "XPC_SERVICE_NAME": "0",
+ "npm_config_unicode": "true",
+ "npm_package_dependencies_typescript_language_server": "^0.4.0",
+ "npm_package_dependencies_prosemirror_model": "^1.18.1",
+ "npm_package_dependencies__ffmpeg_ffmpeg": "0.10.0",
+ "SHLVL": "2",
+ "HOME": "/Users/sarah",
+ "npm_config_fetch_retry_maxtimeout": "60000",
+ "npm_package_dependencies_request_promise": "^4.2.6",
+ "npm_package_dependencies_react_markdown": "^8.0.3",
+ "npm_package_dependencies__hig_theme_context": "^2.1.3",
+ "npm_package_devDependencies__types_react_autosuggest": "^9.3.14",
+ "npm_package_devDependencies__types_mongoose": "^5.11.97",
+ "npm_package_devDependencies__types_d3": "^7.4.0",
+ "npm_package_devDependencies__types_animejs": "^2.0.2",
+ "npm_package_scripts_test": "mocha -r ts-node/register test/**/*.ts",
+ "npm_config_tag_version_prefix": "v",
+ "npm_config_strict_ssl": "true",
+ "npm_config_sso_type": "oauth",
+ "npm_config_scripts_prepend_node_path": "warn-only",
+ "npm_config_save_prefix": "^",
+ "npm_config_loglevel": "notice",
+ "npm_config_ca": "",
+ "npm_package_dependencies_three": "^0.127.0",
+ "npm_package_dependencies_openai": "^3.2.1",
+ "npm_package_dependencies_mobx_react": "^5.4.4",
+ "npm_package_dependencies_google_translate_api_browser": "^3.0.1",
+ "npm_package_dependencies_cookie_parser": "^1.4.6",
+ "npm_package_dependencies_adm_zip": "^0.4.16",
+ "npm_package_devDependencies_eslint_config_node": "^4.1.0",
+ "npm_config_save_exact": "",
+ "npm_config_group": "20",
+ "npm_config_fetch_retry_factor": "10",
+ "npm_config_dev": "",
+ "npm_package_devDependencies_webpack_hot_middleware": "^2.25.1",
+ "npm_package_devDependencies_cross_env": "^5.2.1",
+ "npm_config_version": "",
+ "npm_config_prefer_offline": "",
+ "npm_config_cache_lock_stale": "60000",
+ "npm_package_devDependencies__types_prosemirror_state": "^1.2.8",
+ "npm_package_devDependencies__types_body_parser": "^1.19.2",
+ "npm_config_otp": "",
+ "npm_config_cache_min": "10",
+ "npm_package_dependencies_react_color": "^2.19.3",
+ "npm_package_dependencies_d3": "^7.6.1",
+ "npm_package_devDependencies_ts_node": "^10.9.1",
+ "npm_package_devDependencies__types_react_grid_layout": "^1.3.2",
+ "npm_config_searchexclude": "",
+ "npm_config_cache": "/Users/sarah/.npm",
+ "npm_package_dependencies_tough_cookie": "^4.0.0",
+ "npm_package_dependencies_googleapis": "^40.0.0",
+ "npm_package_devDependencies__types_valid_url": "^1.0.3",
+ "npm_package_devDependencies__types_passport": "^1.0.9",
+ "npm_package_devDependencies__types_adm_zip": "^0.4.34",
+ "CONDA_PYTHON_EXE": "/Users/sarah/miniconda3/bin/python",
+ "LOGNAME": "sarah",
+ "npm_lifecycle_script": "cross-env NODE_OPTIONS=--max_old_space_size=4096 ts-node-dev --debug --transpile-only -- src/server/index.ts",
+ "npm_config_color": "true",
+ "npm_package_dependencies_solr_node": "^1.2.1",
+ "npm_package_dependencies_react_transition_group": "^4.4.2",
+ "npm_package_dependencies_iink_js": "^1.5.4",
+ "npm_package_dependencies_html_webpack_plugin": "^5.5.0",
+ "npm_config_proxy": "",
+ "npm_config_package_lock": "true",
+ "npm_package_dependencies_prosemirror_state": "^1.4.1",
+ "npm_package_dependencies_nodemon": "^1.19.4",
+ "npm_package_dependencies_function_plot": "^1.22.8",
+ "npm_package_dependencies_equation_editor_react": "github:bobzel/equation-editor-react#useLocally",
+ "npm_package_devDependencies__types_socket_io_parser": "^3.0.0",
+ "CLASSPATH": "/Users/sarah/Downloads/cs15/*:.",
+ "npm_config_package_lock_only": "",
+ "npm_config_fund": "true",
+ "npm_package_dependencies_react": "^18.2.0",
+ "npm_package_dependencies_bingmaps_react": "^1.2.10",
+ "npm_package_devDependencies_scss_loader": "0.0.1",
+ "npm_package_devDependencies__types_cookie_session": "^2.0.44",
+ "npm_config_save_optional": "",
+ "npm_package_dependencies_textarea_caret": "^3.1.0",
+ "npm_package_dependencies_react_measure": "^2.5.2",
+ "npm_package_dependencies_exif": "^0.6.0",
+ "NVM_BIN": "/Users/sarah/.nvm/versions/node/v12.16.0/bin",
+ "CONDA_DEFAULT_ENV": "base",
+ "npm_config_ignore_scripts": "",
+ "npm_config_user_agent": "npm/6.14.7 node/v12.16.0 darwin x64",
+ "npm_package_dependencies_react_resizable": "^1.11.1",
+ "npm_package_dependencies_prosemirror_commands": "^1.2.1",
+ "npm_package_dependencies_memorystream": "^0.3.1",
+ "npm_package_dependencies_formidable": "1.2.1",
+ "npm_package_devDependencies__types_uuid": "^3.4.10",
+ "npm_config_cache_lock_wait": "10000",
+ "npm_package_dependencies_socket_io_client": "^2.5.0",
+ "npm_package_dependencies_recharts": "^2.1.12",
+ "npm_package_dependencies_react_chartjs_2": "^4.3.0",
+ "npm_package_dependencies_fluent_ffmpeg": "^2.1.2",
+ "npm_package_dependencies__types_cors": "^2.8.12",
+ "npm_package_devDependencies__types_node": "^10.17.60",
+ "npm_package_devDependencies__types_file_saver": "^2.0.5",
+ "npm_config_production": "",
+ "npm_package_dependencies_jsonschema": "^1.4.0",
+ "npm_package_dependencies_ffmpeg": "0.0.4",
+ "npm_package_dependencies_cookie_session": "^2.0.0",
+ "npm_package_dependencies_color": "^3.2.1",
+ "npm_package_devDependencies__types_webpack": "^4.41.32",
+ "npm_package_devDependencies__types_request_promise": "^4.1.48",
+ "npm_package_devDependencies__types_prosemirror_schema_list": "^1.0.3",
+ "npm_config_send_metrics": "",
+ "npm_config_save_bundle": "",
+ "npm_package_dependencies_web_request": "^1.0.7",
+ "npm_package_dependencies_react_datepicker": "^3.8.0",
+ "npm_package_dependencies_express": "^4.17.3",
+ "npm_package_dependencies_D": "^1.0.0",
+ "npm_package_dependencies__types_formidable": "1.0.31",
+ "npm_package_devDependencies__types_rc_switch": "^1.9.2",
+ "npm_package_devDependencies__types_prosemirror_dev_tools": "^2.1.0",
+ "npm_package_devDependencies__types_jquery": "^3.5.14",
+ "npm_config_umask": "0022",
+ "npm_config_node_options": "",
+ "npm_config_init_version": "1.0.0",
+ "npm_package_dependencies_https": "^1.0.0",
+ "npm_package_dependencies_array_batcher": "^1.2.3",
+ "npm_package_dependencies__fortawesome_free_regular_svg_icons": "^6.3.0",
+ "npm_package_devDependencies__types_shelljs": "^0.8.11",
+ "npm_package_devDependencies__types_libxmljs": "^0.18.7",
+ "npm_package_devDependencies__types_express_validator": "^3.0.0",
+ "npm_package_devDependencies__types_bluebird": "^3.5.36",
+ "npm_config_init_author_name": "",
+ "npm_config_git": "git",
+ "npm_config_scope": "",
+ "npm_package_dependencies_react_select": "^3.2.0",
+ "npm_package_dependencies_pdf_parse": "^1.1.1",
+ "npm_package_dependencies_colors": "^1.4.0",
+ "npm_package_dependencies_archiver": "^3.1.1",
+ "npm_package_devDependencies_css_loader": "^2.1.1",
+ "npm_package_devDependencies__types_socket_io_client": "^1.4.36",
+ "SECURITYSESSIONID": "186a6",
+ "npm_config_unsafe_perm": "true",
+ "npm_config_tmp": "/var/folders/yk/p_39q8jn673c5p8_66mcxm7r0000gn/T",
+ "npm_config_onload_script": "",
+ "npm_package_dependencies_serializr": "^1.5.4",
+ "npm_package_dependencies_fit_curve": "^0.1.7",
+ "npm_package_dependencies__webscopeio_react_textarea_autocomplete": "^4.9.1",
+ "npm_package_dependencies__types_three": "^0.126.2",
+ "npm_package_devDependencies_ts_node_dev": "^2.0.0",
+ "npm_node_execpath": "/Users/sarah/.nvm/versions/node/v12.16.0/bin/node",
+ "npm_config_prefix": "/Users/sarah/.nvm/versions/node/v12.16.0",
+ "npm_config_link": "",
+ "npm_config_format_package_lock": "true",
+ "npm_package_dependencies_passport": "^0.4.0",
+ "npm_package_devDependencies_eslint_plugin_react": "^7.30.1",
+ "npm_package_devDependencies__types_react_table": "^6.8.9",
+ "npm_package_devDependencies__types_react_reconciler": "^0.26.4",
+ "NODE_OPTIONS": "--max_old_space_size=4096",
+ "TS_NODE_DEV": "true",
+ "_CLIENT_OPENAI_KEY": "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",
+ "VIPSHOME": "/usr/local/Cellar/vips/8.8.1",
+ "TYPESCRIPT_PATH": "/Users/sarah/Desktop/dash/Dash-Web/node_modules/typescript/lib/typescript.js",
+ "TSCONFIG": "/Users/sarah/Desktop/dash/Dash-Web/tsconfig.json",
+ "COMPILER_OPTIONS": "{}",
+ "TSLINT": "true",
+ "CONTEXT": "/Users/sarah/Desktop/dash/Dash-Web",
+ "TSLINTAUTOFIX": "false",
+ "ESLINT": "false",
+ "ESLINT_OPTIONS": "{}",
+ "WATCH": "",
+ "WORK_DIVISION": "1",
+ "MEMORY_LIMIT": "2048",
+ "CHECK_SYNTACTIC_ERRORS": "false",
+ "USE_INCREMENTAL_API": "true",
+ "VUE": "false"
+ },
+ "userLimits": {
+ "core_file_size_blocks": {
+ "soft": 0,
+ "hard": "unlimited"
+ },
+ "data_seg_size_kbytes": {
+ "soft": "unlimited",
+ "hard": "unlimited"
+ },
+ "file_size_blocks": {
+ "soft": "unlimited",
+ "hard": "unlimited"
+ },
+ "max_locked_memory_bytes": {
+ "soft": "unlimited",
+ "hard": "unlimited"
+ },
+ "max_memory_size_kbytes": {
+ "soft": "unlimited",
+ "hard": "unlimited"
+ },
+ "open_files": {
+ "soft": 1048575,
+ "hard": "unlimited"
+ },
+ "stack_size_bytes": {
+ "soft": 8388608,
+ "hard": 67104768
+ },
+ "cpu_time_seconds": {
+ "soft": "unlimited",
+ "hard": "unlimited"
+ },
+ "max_user_processes": {
+ "soft": 2784,
+ "hard": 4176
+ },
+ "virtual_memory_kbytes": {
+ "soft": "unlimited",
+ "hard": "unlimited"
+ }
+ },
+ "sharedObjects": [
+ "/Users/sarah/.nvm/versions/node/v12.16.0/bin/node",
+ "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation",
+ "/usr/lib/libobjc.A.dylib",
+ "/System/Library/PrivateFrameworks/CoreServicesInternal.framework/Versions/A/CoreServicesInternal",
+ "/usr/lib/liboah.dylib",
+ "/usr/lib/libfakelink.dylib",
+ "/usr/lib/libicucore.A.dylib",
+ "/usr/lib/libSystem.B.dylib",
+ "/System/Library/PrivateFrameworks/SoftLinking.framework/Versions/A/SoftLinking",
+ "/usr/lib/libc++abi.dylib",
+ "/usr/lib/libc++.1.dylib",
+ "/usr/lib/system/libcache.dylib",
+ "/usr/lib/system/libcommonCrypto.dylib",
+ "/usr/lib/system/libcompiler_rt.dylib",
+ "/usr/lib/system/libcopyfile.dylib",
+ "/usr/lib/system/libcorecrypto.dylib",
+ "/usr/lib/system/libdispatch.dylib",
+ "/usr/lib/system/libdyld.dylib",
+ "/usr/lib/system/libkeymgr.dylib",
+ "/usr/lib/system/libmacho.dylib",
+ "/usr/lib/system/libquarantine.dylib",
+ "/usr/lib/system/libremovefile.dylib",
+ "/usr/lib/system/libsystem_asl.dylib",
+ "/usr/lib/system/libsystem_blocks.dylib",
+ "/usr/lib/system/libsystem_c.dylib",
+ "/usr/lib/system/libsystem_collections.dylib",
+ "/usr/lib/system/libsystem_configuration.dylib",
+ "/usr/lib/system/libsystem_containermanager.dylib",
+ "/usr/lib/system/libsystem_coreservices.dylib",
+ "/usr/lib/system/libsystem_darwin.dylib",
+ "/usr/lib/system/libsystem_dnssd.dylib",
+ "/usr/lib/system/libsystem_featureflags.dylib",
+ "/usr/lib/system/libsystem_info.dylib",
+ "/usr/lib/system/libsystem_m.dylib",
+ "/usr/lib/system/libsystem_malloc.dylib",
+ "/usr/lib/system/libsystem_networkextension.dylib",
+ "/usr/lib/system/libsystem_notify.dylib",
+ "/usr/lib/system/libsystem_sandbox.dylib",
+ "/usr/lib/system/libsystem_secinit.dylib",
+ "/usr/lib/system/libsystem_kernel.dylib",
+ "/usr/lib/system/libsystem_platform.dylib",
+ "/usr/lib/system/libsystem_pthread.dylib",
+ "/usr/lib/system/libsystem_symptoms.dylib",
+ "/usr/lib/system/libsystem_trace.dylib",
+ "/usr/lib/system/libunwind.dylib",
+ "/usr/lib/system/libxpc.dylib",
+ "/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit",
+ "/System/Library/Frameworks/CoreServices.framework/Versions/A/CoreServices",
+ "/usr/lib/libDiagnosticMessagesClient.dylib",
+ "/usr/lib/libenergytrace.dylib",
+ "/usr/lib/libbsm.0.dylib",
+ "/usr/lib/libz.1.dylib",
+ "/usr/lib/system/libkxld.dylib",
+ "/System/Library/Frameworks/CFNetwork.framework/Versions/A/CFNetwork",
+ "/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/FSEvents.framework/Versions/A/FSEvents",
+ "/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/CarbonCore.framework/Versions/A/CarbonCore",
+ "/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/Metadata.framework/Versions/A/Metadata",
+ "/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/OSServices.framework/Versions/A/OSServices",
+ "/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/SearchKit.framework/Versions/A/SearchKit",
+ "/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/AE.framework/Versions/A/AE",
+ "/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/LaunchServices",
+ "/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/DictionaryServices.framework/Versions/A/DictionaryServices",
+ "/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/SharedFileList.framework/Versions/A/SharedFileList",
+ "/System/Library/Frameworks/Security.framework/Versions/A/Security",
+ "/System/Library/Frameworks/SystemConfiguration.framework/Versions/A/SystemConfiguration",
+ "/usr/lib/libapple_nghttp2.dylib",
+ "/usr/lib/libcompression.dylib",
+ "/usr/lib/libnetwork.dylib",
+ "/usr/lib/libsqlite3.dylib",
+ "/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation",
+ "/System/Library/Frameworks/Network.framework/Versions/A/Network",
+ "/usr/lib/libCoreEntitlements.dylib",
+ "/System/Library/PrivateFrameworks/MessageSecurity.framework/Versions/A/MessageSecurity",
+ "/System/Library/PrivateFrameworks/ProtocolBuffer.framework/Versions/A/ProtocolBuffer",
+ "/usr/lib/libMobileGestalt.dylib",
+ "/System/Library/PrivateFrameworks/AppleFSCompression.framework/Versions/A/AppleFSCompression",
+ "/usr/lib/libcoretls.dylib",
+ "/usr/lib/libcoretls_cfhelpers.dylib",
+ "/usr/lib/libpam.2.dylib",
+ "/usr/lib/libxar.1.dylib",
+ "/System/Library/PrivateFrameworks/CoreAutoLayout.framework/Versions/A/CoreAutoLayout",
+ "/System/Library/Frameworks/DiskArbitration.framework/Versions/A/DiskArbitration",
+ "/usr/lib/libarchive.2.dylib",
+ "/usr/lib/libxml2.2.dylib",
+ "/usr/lib/liblangid.dylib",
+ "/System/Library/Frameworks/Combine.framework/Versions/A/Combine",
+ "/usr/lib/swift/libswiftCore.dylib",
+ "/usr/lib/swift/libswiftCoreFoundation.dylib",
+ "/usr/lib/swift/libswiftDarwin.dylib",
+ "/usr/lib/swift/libswiftDispatch.dylib",
+ "/usr/lib/swift/libswiftIOKit.dylib",
+ "/usr/lib/swift/libswiftObjectiveC.dylib",
+ "/usr/lib/swift/libswiftXPC.dylib",
+ "/usr/lib/swift/libswift_Concurrency.dylib",
+ "/usr/lib/swift/libswift_StringProcessing.dylib",
+ "/usr/lib/swift/libswiftos.dylib",
+ "/System/Library/PrivateFrameworks/AppleSystemInfo.framework/Versions/A/AppleSystemInfo",
+ "/System/Library/PrivateFrameworks/IOMobileFramebuffer.framework/Versions/A/IOMobileFramebuffer",
+ "/System/Library/Frameworks/IOSurface.framework/Versions/A/IOSurface",
+ "/usr/lib/libpcap.A.dylib",
+ "/usr/lib/libdns_services.dylib",
+ "/usr/lib/liblzma.5.dylib",
+ "/usr/lib/libbz2.1.0.dylib",
+ "/usr/lib/libiconv.2.dylib",
+ "/usr/lib/libcharset.1.dylib",
+ "/usr/lib/swift/libswift_RegexParser.dylib",
+ "/usr/lib/libheimdal-asn1.dylib",
+ "/usr/lib/libCheckFix.dylib",
+ "/System/Library/PrivateFrameworks/TCC.framework/Versions/A/TCC",
+ "/System/Library/PrivateFrameworks/CoreNLP.framework/Versions/A/CoreNLP",
+ "/System/Library/PrivateFrameworks/MetadataUtilities.framework/Versions/A/MetadataUtilities",
+ "/System/Library/Frameworks/Accelerate.framework/Versions/A/Accelerate",
+ "/usr/lib/libmecab.dylib",
+ "/usr/lib/libCRFSuite.dylib",
+ "/usr/lib/libgermantok.dylib",
+ "/usr/lib/libThaiTokenizer.dylib",
+ "/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vImage.framework/Versions/A/vImage",
+ "/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/vecLib",
+ "/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libvMisc.dylib",
+ "/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libvDSP.dylib",
+ "/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib",
+ "/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libLAPACK.dylib",
+ "/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libLinearAlgebra.dylib",
+ "/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libSparseBLAS.dylib",
+ "/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libQuadrature.dylib",
+ "/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBNNS.dylib",
+ "/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libSparse.dylib",
+ "/System/Library/PrivateFrameworks/MIL.framework/Versions/A/MIL",
+ "/System/Library/Frameworks/OpenDirectory.framework/Versions/A/Frameworks/CFOpenDirectory.framework/Versions/A/CFOpenDirectory",
+ "/System/Library/Frameworks/OpenDirectory.framework/Versions/A/OpenDirectory",
+ "/System/Library/PrivateFrameworks/APFS.framework/Versions/A/APFS",
+ "/System/Library/Frameworks/SecurityFoundation.framework/Versions/A/SecurityFoundation",
+ "/usr/lib/libutil.dylib",
+ "/System/Library/PrivateFrameworks/InstalledContentLibrary.framework/Versions/A/InstalledContentLibrary",
+ "/System/Library/PrivateFrameworks/CoreServicesStore.framework/Versions/A/CoreServicesStore",
+ "/usr/lib/libapp_launch_measurement.dylib",
+ "/System/Library/PrivateFrameworks/AppleMobileFileIntegrity.framework/Versions/A/AppleMobileFileIntegrity",
+ "/usr/lib/libmis.dylib",
+ "/System/Library/PrivateFrameworks/MobileSystemServices.framework/Versions/A/MobileSystemServices",
+ "/System/Library/PrivateFrameworks/ConfigProfileHelper.framework/Versions/A/ConfigProfileHelper",
+ "/System/Library/PrivateFrameworks/CoreAnalytics.framework/Versions/A/CoreAnalytics",
+ "/System/Library/PrivateFrameworks/AppleSauce.framework/Versions/A/AppleSauce",
+ "/System/Library/PrivateFrameworks/LanguageModeling.framework/Versions/A/LanguageModeling",
+ "/usr/lib/libxslt.1.dylib",
+ "/usr/lib/libcmph.dylib",
+ "/System/Library/PrivateFrameworks/CoreEmoji.framework/Versions/A/CoreEmoji",
+ "/System/Library/PrivateFrameworks/LinguisticData.framework/Versions/A/LinguisticData",
+ "/System/Library/PrivateFrameworks/Lexicon.framework/Versions/A/Lexicon",
+ "/System/Library/PrivateFrameworks/BackgroundTaskManagement.framework/Versions/A/BackgroundTaskManagement",
+ "/usr/lib/libTLE.dylib",
+ "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/ApplicationServices",
+ "/System/Library/Frameworks/CoreGraphics.framework/Versions/A/CoreGraphics",
+ "/System/Library/Frameworks/CoreText.framework/Versions/A/CoreText",
+ "/System/Library/Frameworks/ImageIO.framework/Versions/A/ImageIO",
+ "/System/Library/Frameworks/ColorSync.framework/Versions/A/ColorSync",
+ "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/ATS.framework/Versions/A/ATS",
+ "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/HIServices.framework/Versions/A/HIServices",
+ "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/PrintCore.framework/Versions/A/PrintCore",
+ "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/QD.framework/Versions/A/QD",
+ "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/ColorSyncLegacy.framework/Versions/A/ColorSyncLegacy",
+ "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/SpeechSynthesis.framework/Versions/A/SpeechSynthesis",
+ "/System/Library/PrivateFrameworks/SkyLight.framework/Versions/A/SkyLight",
+ "/System/Library/PrivateFrameworks/FontServices.framework/libFontParser.dylib",
+ "/System/Library/PrivateFrameworks/RunningBoardServices.framework/Versions/A/RunningBoardServices",
+ "/System/Library/PrivateFrameworks/IOSurfaceAccelerator.framework/Versions/A/IOSurfaceAccelerator",
+ "/System/Library/PrivateFrameworks/WatchdogClient.framework/Versions/A/WatchdogClient",
+ "/System/Library/Frameworks/CoreDisplay.framework/Versions/A/CoreDisplay",
+ "/System/Library/Frameworks/CoreMedia.framework/Versions/A/CoreMedia",
+ "/System/Library/PrivateFrameworks/IOAccelerator.framework/Versions/A/IOAccelerator",
+ "/System/Library/Frameworks/Metal.framework/Versions/A/Metal",
+ "/System/Library/Frameworks/CoreVideo.framework/Versions/A/CoreVideo",
+ "/System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/MetalPerformanceShaders",
+ "/System/Library/PrivateFrameworks/MultitouchSupport.framework/Versions/A/MultitouchSupport",
+ "/System/Library/Frameworks/QuartzCore.framework/Versions/A/QuartzCore",
+ "/System/Library/Frameworks/VideoToolbox.framework/Versions/A/VideoToolbox",
+ "/System/Library/PrivateFrameworks/BaseBoard.framework/Versions/A/BaseBoard",
+ "/System/Library/PrivateFrameworks/AppleJPEG.framework/Versions/A/AppleJPEG",
+ "/usr/lib/libexpat.1.dylib",
+ "/System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libPng.dylib",
+ "/System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libTIFF.dylib",
+ "/System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libGIF.dylib",
+ "/System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libJP2.dylib",
+ "/usr/lib/libate.dylib",
+ "/System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libJPEG.dylib",
+ "/System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libRadiance.dylib",
+ "/System/Library/PrivateFrameworks/GPUWrangler.framework/Versions/A/GPUWrangler",
+ "/System/Library/PrivateFrameworks/IOPresentment.framework/Versions/A/IOPresentment",
+ "/System/Library/PrivateFrameworks/DSExternalDisplay.framework/Versions/A/DSExternalDisplay",
+ "/System/Library/PrivateFrameworks/GPUCompiler.framework/Versions/31001/Libraries/libllvm-flatbuffers.dylib",
+ "/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libCoreFSCache.dylib",
+ "/System/Library/PrivateFrameworks/GPUCompiler.framework/Versions/31001/Libraries/libGPUCompilerUtils.dylib",
+ "/System/Library/PrivateFrameworks/CMCaptureCore.framework/Versions/A/CMCaptureCore",
+ "/usr/lib/libspindump.dylib",
+ "/System/Library/Frameworks/CoreAudio.framework/Versions/A/CoreAudio",
+ "/System/Library/Frameworks/ExtensionFoundation.framework/Versions/A/ExtensionFoundation",
+ "/System/Library/PrivateFrameworks/CoreTime.framework/Versions/A/CoreTime",
+ "/System/Library/PrivateFrameworks/AppServerSupport.framework/Versions/A/AppServerSupport",
+ "/System/Library/PrivateFrameworks/perfdata.framework/Versions/A/perfdata",
+ "/System/Library/PrivateFrameworks/AudioToolboxCore.framework/Versions/A/AudioToolboxCore",
+ "/System/Library/PrivateFrameworks/caulk.framework/Versions/A/caulk",
+ "/usr/lib/libAudioStatistics.dylib",
+ "/System/Library/PrivateFrameworks/SystemPolicy.framework/Versions/A/SystemPolicy",
+ "/usr/lib/libSMC.dylib",
+ "/System/Library/Frameworks/CoreMIDI.framework/Versions/A/CoreMIDI",
+ "/usr/lib/libAudioToolboxUtility.dylib",
+ "/System/Library/PrivateFrameworks/OSAServicesClient.framework/Versions/A/OSAServicesClient",
+ "/usr/lib/libperfcheck.dylib",
+ "/System/Library/PrivateFrameworks/PlugInKit.framework/Versions/A/PlugInKit",
+ "/System/Library/PrivateFrameworks/AssertionServices.framework/Versions/A/AssertionServices",
+ "/System/Library/Frameworks/OpenGL.framework/Versions/A/OpenGL",
+ "/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGLU.dylib",
+ "/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGFXShared.dylib",
+ "/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGL.dylib",
+ "/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGLImage.dylib",
+ "/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libCVMSPluginSupport.dylib",
+ "/usr/lib/libRosetta.dylib",
+ "/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libCoreVMClient.dylib",
+ "/System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSCore.framework/Versions/A/MPSCore",
+ "/System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSImage.framework/Versions/A/MPSImage",
+ "/System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSNeuralNetwork.framework/Versions/A/MPSNeuralNetwork",
+ "/System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSMatrix.framework/Versions/A/MPSMatrix",
+ "/System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSRayIntersector.framework/Versions/A/MPSRayIntersector",
+ "/System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSNDArray.framework/Versions/A/MPSNDArray",
+ "/System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSFunctions.framework/Versions/A/MPSFunctions",
+ "/System/Library/PrivateFrameworks/MetalTools.framework/Versions/A/MetalTools",
+ "/System/Library/PrivateFrameworks/AggregateDictionary.framework/Versions/A/AggregateDictionary",
+ "/usr/lib/libIOReport.dylib",
+ "/System/Library/Frameworks/CoreImage.framework/Versions/A/CoreImage",
+ "/System/Library/PrivateFrameworks/PhotosensitivityProcessing.framework/Versions/A/PhotosensitivityProcessing",
+ "/System/Library/Frameworks/OpenCL.framework/Versions/A/OpenCL",
+ "/System/Library/PrivateFrameworks/GraphVisualizer.framework/Versions/A/GraphVisualizer",
+ "/System/Library/PrivateFrameworks/FontServices.framework/Versions/A/FontServices",
+ "/System/Library/Frameworks/UniformTypeIdentifiers.framework/Versions/A/UniformTypeIdentifiers",
+ "/System/Library/PrivateFrameworks/OTSVG.framework/Versions/A/OTSVG",
+ "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/ATS.framework/Versions/A/Resources/libFontRegistry.dylib",
+ "/System/Library/PrivateFrameworks/FontServices.framework/libhvf.dylib",
+ "/System/Library/PrivateFrameworks/FontServices.framework/libXTFontStaticRegistryData.dylib",
+ "/usr/lib/swift/libswiftMetal.dylib",
+ "/usr/lib/swift/libswiftsimd.dylib",
+ "/System/Library/PrivateFrameworks/VideoToolboxParavirtualizationSupport.framework/Versions/A/VideoToolboxParavirtualizationSupport",
+ "/System/Library/PrivateFrameworks/AppleVA.framework/Versions/A/AppleVA",
+ "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/ATSUI.framework/Versions/A/ATSUI",
+ "/usr/lib/libcups.2.dylib",
+ "/System/Library/Frameworks/Kerberos.framework/Versions/A/Kerberos",
+ "/System/Library/Frameworks/GSS.framework/Versions/A/GSS",
+ "/usr/lib/libresolv.9.dylib",
+ "/System/Library/PrivateFrameworks/Heimdal.framework/Versions/A/Heimdal",
+ "/System/Library/Frameworks/Kerberos.framework/Versions/A/Libraries/libHeimdalProxy.dylib",
+ "/System/Library/PrivateFrameworks/CommonAuth.framework/Versions/A/CommonAuth",
+ "/System/Library/Frameworks/AVFAudio.framework/Versions/A/AVFAudio",
+ "/System/Library/PrivateFrameworks/AXCoreUtilities.framework/Versions/A/AXCoreUtilities",
+ "/System/Library/Frameworks/AudioToolbox.framework/Versions/A/AudioToolbox",
+ "/System/Library/PrivateFrameworks/AudioSession.framework/Versions/A/AudioSession",
+ "/System/Library/Frameworks/IOBluetooth.framework/Versions/A/IOBluetooth",
+ "/System/Library/PrivateFrameworks/MediaExperience.framework/Versions/A/MediaExperience",
+ "/System/Library/PrivateFrameworks/AudioSession.framework/libSessionUtility.dylib",
+ "/System/Library/PrivateFrameworks/AudioResourceArbitration.framework/Versions/A/AudioResourceArbitration",
+ "/System/Library/PrivateFrameworks/PowerLog.framework/Versions/A/PowerLog",
+ "/System/Library/Frameworks/CoreData.framework/Versions/A/CoreData",
+ "/System/Library/Frameworks/CoreBluetooth.framework/Versions/A/CoreBluetooth",
+ "/System/Library/Frameworks/AudioUnit.framework/Versions/A/AudioUnit",
+ "/System/Library/PrivateFrameworks/CoreUtils.framework/Versions/A/CoreUtils",
+ "/System/Library/PrivateFrameworks/CoreUtilsExtras.framework/Versions/A/CoreUtilsExtras",
+ "/System/Library/PrivateFrameworks/IO80211.framework/Versions/A/IO80211",
+ "/System/Library/PrivateFrameworks/MobileKeyBag.framework/Versions/A/MobileKeyBag",
+ "/Users/sarah/Desktop/dash/Dash-Web/node_modules/fsevents/build/Release/fse.node"
+ ]
+}
\ No newline at end of file
diff --git a/src/client/apis/gpt/GPT.ts b/src/client/apis/gpt/GPT.ts
index caddc41a2..8e08265d6 100644
--- a/src/client/apis/gpt/GPT.ts
+++ b/src/client/apis/gpt/GPT.ts
@@ -13,6 +13,7 @@ enum GPTCallType {
MERMAID = 'mermaid',
DATA = 'data',
STACK = 'stack',
+ PRONUNCIATION = 'pronunciation',
}
type GPTCallOpts = {
@@ -58,7 +59,13 @@ const callTypeMap: { [type: string]: GPTCallOpts } = {
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 the Rubric is incorrect, explain why. If there are no differences, say correct. If it is empty, say there is nothing for me to evaluate.',
+ 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 the Rubric is incorrect, explain why. If there are no differences, say correct. If it is empty, say there is nothing for me to evaluate. If it is comparing two words, look for spelling and not capitalization and not punctuation.',
+ },
+ pronunciation: {
+ model: 'gpt-4-turbo',
+ maxTokens: 1024,
+ temp: 0,
+ prompt: 'List unique differences between the pronunciation of the ',
},
};
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index e095bc659..c712dba21 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -262,6 +262,7 @@ export class CurrentUserUtils {
}
return plotly;
}
+
const plotlyView = (opts:DocumentOptions) => {
const rtfield = new RichTextField(JSON.stringify(
{doc: {type:"doc",content:[
diff --git a/src/client/util/DictationManager.ts b/src/client/util/DictationManager.ts
index bc9fe813f..b9a465515 100644
--- a/src/client/util/DictationManager.ts
+++ b/src/client/util/DictationManager.ts
@@ -350,7 +350,7 @@ export namespace DictationManager {
const head = 3;
const anchor = head + prompt.length;
const proseMirrorState = `{"doc":{"type":"doc","content":[{"type":"ordered_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"type":"text","text":"${prompt}"}]}]}]}]},"selection":{"type":"text","anchor":${anchor},"head":${head}}}`;
- proto.data = new RichTextField(proseMirrorState);
+ proto.data = new RichTextField(proseMirrorState, prompt);
proto.backgroundColor = '#eeffff';
target.props.addDocTab(newBox, OpenWhere.addRight);
},
diff --git a/src/client/views/.MetadataEntryMenu.tsx.icloud b/src/client/views/.MetadataEntryMenu.tsx.icloud
new file mode 100644
index 000000000..ada9927c9
Binary files /dev/null and b/src/client/views/.MetadataEntryMenu.tsx.icloud differ
diff --git a/src/client/views/.Touchable.tsx.icloud b/src/client/views/.Touchable.tsx.icloud
new file mode 100644
index 000000000..e458bbe47
Binary files /dev/null and b/src/client/views/.Touchable.tsx.icloud differ
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index 6976deea5..c45b1494b 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -2,7 +2,7 @@
/* eslint-disable jsx-a11y/click-events-have-key-events */
/* eslint-disable react/jsx-props-no-spreading */
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, makeObservable, observable } from 'mobx';
+import { action, computed, makeObservable, observable, trace } from 'mobx';
import { observer } from 'mobx-react';
import { Docs } from '../../documents/Documents';
import * as React from 'react';
@@ -56,6 +56,8 @@ export class CollectionCarouselView extends CollectionSubView() {
Doc.setDocFilter(this.Document, 'star', undefined, 'match');
this.layoutDoc.practiceMode = practiceMode.NORMAL;
this.layoutDoc._carousel_index = 0;
+ this.carouselItems.forEach(item => { item.layout[this.practiceField] = undefined}); //prettier-ignore
+ console.log(this.carouselItems.length);
}
componentWillUnmount() {
@@ -202,6 +204,7 @@ export class CollectionCarouselView extends CollectionSubView() {
};
@computed get content() {
+ trace();
if (this.layoutDoc._carousel_index === this.carouselItems.length && this.layoutDoc._carousel_index !== 0) {
this.move(1);
}
diff --git a/src/client/views/nodes/.LinkAnchorBox.scss.icloud b/src/client/views/nodes/.LinkAnchorBox.scss.icloud
new file mode 100644
index 000000000..193619551
Binary files /dev/null and b/src/client/views/nodes/.LinkAnchorBox.scss.icloud differ
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index 9deed4de4..5d0ed7eab 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -10,7 +10,7 @@ import { DateField } from '../../../fields/DateField';
import { Doc } from '../../../fields/Doc';
import { DocData } from '../../../fields/DocSymbols';
import { ComputedField } from '../../../fields/ScriptField';
-import { Cast, DateCast, NumCast } from '../../../fields/Types';
+import { Cast, DateCast, DocCast, NumCast } from '../../../fields/Types';
import { AudioField, nullAudio } from '../../../fields/URLField';
import { formatTime } from '../../../Utils';
import { Docs } from '../../documents/Documents';
@@ -29,6 +29,7 @@ import './AudioBox.scss';
import { DocumentView } from './DocumentView';
import { FieldView, FieldViewProps } from './FieldView';
import { OpenWhere } from './OpenWhere';
+import axios from 'axios';
/**
* AudioBox
@@ -257,6 +258,8 @@ export class AudioBox extends ViewBoxAnnotatableComponent() {
const [{ result }] = await Networking.UploadFilesToServer({ file: e.data });
if (!(result instanceof Error)) {
this.Document[this.fieldKey] = new AudioField(result.accessPaths.agnostic.client);
+ this.Document.url = result.accessPaths.agnostic.client;
+ await this.pushInfo();
}
};
this._recordStart = new Date().getTime();
@@ -284,9 +287,23 @@ export class AudioBox extends ViewBoxAnnotatableComponent() {
}
};
+ pushInfo = async () => {
+ const audio = {
+ file: this.path,
+ };
+ const response = await axios.post('http://localhost:105/recognize/', audio, {
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ });
+ this.Document[DocData].phoneticTranscription = response.data['transcription'];
+ console.log('RESPONSE: ' + response.data['transcription']);
+ };
+
// context menu
specificContextMenu = (): void => {
const funcs: ContextMenuProps[] = [];
+ // funcs.push({ description: 'Push info', event: this.pushInfo, icon: 'redo-alt' });
funcs.push({
description: (this.layoutDoc.hideAnchors ? "Don't hide" : 'Hide') + ' anchors',
event: () => { this.layoutDoc.hideAnchors = !this.layoutDoc.hideAnchors; }, // prettier-ignore
diff --git a/src/client/views/nodes/ComparisonBox.scss b/src/client/views/nodes/ComparisonBox.scss
index 41ff56514..56a1e4fcc 100644
--- a/src/client/views/nodes/ComparisonBox.scss
+++ b/src/client/views/nodes/ComparisonBox.scss
@@ -26,11 +26,12 @@
.submit-button {
position: absolute;
padding-bottom: 10px;
+ padding-top: 5px;
padding-left: 5px;
padding-right: 5px;
// width: 80%;
border-radius: 2px;
- height: 15%;
+ height: 17%;
display: inline-block;
bottom: 0;
// right: 0;
@@ -48,8 +49,13 @@
}
}
+ &.pronunciation {
+ width: 40%;
+ align-items: center;
+ justify-content: center;
+ }
&.submit {
- width: 80%;
+ width: 40%;
// float: right;
// position: absolute;
diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx
index 3d2a1f0a9..ce29a63b4 100644
--- a/src/client/views/nodes/ComparisonBox.tsx
+++ b/src/client/views/nodes/ComparisonBox.tsx
@@ -1,4 +1,5 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { MathJax, MathJaxContext } from 'better-react-mathjax';
import { Tooltip } from '@mui/material';
import { action, computed, makeObservable, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
@@ -33,6 +34,8 @@ import { FollowLinkScript } from '../../documents/DocUtils';
import { schema } from '../nodes/formattedText/schema_rts';
import { Id } from '../../../fields/FieldSymbols';
import axios from 'axios';
+import ReactMarkdown from 'react-markdown';
+import { WebField, nullAudio } from '../../../fields/URLField';
const API_URL = 'https://api.unsplash.com/search/photos';
@observer
@@ -51,9 +54,13 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
@observable private _outputValue = '';
@observable private _loading = false;
@observable private _isEmpty = false;
+ @observable private _audio: Doc = Docs.Create.TextDocument('');
@observable childActive = false;
@observable _yRelativeToTop: boolean = true;
@observable _animating = '';
+ @observable mathJaxConfig = {
+ loader: { load: ['input/asciimath'] },
+ };
private _ref = React.createRef();
get revealOp() {
@@ -237,11 +244,71 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
this.childActive = true;
};
- @action handleRenderGPTClick = () => {
+ @action handleRenderGPTClick = async () => {
// Call the GPT model and get the output
+ // await this.pushInfo();
+ // console.log('PHONETIC TRANSCRIPTION: ' + DocCast(this._audio)[DocData]);
+ // this.Document.audio = this._audio;
+ console.log('Phonetic transcription: ' + DocCast(this.Document.audio).phoneticTranscription);
+ const phonTrans = DocCast(this.Document.audio).phoneticTranscription;
+ if (phonTrans) {
+ this._inputValue = StrCast(phonTrans);
+ console.log('INPUT:' + this._inputValue);
+ this.askGPTPhonemes(this._inputValue);
+ } else if (this._inputValue) this.askGPT(GPTCallType.QUIZ);
this.layoutDoc[`_${this._props.fieldKey}_usePath`] = 'alternate';
this._outputValue = '';
- if (this._inputValue) this.askGPT(GPTCallType.QUIZ);
+ };
+
+ askGPTPhonemes = async (phonemes: string) => {
+ const question0 = 'These phonemes should match "what is your name": ' + phonemes + 'Use the structure of this response as guidance: "Your pronunciation of the vowel in "what" is not front enough. It should be pronounced like /uh/."';
+ const question =
+ 'Match the following phonemes with each word in "what is your name": ' +
+ phonemes +
+ '. Note if a letter is added or missing that changes the meaning. If the mismatches are not allophones of the same phoneme and they are far away from each other on the vowel chart, list the difference. For the mismatches, use the structure of this response as guidance: "Your pronunciation of the vowel in "what" is not front enough. It should be pronounced like /uh/."';
+ const question1 =
+ 'Consider all phonetic transcriptions of "what is your name" with different vowel pronunications. Compares these phonemes with that phonetic transcription: ' +
+ phonemes +
+ '. If the differences are not allophones of the same phoneme and they are far away from each other on the vowel chart, list the difference. If it is missing or added a letter, say that.';
+ //Only describe sound changes that will change the meaning drastically. Provide two sentences describing this. Do not list differences that do not change the meaning.';
+ const question2 = 'Is this a valid phonetic transcription of the phrase "what is your name": ' + phonemes + '.';
+ // If the difference found will definitely make the word be not understood and change the meaning, then list it. If the difference is minimal or the sound matches, do not list it.';
+ //These phonemes are supposed to match the pronunciation of ' +
+ //'hello: ' +
+ //phonemes +
+ //'. If there is a difference in sound that would change the meaning of the word or sentence, such as "pen" vs. "pin", describe that. Otherwise say "good job."';
+ // Identify any differences in pronunciation that would change the meaning of the intended word or sentence and only list differences that would change the meaning. If there are no major differences, say "Good job." If there are differences, describe it in terms of sounds in sentences.';
+ // const question =
+ // 'These phonemes are supposed to match the pronunciation of ' +
+ // StrCast(RTFCast(DocCast(this.dataDoc[this.fieldKey + '_0']).text)?.Text) +
+ // '. Identify any differences in pronunciation that would change the meaning of the intended word or sentence.';
+ console.log(question);
+ const res = await gptAPICall(question, GPTCallType.PRONUNCIATION);
+ console.log('GPT: ' + res);
+ if (!res) {
+ console.error('GPT call failed');
+ return;
+ }
+ // 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;
+ // this._loading = true;
+ };
+
+ pushInfo = async () => {
+ const formData = new FormData();
+
+ console.log(DocCast(this._audio).dataDoc);
+ const audio = {
+ file: this._audio.url,
+ };
+ const response = await axios.post('http://localhost:105/recognize/', audio, {
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ });
+ this.Document.phoneticTranscription = response.data['transcription'];
+ console.log('RESPONSE: ' + response.data['transcription']);
};
@action handleHover = () => {
@@ -391,22 +458,41 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
var collectionArr: Doc[] = [];
for (let i = 1; i < senArr?.length!; i++) {
const newDoc = Docs.Create.ComparisonDocument(senArr![i], { _layout_isFlashcard: true, _width: 300, _height: 300 });
- // newDoc.text = senArr![i];
- // const dataSplit = StrCast(this.dataDoc.data).includes('Keyword: ') ? StrCast(this.dataDoc.data).split('Keyword: ') : StrCast(this.dataDoc.data).split('Answer: ');
- // newDoc[this.fieldKey + '_0'] = Docs.Create.TextDocument(dataSplit[1]);
- // newDoc[this.fieldKey + '_1'] = Docs.Create.TextDocument(dataSplit[0]);
- // newDoc['backgroundColor'] = 'lightgray';
- // newDoc.image =
+
if (StrCast(senArr![i]).includes('Keyword: ')) {
- console.log('Here');
- const img = await this.fetchImages(StrCast(senArr![i]).split('Keyword: ')[1]);
- console.log(img);
- DocCast(newDoc).image = img;
- // DocCast(DocCast(newDoc).dataDoc)['image'] = img;
- Doc.AddToMyOverlay(img);
+ const question = StrCast(senArr![i]).split('Keyword: ');
+ const img = await this.fetchImages(question[1]);
+ // newDoc['image'] = img;
+ // const newDoc = Docs.Create.TextDocument(dataSplit[1]);
+ const textSide1 = question[0].includes('Answer: ') ? question[0].split('Answer: ')[0] : question[0];
+ const textDoc1 = Docs.Create.TextDocument(question[0]);
+ const rtfiel = new RichTextField(
+ JSON.stringify({
+ doc: {
+ type: 'doc',
+ content: [
+ {
+ type: 'paragraph',
+ attrs: { align: null, color: null, id: null, indent: null, inset: null, lineSpacing: null, paddingBottom: null, paddingTop: null },
+ content: [
+ { type: 'text', text: question[0].includes('Answer: ') ? question[0].split('Answer: ')[0] : question[0] },
+ { type: 'dashDoc', attrs: { width: '200px', height: '200px', title: 'dashDoc', float: 'unset', hidden: false, docId: img![Id] } },
+ ],
+ },
+ ],
+ },
+ selection: { type: 'text', anchor: 2, head: 2 },
+ }),
+ textSide1
+ );
+
+ textDoc1[DocData].text = rtfiel;
+ DocCast(newDoc)[DocData][this.fieldKey + '_1'] = textDoc1;
+
+ DocCast(newDoc)[DocData][this.fieldKey + '_0'] = Docs.Create.TextDocument(question[0].includes('Answer: ') ? question[0].split('Answer: ')[1] : question[1]);
+ // Doc.AddToMyOverlay(img!);
}
- console.log('ARR' + i + senArr![i]);
collectionArr.push(newDoc);
}
this.createFlashcardPile(collectionArr, true);
@@ -473,7 +559,10 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
if (callType == GPTCallType.CHATCARD) {
DocCast(this.dataDoc[this.props.fieldKey + '_0'])[DocData].text = res;
// this.flipFlashcard();
- } else if (callType == GPTCallType.QUIZ) this._outputValue = res;
+ } else if (callType == GPTCallType.QUIZ) {
+ console.log(this._inputValue);
+ this._outputValue = res.replace(/UserAnswer/g, "user's answer").replace(/Rubric/g, 'rubric');
+ }
// DocCast(this.dataDoc[this.props.fieldKey + '_0'])[DocData].text = res;
// this._outputValue = res;
else if (callType === GPTCallType.FLASHCARD) {
@@ -511,7 +600,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
if (i.className !== 'ProseMirror-separator') await this.getImageDesc(i.src);
}
this._loading = false;
- this.flipFlashcard();
+ // this.flipFlashcard();
}
// console.log('HI' + this.ProseRef?.getElementsByTagName('img'));
};
@@ -558,22 +647,9 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
_height: 150,
title: '--snapshot' + NumCast(this.layoutDoc._layout_currentTimecode) + ' image-',
});
- // return imageSnapshot;
imageSnapshot['x'] = this.layoutDoc['x'];
imageSnapshot['y'] = this.layoutDoc['y'];
-
- // const newDoc = Docs.Create.TextDocument(selection);
- // newDoc.text = selection;
- // newDoc['backgroundColor'] = 'lightgray';
-
- // Doc.AddToMyOverlay(imageSnapshot);
return imageSnapshot;
- return data.results[0].urls.small;
- // Doc.AddEmbedding(newDoc, imageSnapshot);
- // Doc.MakeEmbedding(imageSnapshot);
- // return imageSnapshot;
- // imageSnapshot['zIndex'] = 20000;
- // this._props.DocumentView?.()._props.addDocument?.(newDoc);
} catch (error) {
console.log(error);
}
@@ -660,6 +736,14 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
ContextMenu.Instance.displayMenu(x, y);
};
+ evaluatePronunciation = () => {
+ const newAudio = Docs.Create.AudioDocument(nullAudio, { _width: 200, _height: 100 });
+ this.Document.audio = newAudio[DocData];
+ // DocCast(this.Document.embedContainer)()._props.addDocument?.(newAudio);
+ this._props.DocumentView?.()._props.addDocument?.(newAudio);
+ // Doc.AddToMyOverlay(newAudio);
+ };
+
render() {
const clearButton = (which: string) => (
remove }>
@@ -680,6 +764,8 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent
()
// whichDoc['backgroundColor'] = this.layoutDoc['backgroundColor'];
return targetDoc || layoutString ? (
+ //
+ //
<>
()
hideLinkButton
pointerEvents={this.childActive ? undefined : returnNone}
/>
+ {/* */}
{/* {layoutString ? null : clearButton(whichSlot)}
*/}
- > // placeholder image if doc is missingleft: `${NumCast(this.layoutDoc.width, 200) - 33}px`
+ {/* // placeholder image if doc is missingleft: `${NumCast(this.layoutDoc.width, 200) - 33}px` */}
+ >
) : (
@@ -732,79 +820,36 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent
()
if (!this.dataDoc[this.fieldKey + '_0'] && !this._isEmpty) {
const dataSplit = StrCast(this.dataDoc.data).includes('Keyword: ') ? StrCast(this.dataDoc.data).split('Keyword: ') : StrCast(this.dataDoc.data).split('Answer: ');
const newDoc = Docs.Create.TextDocument(dataSplit[1]);
- if (this.Document.image) DocCast(newDoc).image = DocCast(this.Document.image);
- // console.log('D' + this.Document.image);
- //if (DocCast(DocCast(newDoc).dataDoc)) DocCast(DocCast(newDoc).dataDoc)['image'] = this.dataDoc['image'];
-
- // console.log('HI' + this.Document.image);
- // const imageSnapshot = Docs.Create.ImageDocument(StrCast(this.Document.image), {
- // _nativeWidth: Doc.NativeWidth(this.layoutDoc),
- // _nativeHeight: Doc.NativeHeight(this.layoutDoc),
- // x: NumCast(this.layoutDoc.x),
- // y: NumCast(this.layoutDoc.y),
- // onClick: FollowLinkScript(),
- // _width: 150,
- // _height: 150,
- // title: '--snapshot' + NumCast(this.layoutDoc._layout_currentTimecode) + ' image-',
- // });
- // // return imageSnapshot;
- // imageSnapshot['x'] = this.layoutDoc['x'];
- // imageSnapshot['y'] = this.layoutDoc['y'];
-
- // const newDoc = Docs.Create.TextDocument(selection);
- // newDoc.text = selection;
- // newDoc['backgroundColor'] = 'lightgray';
- // newDoc.data = imageSnapshot;
- // this.createDropTarget(this., this.fieldKey + '_0', 0)
- // Doc.AddEmbedding(imageSnapshot, newDoc);
- // Doc.SetContainer(imageSnapshot, newDoc);
- // Doc.AddToMyOverlay(imageSnapshot);
-
- // if (StrCast(this.dataDoc.data).includes('Keyword: ')) {
- // console.log('HERE' + this.dataDoc.data);
- // this.fetchImages(StrCast(this.dataDoc.data).split('Keyword: ')[1]);
- // }
- // // const node = schema.nodes.dashDoc.create({
- // // width: NumCast(newDoc._width),
- // // height: NumCast(newDoc._height),
- // // title: 'dashDoc',
- // // docId: newDoc[Id],
- // // float: 'unset',
- // // });
- // // Doc.AddEmbedding(images!, newDoc);
- // // Doc.SetContainer(images, this.newDoc);
- // } else {
- // newDoc.text = dataSplit[1];
- // newDoc['backgroundColor'] = 'lightgray';
this.addDoc(newDoc, this.fieldKey + '_0');
- // this.addDoc()
- // }
- // newDoc?.addDocument?.(images);
-
- // if there is text from the pdf ai cards, put the question on the front side.
- // eslint-disable-next-line prefer-destructuring
- // newDoc.text = dataSplit[1];
- // newDoc['backgroundColor'] = 'lightgray';
- // this.addDoc(newDoc, this.fieldKey + '_0');
- // DocCast(this.dataDoc[this.fieldKey + '_0'])[DocData].text = dataSplit[1];
- // DocCast(this.dataDoc[this.fieldKey + '_0']).text = dataSplit[1];
- // console.log('HI' + DocCast(this.dataDoc[this.fieldKey + '_0']).text);
- //console.log('HEREEE' + StrCast(RTFCast(DocCast(this.dataDoc[this.fieldKey + '_0']).text)?.Text));
}
if (!this.dataDoc[this.fieldKey + '_1'] && !this._isEmpty) {
const dataSplit = StrCast(this.dataDoc.data).includes('Keyword: ') ? StrCast(this.dataDoc.data).split('Keyword: ') : StrCast(this.dataDoc.data).split('Answer: ');
const newDoc = Docs.Create.TextDocument(dataSplit[0]);
this.addDoc(newDoc, this.fieldKey + '_1');
- // if there is text from the pdf ai cards, put the answer on the alternate side.
- // eslint-disable-next-line prefer-destructuring
-
- // newDoc[DocData].text = dataSplit[0];
- // console.log('HEREEE' + StrCast(RTFCast(DocCast(this.dataDoc[this.fieldKey + '_1']).text)?.Text));
- // console.log('HI' + DocCast(this.dataDoc[this.fieldKey + '_1']).text);
- // DocCast(this.dataDoc[this.props.fieldKey + '_1'])[DocData].text = dataSplit[0];
- // console.log('HEREEE' + StrCast(RTFCast(DocCast(this.dataDoc[this.fieldKey + '_0']).text)?.Text));
- // DocCast(this.dataDoc[this.fieldKey + '_1'])[DocData].text = dataSplit[0];
+ // if (this.Document.image) {
+ // console.log('ID: ' + DocCast(this.Document.image)[Id]);
+ // const rtfiel = new RichTextField(
+ // JSON.stringify({
+ // doc: {
+ // type: 'doc',
+ // content: [
+ // {
+ // type: 'paragraph',
+ // attrs: { align: null, color: null, id: null, indent: null, inset: null, lineSpacing: null, paddingBottom: null, paddingTop: null },
+ // content: [
+ // { type: 'text', text: dataSplit[0] },
+ // { type: 'dashDoc', attrs: { width: '200px', height: '200px', title: 'dashDoc', float: 'unset', hidden: false, docId: DocCast(this.Document.image)[Id] } },
+ // ],
+ // },
+ // ],
+ // },
+ // selection: { type: 'text', anchor: 2, head: 2 },
+ // })
+ // );
+
+ // newDoc[DocData].text = rtfiel;
+ // }
}
// render the QuizCards
@@ -819,7 +864,10 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
@@ -834,19 +882,25 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
this.openContextMenu(e.clientX, e.clientY)}
- style={{ position: 'absolute', top: '1px', left: '11px', zIndex: '100', width: '5px', height: '5px', cursor: 'pointer' }}>
+ style={{ position: 'absolute', top: '5px', left: '11px', zIndex: '100', width: '5px', height: '5px', cursor: 'pointer' }}>
{ }
+
+ Evaluate Pronunciation
+
{this.layoutDoc[`_${this._props.fieldKey}_usePath`] !== 'alternate' ? (
-
+
Submit
) : (
-
+
Redo the Question
)}
@@ -871,7 +925,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
}}
// onPointerUp={() => (this._isAnyChildContentActive = true)}
>
- {!this.layoutDoc[`_${this._props.fieldKey}_usePath`] && StrCast(RTFCast(DocCast(this.dataDoc[this.fieldKey + '_1']).text)?.Text) === '' && !this.childActive ? Enter text in the flashcard.
: null}
+ {/* {!this.layoutDoc[`_${this._props.fieldKey}_usePath`] && StrCast(RTFCast(DocCast(this.dataDoc[this.fieldKey + '_1']).text)?.Text) === '' && !this.childActive ? Enter text in the flashcard.
: null} */}
{displayBox(`${this.fieldKey}_${side === 0 ? 1 : 0}`, side, this._props.PanelWidth() - 3)}
{this._loading ? (
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 138f00492..b9c3528d3 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -50,7 +50,7 @@ export interface FieldViewSharedProps {
PanelHeight: () => number;
isDocumentActive?: () => boolean | undefined; // whether a document should handle pointer events
isContentActive: () => boolean | undefined; // whether document contents should handle pointer events
- dontSelect: () => boolean | undefined;
+ dontSelect?: () => boolean | undefined;
childFilters: () => string[];
childFiltersByRanges: () => string[];
styleProvider: Opt
;
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 93c07f3a8..ab7605829 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -384,17 +384,93 @@ export class ImageBox extends ViewBoxAnnotatableComponent() {
this._loading = false;
};
+ getImageLabels = async () => {
+ this._loading = true;
+ try {
+ const hrefBase64 = await this.createCanvas();
+ // const hw = await gptImageLabel(hrefBase64, 'Find the image dimensions. Return as height and width.');
+ const response = await gptImageLabel(
+ hrefBase64,
+ //'What is the height and width of the image'
+ 'For each group of words in the image, find the x-coordinate and ycoordinate of the top left corner. Find the width and height of the group. Return this information in this format with the correct information replacing the underscores: "observed word: _, x: _, y: _, width: _, height: _," No additional text, asterisks and put it all in one line. Divide the x and width by the width of the image. Divide the y and the height by the height of the image.'
+ );
+ // console.log(hw);
+ console.log('RESPONSE: ' + response);
+ this.createTextboxes(response);
+
+ //AnchorMenu.Instance.transferToFlashcard(response, NumCast(this.layoutDoc['x']), NumCast(this.layoutDoc['y']));
+ } catch (error) {
+ console.log('Error');
+ }
+ this._loading = false;
+ };
+
+ createTextboxes = (response: string) => {
+ const groups = response.replace('*', '').toLowerCase().split('observed word: ');
+ groups.shift();
+ for (var i = 0; i < groups.length; i++) {
+ console.log('Group ' + i + ': ' + groups[i]);
+ }
+ // console.log('GROUPS: ' + groups);
+ groups.forEach(
+ group => {
+ const groupArr = group.split(', ');
+ const word = groupArr[0];
+ const x = parseFloat(groupArr[1].substring(3));
+ const y = parseFloat(groupArr[2].substring(3));
+ const width = parseFloat(groupArr[3].substring(7));
+ const height = parseFloat(groupArr[4].substring(8));
+ const scaling = 1 / (this._props.NativeDimScaling?.() || 1);
+ const w = AnchorMenu.Instance.marqueeWidth * scaling;
+ const h = AnchorMenu.Instance.marqueeHeight * scaling;
+
+ const newCol = Docs.Create.TextDocument(word, {
+ _width: w * width,
+ //width * NumCast(this.dataDoc[this.fieldKey + '_nativeWidth']),
+ _height: h * height + 20,
+ //height * NumCast(this.dataDoc[this.fieldKey + '_nativeHeight']),
+ _layout_fitWidth: true,
+ _layout_autoHeight: true,
+ });
+ newCol.x = x * w;
+ newCol.y = y * h;
+ // newCol.x = x * NumCast(this.dataDoc[this.fieldKey + '_nativeWidth']);
+ // newCol.y = y * NumCast(this.dataDoc[this.fieldKey + '_nativeHeight']);
+ newCol.zIndex = 1000;
+ newCol.forceActive = true;
+ newCol.quiz = true;
+ this.addDocument(newCol);
+ }
+ // console.log(group);
+ );
+ };
+
@action
setRef = (iref: HTMLImageElement | null) => {
this._imageRef = iref;
};
+ pushInfo = async () => {
+ const formData = new FormData();
+
+ const newArticle = {
+ file: '/files/audio/6b412a6222d631a7fff8a8320.mp3',
+ };
+ const response = await axios.post('http://localhost:105/recognize/', newArticle, {
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ });
+ console.log('RESPONSE: ' + response.data['transcription']);
+ };
+
specificContextMenu = (): void => {
const field = Cast(this.dataDoc[this.fieldKey], ImageField);
if (field) {
const funcs: ContextMenuProps[] = [];
// funcs.push({ description: 'Create ai flashcards', event: () => this.getImageDesc(), icon: 'id-card' });
- funcs.push({ description: 'Get Images', event: () => this.handleSelection('Cats'), icon: 'redo-alt' });
+ // funcs.push({ description: 'Push info', event: this.pushInfo, icon: 'redo-alt' });
+ funcs.push({ description: 'Get Labels', event: this.getImageLabels, icon: 'redo-alt' });
funcs.push({ description: 'Rotate Clockwise 90', event: this.rotate, icon: 'redo-alt' });
funcs.push({ description: `Show ${this.layoutDoc._showFullRes ? 'Dynamic Res' : 'Full Res'}`, event: this.resolution, icon: 'expand' });
funcs.push({ description: 'Set Native Pixel Size', event: this.setNativeSize, icon: 'expand-arrows-alt' });
diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx
new file mode 100644
index 000000000..0a4325d8c
--- /dev/null
+++ b/src/client/views/nodes/LinkAnchorBox.tsx
@@ -0,0 +1,115 @@
+import { action, computed, makeObservable } from 'mobx';
+import { observer } from 'mobx-react';
+import * as React from 'react';
+import { Utils, emptyFunction, setupMoveUpEvents } from '../../../Utils';
+import { Doc } from '../../../fields/Doc';
+import { NumCast, StrCast } from '../../../fields/Types';
+import { TraceMobx } from '../../../fields/util';
+import { DragManager, dropActionType } from '../../util/DragManager';
+import { LinkFollower } from '../../util/LinkFollower';
+import { SelectionManager } from '../../util/SelectionManager';
+import { ViewBoxBaseComponent } from '../DocComponent';
+import { StyleProp } from '../StyleProvider';
+import { FieldView, FieldViewProps } from './FieldView';
+import './LinkAnchorBox.scss';
+import { LinkInfo } from './LinkDocPreview';
+const { default: { MEDIUM_GRAY }, } = require('../global/globalCssVariables.module.scss'); // prettier-ignore
+@observer
+export class LinkAnchorBox extends ViewBoxBaseComponent() {
+ public static LayoutString(fieldKey: string) {
+ return FieldView.LayoutString(LinkAnchorBox, fieldKey);
+ }
+ _doubleTap = false;
+ _lastTap: number = 0;
+ _ref = React.createRef();
+ _isOpen = false;
+ _timeout: NodeJS.Timeout | undefined;
+
+ constructor(props: FieldViewProps) {
+ super(props);
+ makeObservable(this);
+ }
+
+ componentDidMount() {
+ this._props.setContentViewBox?.(this);
+ }
+
+ @computed get linkSource() {
+ return this.DocumentView?.().containerViewPath?.().lastElement().Document; // this._props.styleProvider?.(this.dataDoc, this._props, StyleProp.LinkSource);
+ }
+
+ onPointerDown = (e: React.PointerEvent) => {
+ const linkSource = this.linkSource;
+ linkSource &&
+ setupMoveUpEvents(this, e, this.onPointerMove, emptyFunction, (e, doubleTap) => {
+ if (doubleTap) LinkFollower.FollowLink(this.Document, linkSource, false);
+ else this._props.select(false);
+ });
+ };
+ onPointerMove = action((e: PointerEvent, down: number[], delta: number[]) => {
+ const cdiv = this._ref?.current?.parentElement;
+ if (!this._isOpen && cdiv) {
+ const bounds = cdiv.getBoundingClientRect();
+ const pt = Utils.getNearestPointInPerimeter(bounds.left, bounds.top, bounds.width, bounds.height, e.clientX, e.clientY);
+ const separation = Math.sqrt((pt[0] - e.clientX) * (pt[0] - e.clientX) + (pt[1] - e.clientY) * (pt[1] - e.clientY));
+ if (separation > 100) {
+ const dragData = new DragManager.DocumentDragData([this.Document]);
+ dragData.dropAction = dropActionType.embed;
+ dragData.dropPropertiesToRemove = ['link_anchor_1_x', 'link_anchor_1_y', 'link_anchor_2_x', 'link_anchor_2_y', 'onClick'];
+ DragManager.StartDocumentDrag([this._ref.current!], dragData, pt[0], pt[1]);
+ return true;
+ } else {
+ this.layoutDoc[this.fieldKey + '_x'] = ((pt[0] - bounds.left) / bounds.width) * 100;
+ this.layoutDoc[this.fieldKey + '_y'] = ((pt[1] - bounds.top) / bounds.height) * 100;
+ this.layoutDoc.link_autoMoveAnchors = false;
+ }
+ }
+ return false;
+ });
+
+ specificContextMenu = (e: React.MouseEvent): void => {};
+
+ render() {
+ TraceMobx();
+ const small = this._props.PanelWidth() <= 1; // this happens when rendered in a treeView
+ const x = NumCast(this.layoutDoc[this.fieldKey + '_x'], 100);
+ const y = NumCast(this.layoutDoc[this.fieldKey + '_y'], 100);
+ const background = this._props.styleProvider?.(this.dataDoc, this._props, StyleProp.BackgroundColor + ':anchor');
+ const anchor = this.fieldKey === 'link_anchor_1' ? 'link_anchor_2' : 'link_anchor_1';
+ const anchorScale = !this.dataDoc[this.fieldKey + '_useSmallAnchor'] && (x === 0 || x === 100 || y === 0 || y === 100) ? 1 : 0.25;
+ const targetTitle = StrCast((this.dataDoc[anchor] as Doc)?.title);
+ const selView = SelectionManager.Views.lastElement()?._props.LayoutTemplateString?.includes('link_anchor_1')
+ ? 'link_anchor_1'
+ : SelectionManager.Views.lastElement()?._props.LayoutTemplateString?.includes('link_anchor_2')
+ ? 'link_anchor_2'
+ : '';
+ return (
+
+ LinkInfo.SetLinkInfo({
+ DocumentView: this.DocumentView,
+ styleProvider: this._props.styleProvider,
+ linkSrc: this.linkSource,
+ linkDoc: this.Document,
+ showHeader: true,
+ location: [e.clientX, e.clientY + 20],
+ noPreview: false,
+ })
+ }
+ onPointerDown={this.onPointerDown}
+ onContextMenu={this.specificContextMenu}
+ style={{
+ border: selView && this.dataDoc[selView] === this.dataDoc[this.fieldKey] ? `solid ${MEDIUM_GRAY} 2px` : undefined,
+ background,
+ left: `calc(${x}% - ${small ? 2.5 : 7.5}px)`,
+ top: `calc(${y}% - ${small ? 2.5 : 7.5}px)`,
+ transform: `scale(${anchorScale})`,
+ cursor: 'grab',
+ }}
+ />
+ );
+ }
+}
diff --git a/src/client/views/nodes/ae6d-ba67-4ace-93aa-0f9e0bd96b88.wav b/src/client/views/nodes/ae6d-ba67-4ace-93aa-0f9e0bd96b88.wav
new file mode 100644
index 000000000..dc71e7886
Binary files /dev/null and b/src/client/views/nodes/ae6d-ba67-4ace-93aa-0f9e0bd96b88.wav differ
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 3c12db965..274330d31 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -3,7 +3,7 @@
import { IconProp } from '@fortawesome/fontawesome-svg-core';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Tooltip } from '@mui/material';
-import { action, computed, IReactionDisposer, makeObservable, observable, ObservableSet, reaction, runInAction } from 'mobx';
+import { action, computed, IReactionDisposer, makeObservable, observable, ObservableSet, reaction, runInAction, trace } from 'mobx';
import { observer } from 'mobx-react';
import { baseKeymap, selectAll } from 'prosemirror-commands';
import { history } from 'prosemirror-history';
@@ -361,10 +361,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent
{this.noSidebar || !this.SidebarShown || this.layout_sidebarWidthPercent === '0%' ? null : this.sidebarCollection}
- {this.noSidebar || this.Document._layout_noSidebar || this.Document._createDocOnCR || this.layoutDoc._chromeHidden ? null : this.sidebarHandle}
+ {this.noSidebar || this.Document._layout_noSidebar || this.Document._createDocOnCR || this.layoutDoc._chromeHidden || this.Document.quiz ? null : this.sidebarHandle}
{this.audioHandle}
{this.layoutDoc._layout_enableAltContentUI && !this.layoutDoc._chromeHidden ? this.overlayAlternateIcon : null}
diff --git a/src/fields/RichTextField.ts b/src/fields/RichTextField.ts
index 3f13f7e6d..613bb0fd1 100644
--- a/src/fields/RichTextField.ts
+++ b/src/fields/RichTextField.ts
@@ -13,10 +13,15 @@ export class RichTextField extends ObjectField {
@serializable(true)
readonly Text: string;
- constructor(data: string, text: string = '') {
+ /**
+ * NOTE: if 'text' doesn't match the plain text of 'data', this can cause infinite loop problems or other artifacts when rendered.
+ * @param data this is the formatted text representation of the RTF
+ * @param text this is the plain text of whatever text is in the 'data'
+ */
+ constructor(data: string, text: string) {
super();
this.Data = data;
- this.Text = text;
+ this.Text = text; // ideally, we'd compute 'text' from 'data' by doing what Prosemirror does at run-time ... just need to figure out how to write that function accurately
}
Empty() {
--
cgit v1.2.3-70-g09d2
From fa54fdf2bf9bf8523da393a81ec1ac1cd4fa0f4c Mon Sep 17 00:00:00 2001
From: alyssaf16
Date: Sun, 6 Oct 2024 15:11:20 -0400
Subject: comments
---
src/client/views/nodes/AudioBox.tsx | 3 --
src/client/views/nodes/ComparisonBox.scss | 44 ------------------------
src/client/views/nodes/ComparisonBox.tsx | 13 ++++++-
src/client/views/nodes/ImageBox.tsx | 57 +++++++++++++++++++++++++++++--
src/client/views/nodes/LabelBox.tsx | 8 +----
src/client/views/nodes/PDFBox.tsx | 5 +--
src/client/views/pdf/PDFViewer.tsx | 43 -----------------------
7 files changed, 69 insertions(+), 104 deletions(-)
(limited to 'src/client/views/nodes/ComparisonBox.scss')
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index 8056ced1e..6a918664c 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -297,7 +297,6 @@ export class AudioBox extends ViewBoxAnnotatableComponent() {
},
});
this.Document[DocData].phoneticTranscription = response.data['transcription'];
- console.log('RESPONSE: ' + response.data['transcription']);
};
youtubeUpload = async () => {
@@ -310,13 +309,11 @@ export class AudioBox extends ViewBoxAnnotatableComponent() {
'Content-Type': 'application/json',
},
});
- console.log('RESPONSE: ' + response.data['transcription']);
};
// context menu
specificContextMenu = (): void => {
const funcs: ContextMenuProps[] = [];
- // funcs.push({ description: 'Push info', event: this.pushInfo, icon: 'redo-alt' });
funcs.push({
description: 'Youtube',
diff --git a/src/client/views/nodes/ComparisonBox.scss b/src/client/views/nodes/ComparisonBox.scss
index da1d352f2..b7307f3a3 100644
--- a/src/client/views/nodes/ComparisonBox.scss
+++ b/src/client/views/nodes/ComparisonBox.scss
@@ -30,12 +30,10 @@
padding-top: 5px;
padding-left: 5px;
padding-right: 5px;
- // width: 80%;
border-radius: 2px;
height: 17%;
display: inline-block;
bottom: 0;
- // right: 0;
&.schema-header-button {
color: gray;
@@ -57,28 +55,11 @@
}
&.submit {
width: 40%;
- // float: right;
-
- // position: absolute;
- // position: 10px;
- // padding-left: 35%;
- // padding-right: 80%;
- // // width: 80px;
- // // right: 0;
- // right: 0;
- // bottom: 0;
}
&.record {
width: 20%;
float: left;
border-radius: 2px;
- // right: 0;
- // height: 30%;
- }
-
- button {
- // flex: 1;
- // position: relative;
}
}
@@ -128,7 +109,6 @@
textarea {
flex: 1;
padding: 10px;
- // position: relative;
resize: none;
position: 'absolute';
width: '91%';
@@ -235,7 +215,6 @@
top: 10px;
left: 10px;
z-index: 200;
- // padding: 5px;
background: #dfdfdf;
pointer-events: none;
}
@@ -249,29 +228,8 @@
display: flex;
}
}
-
- // .input-box {
- // position: absolute;
- // 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%;
@@ -288,8 +246,6 @@
align-items: center;
justify-content: center;
.QuizCardBox {
- /* existing code */
-
.DIYNodeBox-iframe {
height: 100%;
width: 100%;
diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx
index 9ac8d7c52..461a65c27 100644
--- a/src/client/views/nodes/ComparisonBox.tsx
+++ b/src/client/views/nodes/ComparisonBox.tsx
@@ -340,6 +340,10 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
ContextMenu.Instance.setLangIndex(ind);
};
+ /**
+ * Determine which language the speech to text tool is in.
+ * @returns
+ */
convertAbr = () => {
switch (this.recognition.lang) {
case 'en-US': return 'English'; //prettier-ignore
@@ -364,6 +368,9 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
ContextMenu.Instance.displayMenu(x, y);
};
+ /**
+ * Creates an AudioBox to record a user's audio.
+ */
evaluatePronunciation = () => {
const newAudio = Docs.Create.AudioDocument(nullAudio, { _width: 200, _height: 100 });
this.Document.audio = newAudio[DocData];
@@ -386,6 +393,11 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
this.Document.phoneticTranscription = response.data['transcription'];
};
+ /**
+ * Extracts the id of the youtube video url.
+ * @param url
+ * @returns
+ */
getYouTubeVideoId = (url: string) => {
const regExp = /^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|\&v=|\?v=)([^#\&\?]*).*/;
const match = url.match(regExp);
@@ -510,7 +522,6 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
} else if (callType === GPTCallType.FLASHCARD) {
this._loading = false;
return res;
- } else if (callType === GPTCallType.STACK) {
}
this._loading = false;
return res;
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 91e51d24e..32d1e471e 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -316,6 +316,14 @@ export class ImageBox extends ViewBoxAnnotatableComponent() {
return cropping;
};
+ /**
+ * Draw image to a canvas so it can be converted to base64 and be passed into
+ * GPT to be analyzed.
+ * @param downX
+ * @param downY
+ * @param cb
+ * @returns
+ */
createCanvas = async (downX?: number, downY?: number, cb?: (filename: string, x: number | undefined, y: number | undefined) => void) => {
const canvas = document.createElement('canvas');
const scaling = 1 / (this._props.NativeDimScaling?.() || 1);
@@ -366,6 +374,12 @@ export class ImageBox extends ViewBoxAnnotatableComponent() {
}
};
+ /**
+ * Calls backend to find any text on an image. Gets the text and the
+ * coordinates of the text and creates label boxes at those locations.
+ * @param quiz
+ * @param i
+ */
pushInfo = async (quiz: quizMode, i?: string) => {
this._quizMode = quiz;
this._loading = true;
@@ -437,6 +451,11 @@ export class ImageBox extends ViewBoxAnnotatableComponent() {
this._loading = false;
};
+ /**
+ * Calls the createCanvas and pushInfo methods to convert the
+ * image to a form that can be passed to GPT and find the locations
+ * of the text.
+ */
makeLabels = async () => {
try {
const hrefBase64 = await this.createCanvas();
@@ -446,6 +465,13 @@ export class ImageBox extends ViewBoxAnnotatableComponent() {
}
};
+ /**
+ * Determines whether two words should be considered
+ * the same, allowing minor typos.
+ * @param str1
+ * @param str2
+ * @returns
+ */
levenshteinDistance = (str1: string, str2: string) => {
const len1 = str1.length;
const len2 = str2.length;
@@ -471,6 +497,12 @@ export class ImageBox extends ViewBoxAnnotatableComponent() {
return dp[len1][len2];
};
+ /**
+ * Different algorithm for determining string similarity.
+ * @param str1
+ * @param str2
+ * @returns
+ */
jaccardSimilarity = (str1: string, str2: string) => {
const set1 = new Set(str1.split(' '));
const set2 = new Set(str2.split(' '));
@@ -481,6 +513,14 @@ export class ImageBox extends ViewBoxAnnotatableComponent() {
return intersection.size / union.size;
};
+ /**
+ * Averages the jaccardSimilarity and levenshteinDistance scores
+ * to determine string similarity for the labelboxes answers and
+ * the users response.
+ * @param str1
+ * @param str2
+ * @returns
+ */
stringSimilarity(str1: string, str2: string) {
const levenshteinDist = this.levenshteinDistance(str1, str2);
const levenshteinScore = 1 - levenshteinDist / Math.max(str1.length, str2.length);
@@ -511,12 +551,23 @@ export class ImageBox extends ViewBoxAnnotatableComponent() {
);
}
+ /**
+ * Returns whether two strings are similar
+ * @param input
+ * @param target
+ * @returns
+ */
compareWords = (input: string, target: string) => {
const distance = this.stringSimilarity(input.toLowerCase(), target.toLowerCase());
- // const threshold = Math.max(input.length, target.length) * 0.2; // Allow up to 20% of the length as difference
return distance >= 0.7;
};
+ /**
+ * GPT returns a hex color for what color the label box should be based on
+ * the correctness of the users answer.
+ * @param inputString
+ * @returns
+ */
extractHexAndSentences = (inputString: string) => {
// Regular expression to match a hexadecimal number at the beginning followed by a period and sentences
const regex = /^#([0-9A-Fa-f]+)\.\s*(.+)$/s;
@@ -569,13 +620,15 @@ export class ImageBox extends ViewBoxAnnotatableComponent() {
});
};
+ /**
+ * Get rid of all the label boxes on the images.
+ */
exitQuizMode = () => {
this._quizMode = quizMode.NONE;
this._quizBoxes.forEach(doc => {
this.removeDocument?.(doc);
});
this._quizBoxes = [];
- console.log('remove');
};
@action
diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx
index bcf55fbe8..79366f76f 100644
--- a/src/client/views/nodes/LabelBox.tsx
+++ b/src/client/views/nodes/LabelBox.tsx
@@ -169,11 +169,7 @@ export class LabelBox extends ViewBoxBaseComponent() {
width: this._props.PanelWidth(),
height: this._props.PanelHeight(),
whiteSpace: 'multiLine' in boxParams && boxParams.multiLine ? 'pre-wrap' : 'pre',
- }}
- // onMouseLeave={() => {
- // this.hoverFlip(undefined);
- // }}
- >
+ }}>
() {
})}
onKeyUp={action(e => {
e.stopPropagation();
- // if (e.key === 'Enter') {
this.dataDoc[this.fieldKey] = this._divRef?.innerText ?? '';
setTimeout(() => this._props.select(false));
- // }
})}
onBlur={() => {
this.dataDoc[this.fieldKey] = this._divRef?.innerText ?? '';
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index cb0b0d71f..e7470c74c 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -120,11 +120,9 @@ export class PDFBox extends ViewBoxAnnotatableComponent
() {
this.replaceCanvases(docViewContent, newDiv);
const htmlString = this._pdfViewer?._mainCont.current && new XMLSerializer().serializeToString(newDiv);
- // const anchx = NumCast(cropping.x);
- // const anchy = NumCast(cropping.y);
const anchw = NumCast(cropping._width) * (this._props.NativeDimScaling?.() || 1);
const anchh = NumCast(cropping._height) * (this._props.NativeDimScaling?.() || 1);
- // const viewScale = 1;
+
cropping.title = 'crop: ' + this.Document.title;
cropping.x = NumCast(this.Document.x) + NumCast(this.layoutDoc._width);
cropping.y = NumCast(this.Document.y);
@@ -473,7 +471,6 @@ export class PDFBox extends ViewBoxAnnotatableComponent() {
!Doc.noviceMode && optionItems.push({ description: 'Toggle Sidebar Type', event: this.toggleSidebarType, icon: 'expand-arrows-alt' });
!Doc.noviceMode && optionItems.push({ description: 'update icon', event: () => this.pdfUrl && this.updateIcon(), icon: 'expand-arrows-alt' });
- // optionItems.push({ description: "Toggle Sidebar ", event: () => this.toggleSidebar(), icon: "expand-arrows-alt" });
!options && ContextMenu.Instance.addItem({ description: 'Options...', subitems: optionItems, icon: 'asterisk' });
const help = cm.findByDescription('Help...');
const helpItems = help?.subitems ?? [];
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 02d310f7d..1d7f269fb 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -30,12 +30,6 @@ import { GPTPopup } from './GPTPopup/GPTPopup';
import './PDFViewer.scss';
import { GPTCallType, gptAPICall } from '../../apis/gpt/GPT';
import ReactLoading from 'react-loading';
-// import html2canvas from 'html2canvas';
-// import SpeechRecognition, { useSpeechRecognition } from 'react-speech-recognition';
-
-// pdfjsLib.GlobalWorkerOptions.workerSrc = `/assets/pdf.worker.js`;
-// The workerSrc property shall be specified.
-// Pdfjs.GlobalWorkerOptions.workerSrc = 'https://unpkg.com/pdfjs-dist@4.4.168/build/pdf.worker.mjs';
interface IViewerProps extends FieldViewProps {
pdfBox: PDFBox;
@@ -64,43 +58,6 @@ export class PDFViewer extends ObservableReactComponent {
super(props);
makeObservable(this);
}
- // @observable transcriptRef = React.createRef();
- // @observable startBtnRef = React.createRef();
- // @observable stopBtnRef = React.createRef();
- // @observable transcriptElement = '';
-
- // handleResult = (e: SpeechRecognitionEvent) => {
- // let interimTranscript = '';
- // let finalTranscript = '';
- // console.log('H');
- // for (let i = e.resultIndex; i < e.results.length; i++) {
- // const transcript = e.results[i][0].transcript;
- // if (e.results[i].isFinal) {
- // finalTranscript += transcript;
- // } else {
- // interimTranscript += transcript;
- // }
- // }
- // console.log(interimTranscript);
- // this.transcriptElement = finalTranscript || interimTranscript;
- // };
-
- // startListening = () => {
- // const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
- // if (SpeechRecognition) {
- // console.log('here');
- // const recognition = new SpeechRecognition();
- // recognition.continuous = true; // Continue listening even if the user pauses
- // recognition.interimResults = true; // Show interim results
- // recognition.lang = 'en-US'; // Set language (optional)
- // recognition.onresult = this.handleResult.bind(this);
- // // recognition.onend = this.handleEnd.bind(this);
-
- // recognition.start();
- // // this.handleResult;
- // // recognition.stop();
- // }
- // };
@observable _pageSizes: { width: number; height: number }[] = [];
@observable _savedAnnotations = new ObservableMap();
--
cgit v1.2.3-70-g09d2
From 972839216c14baa5c9eaf80e1fb2fb2694bbb72c Mon Sep 17 00:00:00 2001
From: bobzel
Date: Tue, 8 Oct 2024 22:51:46 -0400
Subject: modified how buttons are laid out on carousel and comparison views so
that text boxes can reflow around them. extracted flashcard pratice into its
own component and applied it to carousel3D and carousel
---
src/client/views/PropertiesView.tsx | 7 +-
.../collections/CollectionCarousel3DView.scss | 1 +
.../views/collections/CollectionCarousel3DView.tsx | 70 ++++-
.../views/collections/CollectionCarouselView.scss | 86 +-----
.../views/collections/CollectionCarouselView.tsx | 316 ++++++---------------
.../views/collections/FlashcardPracticeUI.scss | 64 +++++
.../views/collections/FlashcardPracticeUI.tsx | 172 +++++++++++
src/client/views/nodes/ComparisonBox.scss | 15 +
src/client/views/nodes/ComparisonBox.tsx | 92 ++++--
src/client/views/nodes/FieldView.tsx | 1 +
.../nodes/formattedText/FormattedTextBox.scss | 6 -
.../views/nodes/formattedText/FormattedTextBox.tsx | 16 +-
12 files changed, 475 insertions(+), 371 deletions(-)
create mode 100644 src/client/views/collections/FlashcardPracticeUI.scss
create mode 100644 src/client/views/collections/FlashcardPracticeUI.tsx
(limited to 'src/client/views/nodes/ComparisonBox.scss')
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 371d34173..442dab671 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -139,6 +139,9 @@ export class PropertiesView extends ObservableReactComponent disposer?.());
}
+ @computed get isText() {
+ return this.selectedDoc?.type === DocumentType.RTF;
+ }
@computed get isInk() {
return this.selectedDoc?.type === DocumentType.INK;
}
@@ -1199,8 +1202,8 @@ export class PropertiesView extends ObservableReactComponent
{!this.isStack ? null : this.getNumber('Gap', ' px', 0, 200, NumCast(this.selectedDoc!.gridGap), this.setVal((doc: Doc, val: number) => { doc.gridGap = val; })) }
- {!this.isStack ? null : this.getNumber('xMargin', ' px', 0, 500, NumCast(this.selectedDoc!.xMargin), this.setVal((doc: Doc, val: number) => { doc.xMargin = val; })) }
- {!this.isStack ? null : this.getNumber('yMargin', ' px', 0, 500, NumCast(this.selectedDoc!.yMargin), this.setVal((doc: Doc, val: number) => { doc.yMargin = val; })) }
+ {!this.isStack && !this.isText? null : this.getNumber('xMargin', ' px', 0, 500, NumCast(this.selectedDoc!.xMargin), this.setVal((doc: Doc, val: number) => { doc.xMargin = val; })) }
+ {!this.isStack && !this.isText? null : this.getNumber('yMargin', ' px', 0, 500, NumCast(this.selectedDoc!.yMargin), this.setVal((doc: Doc, val: number) => { doc.yMargin = val; })) }
{!this.isGroup ? null : this.getNumber('Padding', ' px', 0, 500, NumCast(this.selectedDoc!.xPadding), this.setVal((doc: Doc, val: number) => { doc.xPadding = doc.yPadding = val; })) }
{this.isInk ? this.controlPointsButton : null}
{this.getNumber('Width', ' px', 0, Math.max(1000, this.shapeWid), this.shapeWid, this.setVal((doc: Doc, val:number) => {this.shapeWid = val}), 1000, 1)}
diff --git a/src/client/views/collections/CollectionCarousel3DView.scss b/src/client/views/collections/CollectionCarousel3DView.scss
index a556d0fa7..42e112906 100644
--- a/src/client/views/collections/CollectionCarousel3DView.scss
+++ b/src/client/views/collections/CollectionCarousel3DView.scss
@@ -4,6 +4,7 @@
position: relative;
background-color: white;
overflow: hidden;
+ display: flex;
}
.carousel-wrapper {
diff --git a/src/client/views/collections/CollectionCarousel3DView.tsx b/src/client/views/collections/CollectionCarousel3DView.tsx
index c5da8e037..1583f0e0c 100644
--- a/src/client/views/collections/CollectionCarousel3DView.tsx
+++ b/src/client/views/collections/CollectionCarousel3DView.tsx
@@ -1,5 +1,5 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { computed, makeObservable } from 'mobx';
+import { action, computed, makeObservable, observable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { returnZero } from '../../../ClientUtils';
@@ -9,12 +9,13 @@ import { Id } from '../../../fields/FieldSymbols';
import { DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
import { DocumentType } from '../../documents/DocumentTypes';
import { DragManager } from '../../util/DragManager';
+import { Transform } from '../../util/Transform';
import { StyleProp } from '../StyleProp';
import { DocumentView } from '../nodes/DocumentView';
import { FocusViewOptions } from '../nodes/FocusViewOptions';
import './CollectionCarousel3DView.scss';
import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
-import { Transform } from '../../util/Transform';
+import { FlashcardPracticeUI } from './FlashcardPracticeUI';
// eslint-disable-next-line @typescript-eslint/no-require-imports
const { CAROUSEL3D_CENTER_SCALE, CAROUSEL3D_SIDE_SCALE, CAROUSEL3D_TOP } = require('../global/globalCssVariables.module.scss');
@@ -24,6 +25,9 @@ export class CollectionCarousel3DView extends CollectionSubView() {
@computed get scrollSpeed() {
return this.layoutDoc._autoScrollSpeed ? NumCast(this.layoutDoc._autoScrollSpeed) : 1000; // default scroll speed
}
+ _sideBtnWidth = 35;
+ @observable _filterFunc: ((doc: Doc) => boolean) | undefined = undefined;
+
constructor(props: SubCollectionViewProps) {
super(props);
makeObservable(this);
@@ -43,7 +47,7 @@ export class CollectionCarousel3DView extends CollectionSubView() {
};
@computed get carouselItems() {
- return this.childLayoutPairs.filter(pair => pair.layout.type !== DocumentType.LINK);
+ return this.childLayoutPairs.filter(pair => pair.layout.type !== DocumentType.LINK).filter(pair => !this._filterFunc?.(pair.layout));
}
centerScale = Number(CAROUSEL3D_CENTER_SCALE);
@@ -53,22 +57,17 @@ export class CollectionCarousel3DView extends CollectionSubView() {
onChildDoubleClick = () => ScriptCast(this.layoutDoc.onChildDoubleClick);
isContentActive = () => this._props.isSelected() || this._props.isContentActive() || this._props.isAnyChildContentActive();
isChildContentActive = () => !!this.isContentActive();
+ contentScreenToLocalXf = () => this._props.ScreenToLocalTransform().scale(this._props.NativeDimScaling?.() || 1);
childScreenLeftToLocal = () =>
- this._props
- .ScreenToLocalTransform()
- .scale(this._props.NativeDimScaling?.() || 1)
+ this.contentScreenToLocalXf()
.translate(-(this.panelWidth() - this.panelWidth() * this.sideScale) / 2, -(this.panelHeight() - this.panelHeight() * this.sideScale) / 2 - (Number(CAROUSEL3D_TOP) / 100) * this._props.PanelHeight())
.scale(1 / this.sideScale);
childScreenRightToLocal = () =>
- this._props
- .ScreenToLocalTransform()
- .scale(this._props.NativeDimScaling?.() || 1)
+ this.contentScreenToLocalXf()
.translate(-2 * this.panelWidth() - (this.panelWidth() - this.panelWidth() * this.sideScale) / 2, -(this.panelHeight() - this.panelHeight() * this.sideScale) / 2 - (Number(CAROUSEL3D_TOP) / 100) * this._props.PanelHeight())
.scale(1 / this.sideScale);
childCenterScreenToLocal = () =>
- this._props
- .ScreenToLocalTransform()
- .scale(this._props.NativeDimScaling?.() || 1)
+ this.contentScreenToLocalXf()
.translate(
-this.panelWidth() + ((this.centerScale - 1) * this.panelWidth()) / 2, // Focused Doc is shifted right by 1/3 panel width then left by increased size percent of center * 1/2 * panel width / 3
-((Number(CAROUSEL3D_TOP) / 100) * this._props.PanelHeight()) + ((this.centerScale - 1) * this.panelHeight()) / 2
@@ -119,7 +118,7 @@ export class CollectionCarousel3DView extends CollectionSubView() {
changeSlide = (direction: number) => {
DocumentView.DeselectAll();
- this.layoutDoc._carousel_index = (NumCast(this.layoutDoc._carousel_index) + direction + this.carouselItems.length) % this.carouselItems.length;
+ this.layoutDoc._carousel_index = !this.curDoc() ? 0 : (NumCast(this.layoutDoc._carousel_index) + direction + this.carouselItems.length) % (this.carouselItems.length || 1);
};
onArrowClick = (direction: number) => {
@@ -192,6 +191,35 @@ export class CollectionCarousel3DView extends CollectionSubView() {
return this.panelWidth() * (1 - index);
}
+ /**
+ * How much the content of the carousel view is being scaled based on its nesting and its fit-to-width settings
+ */
+ @computed get contentScaling() { return this.ScreenToLocalBoxXf().Scale * (this._props.NativeDimScaling?.() ?? 1); } // prettier-ignore
+
+ /**
+ * The maximum size a UI widget can be scaled so that it won't be bigger in screen pixels than its normal 35 pixel size.
+ */
+ @computed get maxWidgetScale() {
+ const maxWidgetSize = Math.min(this._sideBtnWidth * this.contentScaling, 0.1 * NumCast(this.layoutDoc.width, 1));
+ return Math.max(maxWidgetSize / this._sideBtnWidth, 1);
+ }
+ /**
+ * How much to reactively scale a UI element so that it is as big as it can be (up to its normal 35pixel size) without being too big for the Doc content
+ */
+ @computed get uiBtnScaleTransform() { return this.maxWidgetScale * Math.min(1, this.contentScaling); } // prettier-ignore
+ screenXPadding = () => (this.uiBtnScaleTransform * this._sideBtnWidth - NumCast(this.layoutDoc.xMargin)) / this._props.ScreenToLocalTransform().Scale;
+
+ docViewProps = () => ({
+ ...this._props, //
+ isDocumentActive: this._props.childDocumentsActive?.() ? this._props.isDocumentActive : this._props.isContentActive,
+ isContentActive: this.isChildContentActive,
+ ScreenToLocalTransform: this.contentScreenToLocalXf,
+ });
+ carouselItemsFunc = () => this.carouselItems.map(pair => pair.layout);
+ @action setFilterFunc = (func?: (doc: Doc) => boolean) => { this._filterFunc = func; }; // prettier-ignore
+ answered = (correct: boolean) => (!correct || !this.curDoc()) && this.changeSlide(1);
+ curDoc = () => this.carouselItems[NumCast(this.layoutDoc._carousel_index)]?.layout;
+
render() {
return (
{this.buttons}
{this.dots}
+
);
}
diff --git a/src/client/views/collections/CollectionCarouselView.scss b/src/client/views/collections/CollectionCarouselView.scss
index 97952822e..757072453 100644
--- a/src/client/views/collections/CollectionCarouselView.scss
+++ b/src/client/views/collections/CollectionCarouselView.scss
@@ -12,23 +12,10 @@
display: inline-block;
width: 100%;
user-select: none;
+ position: absolute;
+ top: 0;
+ left: 0;
}
- .message {
- justify-content: center;
- align-items: center;
- display: flex;
- height: 60%;
- z-index: -1;
- // margin: 15px;
- }
-}
-
-.collectionCarouselView-addFlashcards {
- justify-content: center;
- align-items: center;
- height: 100%;
- z-index: -1;
- pointer-events: none;
}
.collectionCarouselView-recentlyMissed {
color: red;
@@ -39,10 +26,7 @@
pointer-events: none;
}
.carouselView-back,
-.carouselView-fwd,
-.carouselView-remove,
-.carouselView-check,
-.carouselView-add {
+.carouselView-fwd {
position: absolute;
display: flex;
width: 30;
@@ -66,68 +50,6 @@
left: 0;
transform-origin: top left;
}
-.carouselView-add {
- position: absolute;
- bottom: 0;
- left: 0;
-}
-.carouselView-remove {
- left: 52%;
-}
-.carouselView-check {
- right: 52%;
-}
-.carouselView-menu {
- position: absolute;
- flex-direction: column;
- align-items: center;
- display: flex;
- top: 0px;
- left: 0px;
- width: 30;
- transform-origin: top left;
- border-radius: 5px;
- color: rgba(255, 255, 255, 0.5);
- background: rgba(0, 0, 0, 0.1);
- .carouselView-practiceModes {
- width: 100%;
- display: flex;
- flex-direction: column;
- top: 0;
- position: relative;
- .carouselView-quiz {
- position: relative;
- display: flex;
- height: 20px;
- align-items: center;
- margin: auto;
- &:hover {
- color: white;
- }
- & > svg {
- height: 100%;
- width: 100%;
- }
- }
-
- .carouselView-practice {
- position: relative;
- display: flex;
- flex-direction: column;
- height: 20px;
- align-items: center;
- margin: auto;
- &:hover {
- color: white;
- }
- & > svg {
- height: 100%;
- width: 100%;
- }
- }
- }
-}
-
.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 559dcfe2a..64ddaac79 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -1,52 +1,35 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { Tooltip } from '@mui/material';
import { action, computed, makeObservable, observable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { StopEvent, returnOne, returnZero } from '../../../ClientUtils';
-import { Doc, DocListCast, Opt } from '../../../fields/Doc';
+import { Doc, Opt } from '../../../fields/Doc';
import { BoolCast, DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
import { DocumentType } from '../../documents/DocumentTypes';
import { DragManager } from '../../util/DragManager';
import { StyleProp } from '../StyleProp';
-import { TagItem } from '../TagsView';
import { DocumentView } from '../nodes/DocumentView';
import { FieldViewProps } from '../nodes/FieldView';
import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
import './CollectionCarouselView.scss';
import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
+import { FlashcardPracticeUI } from './FlashcardPracticeUI';
-enum cardMode {
- STAR = 'star',
- ALL = 'all',
-}
-enum practiceMode {
- PRACTICE = 'practice',
- QUIZ = 'quiz',
-}
-enum practiceVal {
- MISSED = 'missed',
- CORRECT = 'correct',
-}
@observer
export class CollectionCarouselView extends CollectionSubView() {
private _dropDisposer?: DragManager.DragDropDisposer;
- get practiceField() { return this.fieldKey + "_practice"; } // prettier-ignore
- get sideField() { return "_" + this.fieldKey + "_usePath"; } // prettier-ignore
- get starField() { return "#star"; } // prettier-ignore
-
- _sideBtnWidth = 35;
_fadeTimer: NodeJS.Timeout | undefined;
+ _sideBtnWidth = 35;
+ @observable _filterFunc: ((doc: Doc) => boolean) | undefined = undefined;
+ @observable _last_index = this.carouselIndex;
+ @observable _last_opacity = 1;
constructor(props: SubCollectionViewProps) {
super(props);
makeObservable(this);
}
- @observable _last_index = this.carouselIndex;
- @observable _last_opacity = 1;
-
componentWillUnmount() {
this._dropDisposer?.();
}
@@ -58,38 +41,31 @@ export class CollectionCarouselView extends CollectionSubView() {
}
};
- @computed get practiceMode() {
- return this.childDocs.some(doc => doc._layout_isFlashcard) ? StrCast(this.layoutDoc.practiceMode) : '';
- }
- @computed get practiceMessage() {
- const cardCount = this.carouselItems.length;
- if (this.practiceMode) {
- if (!Doc.hasDocFilter(this.layoutDoc, 'tags', Doc.FilterAny) && !cardCount) {
- return 'Finished! Click here to view all flashcards.';
- }
- }
- return '';
- }
-
- @computed get filterMessage() {
- const cardCount = this.carouselItems.length;
- if (!this.practiceMessage) {
- if (Doc.hasDocFilter(this.layoutDoc, 'tags', Doc.FilterAny) && !cardCount) {
- return 'No tagged items. Click here to view all flash cards.';
- }
- if (this.practiceMode) {
- if (!cardCount) return 'No flashcards to show! Click here to leave practice mode';
- }
- }
- return '';
- }
- @computed get marginX() { return NumCast(this.layoutDoc.caption_xMargin, 50); } // prettier-ignore
+ @computed get captionMarginX(){ return NumCast(this.layoutDoc.caption_xMargin, 50); } // prettier-ignore
@computed get carouselIndex() { return NumCast(this.layoutDoc._carousel_index) % this.carouselItems.length; } // prettier-ignore
@computed get carouselItems() { return this.childDocs
.filter(doc => doc.type !== DocumentType.LINK)
- .filter(doc => !this.practiceMode || (BoolCast(doc?._layout_isFlashcard) && doc[this.practiceField] !== practiceVal.CORRECT))// show only cards that aren't marked as correct
+ .filter(doc => !this._filterFunc?.(doc))
} // prettier-ignore
+ /**
+ * How much the content of the carousel view is being scaled based on its nesting and its fit-to-width settings
+ */
+ @computed get contentScaling() { return this.ScreenToLocalBoxXf().Scale * (this._props.NativeDimScaling?.() ?? 1); } // prettier-ignore
+
+ /**
+ * The maximum size a UI widget can be scaled so that it won't be bigger in screen pixels than its normal 35 pixel size.
+ */
+ @computed get maxWidgetScale() {
+ const maxWidgetSize = Math.min(this._sideBtnWidth * this.contentScaling, 0.1 * NumCast(this.layoutDoc.width, 1));
+ return Math.max(maxWidgetSize / this._sideBtnWidth, 1);
+ }
+ /**
+ * How much to reactively scale a UI element so that it is as big as it can be (up to its normal 35pixel size) without being too big for the Doc content
+ */
+ @computed get uiBtnScaleTransform() { return this.maxWidgetScale * Math.min(1, this.contentScaling); } // prettier-ignore
+ screenXPadding = () => (this.uiBtnScaleTransform * this._sideBtnWidth - NumCast(this.layoutDoc.xMargin)) / this._props.ScreenToLocalTransform().Scale;
+
/**
* Move forward or backward the specified number of Docs
* @param dir signed number indicating Docs to move forward or backward
@@ -102,8 +78,8 @@ export class CollectionCarouselView extends CollectionSubView() {
/**
* Goes to the next Doc in the stack subject to the currently selected filter option.
*/
- advance = (e: React.MouseEvent) => {
- e.stopPropagation();
+ advance = (e?: React.MouseEvent) => {
+ e?.stopPropagation();
this.move(1);
};
@@ -115,55 +91,23 @@ export class CollectionCarouselView extends CollectionSubView() {
this.move(-1);
};
- /*
- * Toggles whether the 'star' metadata field is set on the current Doc
- */
- toggleStar = (e: React.MouseEvent) => {
- e.stopPropagation();
- const curDoc = this.carouselItems[this.carouselIndex];
- if (curDoc) {
- if (TagItem.docHasTag(curDoc, this.starField)) TagItem.removeTagFromDoc(curDoc, this.starField);
- else TagItem.addTagToDoc(curDoc, this.starField);
- }
- };
-
- /*
- * Sets a flashcard to either missed or correct depending on if they got the question right in practice mode.
- */
- setPracticeVal = (e: React.MouseEvent, val: string) => {
- e.stopPropagation();
- const curDoc = this.carouselItems[this.carouselIndex];
- curDoc && (curDoc[this.practiceField] = val);
- this.advance(e);
- };
-
- /**
- * Sets the practice mode answer style for flashcards
- * @param mode practiceMode or undefined for no practice
- */
- setPracticeMode = (mode: practiceMode | undefined) => {
- this.layoutDoc.practiceMode = mode;
- this.carouselItems?.map(doc => (doc[this.practiceField] = undefined));
- if (mode === practiceMode.QUIZ) this.carouselItems?.map(doc => (doc[this.sideField] = undefined));
- };
+ curDoc = () => this.carouselItems[this.carouselIndex];
captionStyleProvider = (doc: Doc | undefined, captionProps: Opt, property: string) => {
// 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;
return childValue ?? this._props.styleProvider?.(this.layoutDoc, captionProps, property);
};
+ contentPanelWidth = () => this._props.PanelWidth() - 2 * NumCast(this.layoutDoc.xMargin);
contentPanelHeight = () => this._props.PanelHeight() - (StrCast(this.layoutDoc._layout_showCaption) ? 50 : 0) - 2 * NumCast(this.layoutDoc.yMargin);
onContentDoubleClick = () => ScriptCast(this.layoutDoc.onChildDoubleClick);
onContentClick = () => ScriptCast(this.layoutDoc.onChildClick);
- captionWidth = () => this._props.PanelWidth() - 2 * this.marginX;
+ captionWidth = () => this._props.PanelWidth() - 2 * this.captionMarginX;
contentScreenToLocalXf = () =>
this._props
.ScreenToLocalTransform()
.translate(-NumCast(this.layoutDoc.xMargin), -NumCast(this.layoutDoc.yMargin))
.scale(this._props.NativeDimScaling?.() || 1);
-
- contentPanelWidth = () => this._props.PanelWidth() - 2 * NumCast(this.layoutDoc.xMargin);
-
isChildContentActive = () =>
this._props.isContentActive?.() === false
? false
@@ -172,10 +116,7 @@ export class CollectionCarouselView extends CollectionSubView() {
: this._props.childDocumentsActive?.() === false || this.Document.childDocumentsActive === false
? false
: undefined;
-
renderDoc = (doc: Doc, showCaptions: boolean, overlayFunc?: (r: DocumentView | null) => void) => {
- const screenScale = this.ScreenToLocalBoxXf().Scale;
- const fitWidthScale = (NumCast(this.Document.width, 1) / NumCast(this.carouselItems[this.carouselIndex]?._width)) * (this._props.NativeDimScaling?.() || 1);
return (
);
};
@@ -214,7 +155,7 @@ export class CollectionCarouselView extends CollectionSubView() {
const fadeTime = 500;
const lastDoc = this.carouselItems?.[this._last_index];
return !lastDoc || this.carouselIndex === this._last_index ? null : (
-
+
{this.renderDoc(
lastDoc,
false, // hide captions if the carousel is configured to show the captions
@@ -235,15 +176,18 @@ export class CollectionCarouselView extends CollectionSubView() {
);
}
+ @computed get renderedDoc() {
+ const carouselShowsCaptions = StrCast(this.layoutDoc._layout_showCaption);
+ return this.renderDoc(this.curDoc(), !!carouselShowsCaptions);
+ }
+
@computed get content() {
- const index = this.carouselIndex;
- const curDoc = this.carouselItems?.[index];
const captionProps = { ...this._props, NativeScaling: returnOne, PanelWidth: this.captionWidth, fieldKey: 'caption', setHeight: undefined, setContentView: undefined };
const carouselShowsCaptions = StrCast(this.layoutDoc._layout_showCaption);
- return !curDoc ? null : (
+ return !this.curDoc() ? null : (
<>
- {this.renderDoc(curDoc, !!carouselShowsCaptions)}
+ {this.renderedDoc}
{this.overlay}
{!carouselShowsCaptions ? null : (
@@ -253,158 +197,70 @@ export class CollectionCarouselView extends CollectionSubView() {
onWheel={StopEvent}
style={{
borderRadius: this._props.styleProvider?.(this.layoutDoc, captionProps, StyleProp.BorderRounding) as string,
- marginRight: this.marginX,
- marginLeft: this.marginX,
- width: `calc(100% - ${this.marginX * 2}px)`,
+ marginRight: this.captionMarginX,
+ marginLeft: this.captionMarginX,
+ width: `calc(100% - ${this.captionMarginX * 2}px)`,
}}>
-
+
)}
>
);
}
- togglePracticeMode = (mode: practiceMode) => this.setPracticeMode(mode === this.practiceMode ? undefined : mode);
- toggleFilterMode = () => Doc.setDocFilter(this.Document, 'tags', this.starField, 'check', true);
- setColor = (mode: practiceMode | cardMode, which: string) => (which === mode ? 'white' : 'light gray');
-
- @computed get filterDoc() {
- return DocListCast(Doc.MyContextMenuBtns.data).find(doc => doc.title === 'Filter');
- }
- filterHeight = () => NumCast(this.filterDoc?.height) * Math.min(1, this.ScreenToLocalBoxXf().Scale);
- filterWidth = () => (!this.filterDoc ? 1 : (this.filterHeight() * NumCast(this.filterDoc._width)) / NumCast(this.filterDoc._height));
-
- /**
- * How much the content of the carousel view is being scaled based on its nesting and its fit-to-width settings
- */
- @computed get contentScaling() {
- return this.ScreenToLocalBoxXf().Scale * (this._props.NativeDimScaling?.() ?? 1);
- }
-
- /**
- * The maximum size a UI widget can be scaled so that it won't be bigger in screen pixels than its normal 35 pixel size.
- */
- @computed get maxWidgetScale() {
- const maxWidgetSize = Math.min(this._sideBtnWidth * this.contentScaling, 0.1 * NumCast(this.Document.width, 1));
- return Math.max(maxWidgetSize / this._sideBtnWidth, 1);
- }
- /**
- * How much to reactively scale a UI element so that it is as big as it can be (up to its normal 35pixel size) without being too big for the Doc content
- */
- @computed get uiBtnScaleTransform() {
- return `scale(${this.maxWidgetScale * Math.min(1, this.contentScaling)})`;
- }
- @computed get menu() {
- const curDoc = this.carouselItems?.[this.carouselIndex];
- return (
-
- {!this.filterDoc ? null : (
-
- )}
-
-
- this.togglePracticeMode(practiceMode.QUIZ)}>
-
-
-
-
- this.togglePracticeMode(practiceMode.PRACTICE)}>
-
-
-
-
-
- );
- }
- @computed get buttons() {
- return (
+ @computed get navButtons() {
+ return this.Document._chromeHidden || !this.curDoc() ? null : (
<>
-
+
-
+
- {this.practiceMode == practiceMode.PRACTICE ? (
-
-
- this.setPracticeVal(e, practiceVal.MISSED)}>
-
-
-
-
- this.setPracticeVal(e, practiceVal.CORRECT)}>
-
-
-
-
- ) : null}
>
);
}
+ docViewProps = () => ({
+ ...this._props, //
+ isDocumentActive: this._props.childDocumentsActive?.() ? this._props.isDocumentActive : this._props.isContentActive,
+ isContentActive: this.isChildContentActive,
+ ScreenToLocalTransform: this.contentScreenToLocalXf,
+ });
+ carouselItemsFunc = () => this.carouselItems;
+ answered = () => this.advance();
+ @action setFilterFunc = (func?: (doc: Doc) => boolean) => { this._filterFunc = func; }; // prettier-ignore
+
render() {
return (
-
-
- {!this.practiceMessage && !this.filterMessage ? (
- this.content
- ) : (
-
{
- if (this.filterMessage || this.practiceMessage) {
- this.setPracticeMode(undefined);
- Doc.setDocFilter(this.layoutDoc, 'tags', Doc.FilterAny, 'remove');
- }
- }}>
- {this.filterMessage || this.practiceMessage}
-
- )}
-
- {!this.Document._chromeHidden ? this.menu : null}
- {!this.Document._chromeHidden && this.carouselItems?.[this.carouselIndex] ? this.buttons : null}
+
+ {this.content}
+
+ {this.navButtons}
);
}
diff --git a/src/client/views/collections/FlashcardPracticeUI.scss b/src/client/views/collections/FlashcardPracticeUI.scss
new file mode 100644
index 000000000..53c26ad34
--- /dev/null
+++ b/src/client/views/collections/FlashcardPracticeUI.scss
@@ -0,0 +1,64 @@
+.FlashcardPracticeUI-remove,
+.FlashcardPracticeUI-check {
+ position: absolute;
+ display: flex;
+ width: 30;
+ height: 30;
+ align-items: center;
+ border-radius: 5px;
+ justify-content: center;
+ color: rgba(255, 255, 255, 0.5);
+ background: rgba(0, 0, 0, 0.1);
+ &:hover {
+ color: white;
+ }
+}
+.FlashcardPracticeUI-remove {
+ left: 52%;
+}
+.FlashcardPracticeUI-check {
+ right: 52%;
+}
+.FlashcardPracticeUI-menu {
+ position: absolute;
+ flex-direction: column;
+ align-items: center;
+ display: flex;
+ top: 0px;
+ left: 0px;
+ width: 30;
+ transform-origin: top left;
+ border-radius: 5px;
+ color: rgba(255, 255, 255, 0.5);
+ background: rgba(0, 0, 0, 0.1);
+ .FlashcardPracticeUI-practiceModes {
+ width: 100%;
+ display: flex;
+ flex-direction: column;
+ top: 0;
+ position: relative;
+ .FlashcardPracticeUI-quiz,
+ .FlashcardPracticeUI-practice {
+ position: relative;
+ display: flex;
+ height: 20px;
+ align-items: center;
+ margin: auto;
+ padding: 3px;
+ &:hover {
+ color: white;
+ }
+ & > svg {
+ height: 100%;
+ width: 100%;
+ }
+ }
+ }
+}
+.FlashcardPracticeUI-message {
+ z-index: 100;
+ position: relative;
+ margin: auto;
+ align-content: center;
+ width: max-content;
+}
diff --git a/src/client/views/collections/FlashcardPracticeUI.tsx b/src/client/views/collections/FlashcardPracticeUI.tsx
new file mode 100644
index 000000000..032a405bf
--- /dev/null
+++ b/src/client/views/collections/FlashcardPracticeUI.tsx
@@ -0,0 +1,172 @@
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { Tooltip } from '@mui/material';
+import { computed, makeObservable } from 'mobx';
+import { observer } from 'mobx-react';
+import * as React from 'react';
+import { returnZero } from '../../../ClientUtils';
+import { Doc, DocListCast } from '../../../fields/Doc';
+import { BoolCast, NumCast, StrCast } from '../../../fields/Types';
+import { Transform } from '../../util/Transform';
+import { ObservableReactComponent } from '../ObservableReactComponent';
+import { DocumentView, DocumentViewProps } from '../nodes/DocumentView';
+import './FlashcardPracticeUI.scss';
+
+enum practiceMode {
+ PRACTICE = 'practice',
+ QUIZ = 'quiz',
+}
+enum practiceVal {
+ MISSED = 'missed',
+ CORRECT = 'correct',
+}
+
+interface PracticeUIProps {
+ fieldKey: string;
+ layoutDoc: Doc;
+ carouselItems: () => Doc[];
+ childDocs: Doc[];
+ curDoc: () => Doc;
+ advance: (correct: boolean) => void;
+ renderDepth: number;
+ sideBtnWidth: number;
+ uiBtnScaleTransform: number;
+ ScreenToLocalBoxXf: () => Transform;
+ maxWidgetScale: number;
+ docViewProps: () => DocumentViewProps;
+ setFilterFunc: (func?: (doc: Doc) => boolean) => void;
+ practiceBtnOffset?: number;
+}
+@observer
+export class FlashcardPracticeUI extends ObservableReactComponent
{
+ constructor(props: PracticeUIProps) {
+ super(props);
+ makeObservable(this);
+ this._props.setFilterFunc(this.tryFilterOut);
+ }
+
+ componentWillUnmount(): void {
+ this._props.setFilterFunc(undefined);
+ }
+
+ get practiceField() { return this._props.fieldKey + "_practice"; } // prettier-ignore
+
+ @computed get filterDoc() { return DocListCast(Doc.MyContextMenuBtns.data).find(doc => doc.title === 'Filter'); } // prettier-ignore
+ @computed get practiceMode() { return this._props.childDocs.some(doc => doc._layout_isFlashcard) ? StrCast(this._props.layoutDoc.practiceMode) : ''; } // prettier-ignore
+
+ btnHeight = () => NumCast(this.filterDoc?.height) * Math.min(1, this._props.ScreenToLocalBoxXf().Scale);
+ btnWidth = () => (!this.filterDoc ? 1 : (this.btnHeight() * NumCast(this.filterDoc._width)) / NumCast(this.filterDoc._height));
+
+ /**
+ * Sets the practice mode answer style for flashcards
+ * @param mode practiceMode or undefined for no practice
+ */
+ setPracticeMode = (mode: practiceMode | undefined) => {
+ this._props.layoutDoc.practiceMode = mode;
+ this._props.carouselItems().map(doc => (doc[this.practiceField] = undefined));
+ };
+
+ @computed get emptyMessage() {
+ const cardCount = this._props.carouselItems().length;
+ const practiceMessage = this.practiceMode && !Doc.hasDocFilter(this._props.layoutDoc, 'tags', Doc.FilterAny) && !this._props.carouselItems().length ? 'Finished! Click here to view all flashcards.' : '';
+ const filterMessage = practiceMessage
+ ? ''
+ : Doc.hasDocFilter(this._props.layoutDoc, 'tags', Doc.FilterAny) && !cardCount
+ ? 'No tagged items. Click here to view all flash cards.'
+ : this.practiceMode && !cardCount
+ ? 'No flashcards to show! Click here to leave practice mode'
+ : '';
+ return !practiceMessage && !filterMessage ? null : (
+ {
+ if (filterMessage || practiceMessage) {
+ this.setPracticeMode(undefined);
+ Doc.setDocFilter(this._props.layoutDoc, 'tags', Doc.FilterAny, 'remove');
+ }
+ }}>
+ {filterMessage || practiceMessage}
+
+ );
+ }
+
+ @computed get practiceButtons() {
+ /*
+ * Sets a flashcard to either missed or correct depending on if they got the question right in practice mode.
+ */
+ const setPracticeVal = (e: React.MouseEvent, val: string) => {
+ e.stopPropagation();
+ const curDoc = this._props.curDoc();
+ curDoc && (curDoc[this.practiceField] = val);
+ this._props.advance?.(val === practiceVal.CORRECT);
+ };
+
+ return this.practiceMode == practiceMode.PRACTICE ? (
+
+
+ setPracticeVal(e, practiceVal.MISSED)}>
+
+
+
+
+ setPracticeVal(e, practiceVal.CORRECT)}>
+
+
+
+
+ ) : null;
+ }
+ @computed get practiceModesMenu() {
+ const setColor = (mode: practiceMode) => (StrCast(this.practiceMode) === mode ? 'white' : 'light gray');
+ const togglePracticeMode = (mode: practiceMode) => this.setPracticeMode(mode === this.practiceMode ? undefined : mode);
+
+ return !this._props.curDoc()?._layout_isFlashcard ? null : (
+
+
+ togglePracticeMode(practiceMode.QUIZ)}>
+
+
+
+
+ togglePracticeMode(practiceMode.PRACTICE)}>
+
+
+
+
+ );
+ }
+ tryFilterOut = (doc: Doc) => (this.practiceMode && BoolCast(doc?._layout_isFlashcard) && doc[this.practiceField] === practiceVal.CORRECT ? true : false); // show only cards that aren't marked as correct
+ render() {
+ return (
+ <>
+ {this.emptyMessage}
+ {this.practiceButtons}
+
+ {!this.filterDoc || this._props.layoutDoc._chromeHidden ? null : (
+
+ )}
+ {this.practiceModesMenu}
+
+ >
+ );
+ }
+}
diff --git a/src/client/views/nodes/ComparisonBox.scss b/src/client/views/nodes/ComparisonBox.scss
index b7307f3a3..8156c50f6 100644
--- a/src/client/views/nodes/ComparisonBox.scss
+++ b/src/client/views/nodes/ComparisonBox.scss
@@ -296,3 +296,18 @@
}
}
}
+.comparisonBox-bottomMenu {
+ transform-origin: bottom right;
+ width: max-content;
+ justify-content: space-between;
+ height: max-content;
+ position: absolute;
+ bottom: 0;
+ right: 2;
+ flex-direction: row-reverse;
+ display: flex;
+ cursor: pointer;
+ .comparisonBox-button {
+ padding-right: 8px;
+ }
+}
diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx
index ef66c2b11..81e223028 100644
--- a/src/client/views/nodes/ComparisonBox.tsx
+++ b/src/client/views/nodes/ComparisonBox.tsx
@@ -68,7 +68,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
return (
flip }>
setupMoveUpEvents(e.target, e, returnFalse, emptyFunction, () => {
if (!this.revealOp || this.revealOp === 'flip') {
@@ -81,42 +81,74 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent
()
color: this.revealOp === 'hover' ? 'black' : this._frontSide ? 'black' : 'white',
display: 'inline-block',
}}>
-
-
-
+
);
}
+ _sideBtnWidth = 30;
+ /**
+ * How much the content of the view is being scaled based on its nesting and its fit-to-width settings
+ */
+ @computed get contentScaling() {
+ return this.ScreenToLocalBoxXf().Scale * (this._props.NativeDimScaling?.() ?? 1);
+ }
+ /**
+ * The maximum size a UI widget can be scaled so that it won't be bigger in screen pixels than its normal 35 pixel size.
+ */
+ @computed get maxWidgetScale() {
+ const maxWidgetSize = Math.min(this._sideBtnWidth * this.contentScaling, 0.25 * Math.min(NumCast(this.Document.width), NumCast(this.Document.height)));
+ return Math.max(maxWidgetSize / this._sideBtnWidth, 1);
+ }
+ /**
+ * How much to reactively scale a UI element so that it is as big as it can be (up to its normal 35pixel size) without being too big for the Doc content
+ */
+ @computed get uiBtnScaleTransform() {
+ return this.maxWidgetScale * Math.min(1, this.contentScaling);
+ }
+
@computed get flashcardMenu() {
return (
-
- Flip to front side to use GPT
:
Ask GPT to create an answer on the back side of the flashcard based on your question on the front
}>
-
(!this._frontSide ? this.findImageTags() : null)}>
-
-
-
- {DocCast(this.Document.embedContainer)?.type_collection === CollectionViewType.Carousel ? null : (
-
- Create a flashcard pile
}>
-
this.createFlashcardPile([this.Document], false)}>
-
-
-
-
Create new flashcard stack based on text }>
-
this.gptFlashcardPile()}>
-
+
+ {this.overlayAlternateIcon}
+ {!this._props.isContentActive() ? null : (
+ <>
+ {' '}
+ {!this._frontSide ? null : (
+ {
+ !this._frontSide ? "Flip to front side to use GPT":
+ "Ask GPT to create an answer on the back side of the flashcard based on your question on the front"}
+
// prettier-ignore
+ }>
+
(this._frontSide ? this.findImageTags() : null)}>
+
+
+
+ )}
+ {DocCast(this.Document.embedContainer)?.type_collection === CollectionViewType.Carousel ? null : (
+ <>
+
Create a flashcard pile }>
+
this.createFlashcardPile([this.Document], false)}>
+
+
+
+
Create new flashcard stack based on text }>
+
+
+
+
+ >
+ )}
+ Hover to reveal }>
+
+
-
+ >
)}
- Hover to reveal }>
- this.handleHover()}>
-
-
-
);
}
@@ -478,7 +510,6 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent
()
this._loading = false;
return;
}
- this.flipFlashcard();
}
try {
console.log(queryText);
@@ -655,6 +686,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
childActiveFunc = () => this._childActive;
+ contentScreenToLocalXf = () => this._props.ScreenToLocalTransform().scale(this._props.NativeDimScaling?.() || 1);
render() {
const clearButton = (which: string) => (
remove }>
@@ -687,6 +719,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent
()
removeDocument={whichSlot.endsWith('1') ? this.remDoc1 : this.remDoc2}
NativeWidth={returnZero}
NativeHeight={returnZero}
+ ScreenToLocalTransform={this.contentScreenToLocalXf}
isContentActive={this.childActiveFunc}
isDocumentActive={returnFalse}
dontSelect={returnTrue}
@@ -808,8 +841,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
) : null}
- {this._props.isContentActive() ? this.flashcardMenu : null}
- {this.overlayAlternateIcon}
+ {this.flashcardMenu}
);
}
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 170966471..c81631baa 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -51,6 +51,7 @@ export interface FieldViewSharedProps {
LayoutTemplate?: () => Opt;
renderDepth: number;
scriptContext?: unknown; // can be assigned anything and will be passed as 'scriptContext' to any OnClick script that executes on this document
+ screenXPadding?: () => number; // padding in screen space coordinates (used by text box to reflow around UI buttons in carouselView)
xPadding?: number;
yPadding?: number;
dontRegisterView?: boolean;
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.scss b/src/client/views/nodes/formattedText/FormattedTextBox.scss
index d6f13d9ee..f1ae1151f 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.scss
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.scss
@@ -101,12 +101,6 @@ audiotag:hover {
height: 22;
cursor: default;
}
-.formattedTextBox-flip {
- align-items: center;
- position: absolute;
- right: 2px;
- bottom: 4px;
-}
.formattedTextBox-outer {
position: relative;
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index c89737e1e..93153b453 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -763,10 +763,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent {
- const localDelta = this._props
- .ScreenToLocalTransform()
- .scale(this._props.NativeDimScaling?.() || 1)
- .transformDirection(delta[0], delta[1]);
+ const localDelta = this.DocumentView?.().screenToViewTransform().transformDirection(delta[0], delta[1]) ?? delta;
const sidebarWidth = (NumCast(this.layoutDoc._width) * Number(this.layout_sidebarWidthPercent.replace('%', ''))) / 100;
const width = NumCast(this.layoutDoc._width) + localDelta[0];
this.layoutDoc._layout_sidebarWidthPercent = Math.max(0, (sidebarWidth + localDelta[0]) / width) * 100 + '%';
@@ -1264,7 +1261,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent Doc.RecordingEvent, this.breakupDictation);
this._disposers.layout_autoHeight = reaction(
- () => ({ autoHeight: this.layout_autoHeight, fontSize: this.fontSize, css: this.Document[DocCss] }),
+ () => ({ autoHeight: this.layout_autoHeight, fontSize: this.fontSize, css: this.Document[DocCss], xMargin: this.Document.xMargin, yMargin: this.Document.yMargin }),
autoHeight => setTimeout(() => autoHeight && this.tryUpdateScrollHeight())
);
this._disposers.highlights = reaction(
@@ -2088,8 +2085,12 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent !this._props.isContentActive() && FormattedTextBoxComment.textBox === this && FormattedTextBoxComment.Hide);
- const paddingX = Math.max(this._props.xPadding ?? 0, NumCast(this.layoutDoc._xMargin));
- const paddingY = Math.max(this._props.yPadding ?? 0, NumCast(this.layoutDoc._yMargin));
+
+ const scrSize = (which: number, view = this._props.docViewPath().slice(-which)[0]) =>
+ [view._props.PanelWidth() / view.screenToLocalScale(), view._props.PanelHeight() / view.screenToLocalScale()]; // prettier-ignore
+ const scrMargin = [Math.max(0, (scrSize(2)[0] - scrSize(1)[0]) / 2), Math.max(0, (scrSize(2)[1] - scrSize(1)[1]) / 2)];
+ const paddingX = Math.max(NumCast(this.layoutDoc._xMargin), this._props.xPadding ?? 0, 0, ((this._props.screenXPadding?.() ?? 0) - scrMargin[0]) * this.ScreenToLocalBoxXf().Scale);
+ const paddingY = Math.max(NumCast(this.layoutDoc._yMargin), 0, ((this._props.yPadding ?? 0) - scrMargin[1]) * this.ScreenToLocalBoxXf().Scale);
const styleFromLayout = styleFromLayoutString(this.Document, this._props, scale); // this converts any expressions in the format string to style props. e.g.,
return styleFromLayout?.height === '0px' ? null : (
Date: Thu, 10 Oct 2024 18:27:25 -0400
Subject: adjusted hiding chrome for carousel. cleaned up some comparisonBox
quiz code. removed create flashcard pile button from flashcard - would
prefer carousel being added to marquee menu.
---
.../views/collections/CollectionCarouselView.tsx | 2 +-
.../views/collections/FlashcardPracticeUI.tsx | 46 ++++-----
src/client/views/nodes/ComparisonBox.scss | 29 +++++-
src/client/views/nodes/ComparisonBox.tsx | 109 ++++++++++-----------
src/client/views/pdf/AnchorMenu.tsx | 2 +-
5 files changed, 106 insertions(+), 82 deletions(-)
(limited to 'src/client/views/nodes/ComparisonBox.scss')
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index 538eba356..aa447c7bf 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -184,7 +184,7 @@ export class CollectionCarouselView extends CollectionSubView() {
}
@computed get navButtons() {
- return this.Document._chromeHidden || !this.curDoc() ? null : (
+ return !this.curDoc() ? null : (
<>
diff --git a/src/client/views/collections/FlashcardPracticeUI.tsx b/src/client/views/collections/FlashcardPracticeUI.tsx
index 4e424f5cd..7697d308b 100644
--- a/src/client/views/collections/FlashcardPracticeUI.tsx
+++ b/src/client/views/collections/FlashcardPracticeUI.tsx
@@ -15,7 +15,7 @@ import { SnappingManager } from '../../util/SnappingManager';
import { IconProp } from '@fortawesome/fontawesome-svg-core';
import { emptyFunction } from '../../../Utils';
-enum practiceMode {
+export enum practiceMode {
PRACTICE = 'practice',
QUIZ = 'quiz',
}
@@ -172,27 +172,29 @@ export class FlashcardPracticeUI extends ObservableReactComponent
{this.emptyMessage}
{this.practiceButtons}
-
- {!this.filterDoc || this._props.layoutDoc._chromeHidden ? null : (
-
- )}
- {this.practiceModesMenu}
-
+ {this._props.layoutDoc._chromeHidden ? null : (
+
+ {!this.filterDoc ? null : (
+
+ )}
+ {this.practiceModesMenu}
+
+ )}
>
);
}
diff --git a/src/client/views/nodes/ComparisonBox.scss b/src/client/views/nodes/ComparisonBox.scss
index 8156c50f6..c328ef4bf 100644
--- a/src/client/views/nodes/ComparisonBox.scss
+++ b/src/client/views/nodes/ComparisonBox.scss
@@ -9,6 +9,7 @@
z-index: 0;
pointer-events: none;
display: flex;
+ flex-direction: column;
p {
// bcz: what is this styling for? if text in the comparison box is colored, then this causes it to render with a black outline
color: rgb(0, 0, 0);
@@ -32,8 +33,10 @@
padding-right: 5px;
border-radius: 2px;
height: 17%;
- display: inline-block;
bottom: 0;
+ overflow: hidden;
+ display: flex;
+ width: 100%;
&.schema-header-button {
color: gray;
@@ -61,6 +64,29 @@
float: left;
border-radius: 2px;
}
+ .submit-buttonrecord {
+ border-radius: 2px;
+ }
+ .submit-buttonpronunciation {
+ display: inline-flex;
+ align-items: center;
+ }
+ .submit-buttonschema-header-button {
+ position: absolute;
+ top: 5px;
+ left: 11px;
+ z-index: 10;
+ width: 5px;
+ height: 5px;
+ cursor: pointer;
+ }
+ .submit-buttonsubmit {
+ border-radius: 2px;
+ margin-bottom: 3px;
+ width: 100%;
+ display: inline-flex;
+ align-items: center;
+ }
}
.dropbtn {
@@ -194,6 +220,7 @@
.loading-spinner {
display: flex;
+ position: absolute;
justify-content: center;
align-items: center;
height: 90%;
diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx
index 9852228fa..ccbe98257 100644
--- a/src/client/views/nodes/ComparisonBox.tsx
+++ b/src/client/views/nodes/ComparisonBox.tsx
@@ -1,7 +1,7 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Tooltip } from '@mui/material';
import axios from 'axios';
-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 ReactLoading from 'react-loading';
@@ -30,6 +30,7 @@ import './ComparisonBox.scss';
import { DocumentView } from './DocumentView';
import { FieldView, FieldViewProps } from './FieldView';
import { FormattedTextBox } from './formattedText/FormattedTextBox';
+import { practiceMode } from '../collections/FlashcardPracticeUI';
const API_URL = 'https://api.unsplash.com/search/photos';
@@ -122,18 +123,11 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
)}
{DocCast(this.Document.embedContainer)?.type_collection !== CollectionViewType.Freeform ? null : (
- <>
- Create a flashcard pile }>
-
this.createFlashcardPile([this.Document], false)}>
-
-
-
-
Create new flashcard stack based on text }>
-
-
-
-
- >
+ Create new flashcard stack based on text}>
+
+
+
+
)}
>
)}
@@ -153,7 +147,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
this._frontSide = !this._frontSide;
};
- @action handleRenderGPTClick = async () => {
+ @action handleRenderGPTClick = () => {
const phonTrans = DocCast(this.Document.audio) ? DocCast(this.Document.audio).phoneticTranscription : undefined;
if (phonTrans) {
this._inputValue = StrCast(phonTrans);
@@ -490,20 +484,22 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
}
try {
const res = await gptAPICall(questionText, GPTCallType.FLASHCARD);
- if (!res) {
- console.error('GPT call failed');
- return;
- }
- if (callType == GPTCallType.CHATCARD) {
- DocCast(this.dataDoc[this.props.fieldKey + '_0'])[DocData].text = res;
- } else if (callType == GPTCallType.QUIZ) {
- this._frontSide = true;
- this._outputValue = res.replace(/UserAnswer/g, "user's answer").replace(/Rubric/g, 'rubric');
- } else if (callType === GPTCallType.FLASHCARD) {
+ runInAction(() => {
+ if (!res) {
+ console.error('GPT call failed');
+ return;
+ }
+ if (callType == GPTCallType.CHATCARD) {
+ DocCast(this.dataDoc[this.props.fieldKey + '_0'])[DocData].text = res;
+ } else if (callType == GPTCallType.QUIZ) {
+ this._frontSide = true;
+ this._outputValue = res.replace(/UserAnswer/g, "user's answer").replace(/Rubric/g, 'rubric');
+ } else if (callType === GPTCallType.FLASHCARD) {
+ this._loading = false;
+ return res;
+ }
this._loading = false;
- return res;
- }
- this._loading = false;
+ });
return res;
} catch (err) {
console.error('GPT call failed', err);
@@ -725,24 +721,33 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
if (this.Document._layout_isFlashcard) {
const side = this._frontSide ? 1 : 0;
+ const dataSplit = StrCast(this.dataDoc.data).includes('Keyword: ') ? StrCast(this.dataDoc.data).split('Keyword: ') : StrCast(this.dataDoc.data).split('Answer: ');
+ const textCreator = (which: number, title: string, text: string) => {
+ const newDoc = Docs.Create.TextDocument(text, {
+ title, //
+ _layout_autoHeight: true,
+ _layout_centered: true,
+ text_align: 'center',
+ _layout_fitWidth: true,
+ });
+ this.addDoc(newDoc, this.fieldKey + '_' + which);
+ return newDoc;
+ };
// add text box to each side when comparison box is first created
if (!this.dataDoc[this.fieldKey + '_0'] && !this._isEmpty) {
- const dataSplit = StrCast(this.dataDoc.data).includes('Keyword: ') ? StrCast(this.dataDoc.data).split('Keyword: ') : StrCast(this.dataDoc.data).split('Answer: ');
- const newDoc = Docs.Create.TextDocument(dataSplit[1], { title: 'answer', _layout_autoHeight: true, _layout_centered: true, text_align: 'center', _layout_fitWidth: true });
- this.addDoc(newDoc, this.fieldKey + '_0');
+ textCreator(0, 'answer', dataSplit[1]);
}
if (!this.dataDoc[this.fieldKey + '_1'] && !this._isEmpty) {
- const dataSplit = StrCast(this.dataDoc.data).includes('Keyword: ') ? StrCast(this.dataDoc.data).split('Keyword: ') : StrCast(this.dataDoc.data).split('Answer: ');
- const newDoc = Docs.Create.TextDocument(dataSplit[0], { title: 'question', _layout_autoHeight: true, _layout_centered: true, text_align: 'center', _layout_fitWidth: true });
- this.addDoc(newDoc, this.fieldKey + '_1');
+ const question = textCreator(1, 'question', dataSplit[0] || 'hint: Enter a topic, select this document and click the stack button to have GPT create a deck of cards');
+ Doc.SelectOnLoad = dataSplit[0] ? undefined : question;
}
- if (DocCast(this.Document.embedContainer) && DocCast(this.Document.embedContainer).practiceMode === 'quiz') {
+ if (DocCast(this.Document.embedContainer).practiceMode === practiceMode.QUIZ) {
const text = StrCast(RTFCast(DocCast(this.dataDoc[this.fieldKey + '_1']).text)?.Text);
return (
-
+
{text}
Return to all flashcards and add text to both sides.
@@ -757,38 +762,28 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent
()
readOnly={this._frontSide}>
{this._loading ? (
-
-
-
this.openContextMenu(e.clientX, e.clientY, false)}
- style={{ position: 'absolute', top: '5px', left: '11px', zIndex: '100', width: '5px', height: '5px', cursor: 'pointer' }}>
-
+
+
this.openContextMenu(e.clientX, e.clientY, false)}>
+
-
+
{ }
- this.openContextMenu(e.clientX, e.clientY, true)} style={{ position: 'absolute', top: '5px', left: '50px', zIndex: '100', cursor: 'pointer' }}>
-
+
this.openContextMenu(e.clientX, e.clientY, true)} style={{ left: '50px', zIndex: '100' }}>
+
-
+
Evaluate Pronunciation
-
- {!this._frontSide ? (
-
- Submit
-
- ) : (
-
- Redo the Question
-
- )}
+
+ {this._frontSide ? 'Redo the Question' : 'Submit'}
+
@@ -804,7 +799,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent
()
onMouseLeave={() => this.hoverFlip(false)}>
{displayBox(`${this.fieldKey}_${side === 0 ? 1 : 0}`, side, this._props.PanelWidth() - 3)}
{this._loading ? (
-
+
) : null}
diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx
index b204d3692..7243473e0 100644
--- a/src/client/views/pdf/AnchorMenu.tsx
+++ b/src/client/views/pdf/AnchorMenu.tsx
@@ -254,7 +254,7 @@ export class AnchorMenu extends AntimodeMenu
{
/>
)}
{/* Adds a create flashcards option to the anchor menu, which calls the gptFlashcard method. */}
- } color={SettingsManager.userColor} />
+ } color={SettingsManager.userColor} />
} color={SettingsManager.userColor} />
{this._selectedText && (
Date: Mon, 14 Oct 2024 19:55:32 -0400
Subject: reorganized comparisonBox related components -- moved stuff down into
Docs.Crete and CurrentUserUtils. changed Doc.Copy to copy Doc's in fields
tagged with cloneOnCopy. Changed ComparisonBox to support hover for slide or
flip views. Fixed pointerEfvents for hover in comparisonBox
---
src/client/documents/DocUtils.ts | 2 -
src/client/documents/Documents.ts | 43 ++++-
src/client/util/CurrentUserUtils.ts | 4 +-
.../views/collections/CollectionCarousel3DView.tsx | 33 ++--
.../views/collections/CollectionCarouselView.tsx | 58 +++---
src/client/views/collections/CollectionSubView.tsx | 2 +-
src/client/views/collections/CollectionView.tsx | 6 +-
.../views/collections/FlashcardPracticeUI.tsx | 36 ++--
src/client/views/nodes/ComparisonBox.scss | 3 +-
src/client/views/nodes/ComparisonBox.tsx | 196 +++++++++++----------
.../views/nodes/formattedText/FormattedTextBox.tsx | 20 ---
src/fields/Doc.ts | 2 +-
12 files changed, 223 insertions(+), 182 deletions(-)
(limited to 'src/client/views/nodes/ComparisonBox.scss')
diff --git a/src/client/documents/DocUtils.ts b/src/client/documents/DocUtils.ts
index 5f54f9d0a..19f3c89ef 100644
--- a/src/client/documents/DocUtils.ts
+++ b/src/client/documents/DocUtils.ts
@@ -103,7 +103,6 @@ export namespace DocUtils {
return false;
}
const facetKeys = Object.keys(filterFacets).filter(fkey => fkey !== 'cookies' && fkey !== ClientUtils.noDragDocsFilter.split(Doc.FilterSep)[0]);
- // eslint-disable-next-line no-restricted-syntax
for (const facetKey of facetKeys) {
const facet = filterFacets[facetKey];
@@ -288,7 +287,6 @@ export namespace DocUtils {
return doc;
}
export function AssignDocField(doc: Doc, field: string, creator: (reqdOpts: DocumentOptions, items?: Doc[]) => Doc, reqdOpts: DocumentOptions, items?: Doc[], scripts?: { [key: string]: string }, funcs?: { [key: string]: string }) {
- // eslint-disable-next-line no-return-assign
return DocUtils.AssignScripts(DocUtils.AssignOpts(DocCast(doc[field]), reqdOpts, items) ?? (doc[field] = creator(reqdOpts, items)), scripts, funcs);
}
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index f71b9f879..99af1f1a9 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -18,6 +18,7 @@ import { PointData } from '../../pen-gestures/GestureTypes';
import { DocServer } from '../DocServer';
import { dropActionType } from '../util/DropActionTypes';
import { CollectionViewType, DocumentType } from './DocumentTypes';
+import { Id } from '../../fields/FieldSymbols';
class EmptyBox {
public static LayoutString() {
@@ -360,6 +361,7 @@ export class DocumentOptions {
isFolder?: BOOLt = new BoolInfo('is document a tree view folder');
_isTimelineLabel?: BOOLt = new BoolInfo('is document a timeline label');
_isLightbox?: BOOLt = new BoolInfo('whether a collection acts as a lightbox by opening lightbox links by hiding all other documents in collection besides link target');
+ cloneOnCopy?: BOOLt = new BoolInfo('if this Doc is a field of another Doc, then it should be copied when the other Doc is copied');
mapPin?: DOCt = new DocInfo('pin associated with a config anchor', false);
config_latitude?: NUMt = new NumInfo('latitude of a map', false);
@@ -420,6 +422,12 @@ export class DocumentOptions {
flexGap?: NUMt = new NumInfo('Linear view flex gap');
flexDirection?: 'unset' | 'row' | 'column' | 'row-reverse' | 'column-reverse';
+ // Comparison
+ data_revealOp?: STRt = new StrInfo("visual reveal type for front and back of comparison - 'slide' or 'flip' ");
+ data_revealOp_hover?: BOOLt = new BoolInfo('reveal back of comparison manually or by hovering');
+ data_front?: DOCt = new DocInfo('contents of front of flashcard/comparison');
+ data_back?: DOCt = new DocInfo('contents of back of flashcard/comparison');
+
link?: string;
link_description?: string; // added for links
link_relationship?: string; // type of relatinoship a link represents
@@ -784,8 +792,39 @@ export namespace Docs {
return InstanceFromProto(Prototypes.get(DocumentType.SCREENSHOT), '', options);
}
- export function ComparisonDocument(text: string, options: DocumentOptions = { title: 'Comparison Box' }) {
- return InstanceFromProto(Prototypes.get(DocumentType.COMPARISON), text, options);
+ export function ComparisonDocument(title: string, options: DocumentOptions) {
+ return InstanceFromProto(Prototypes.get(DocumentType.COMPARISON), '', options);
+ }
+ /**
+ * Creates a text box where the supplied text (and optional iimage) will be vertically
+ * and horizontally centered. If text_placeholder is set to true, then the text will be
+ * treated as placeholder text and automatically selected when the text box is selected.
+ * @param title name of text box
+ * @param text text to display in text box
+ * @param opts metadata fields to set on text box
+ * @param img optional image to add to text box
+ * @returns
+ */
+ export function CenteredTextCreator(title: string, text: string, opts: DocumentOptions, img?: Doc) {
+ return TextDocument(RichTextField.textToRtf(text, img?.[Id]), {
+ title, //
+ _layout_autoHeight: true,
+ _layout_centered: true,
+ text_align: 'center',
+ _layout_fitWidth: true,
+ ...opts,
+ });
+ }
+
+ export function FlashcardDocument(title: string, front?: Doc, back?: Doc, options: DocumentOptions = { title: 'Flashcard' }) {
+ return InstanceFromProto(Prototypes.get(DocumentType.COMPARISON), '', {
+ data_front: front ?? CenteredTextCreator('question', 'hint: Enter a topic, select this document and click the stack button to have GPT create a deck of cards', { text_placeholder: true, cloneOnCopy: true }, undefined),
+ data_back: back ?? CenteredTextCreator('answer', 'answer here', { text_placeholder: true, cloneOnCopy: true }, undefined),
+ _layout_fitWidth: true,
+ _layout_isFlashcard: true,
+ title,
+ ...options,
+ });
}
export function DiagramDocument(options: DocumentOptions = { title: '' }) {
return InstanceFromProto(Prototypes.get(DocumentType.DIAGRAM), undefined, options);
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 798cdf5a9..555ccdd88 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -369,14 +369,14 @@ pie title Minerals in my tap water
creator:(opts:DocumentOptions)=> Doc // 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: opts => Docs.Create.ComparisonDocument("", opts), opts: { _layout_isFlashcard: true, _layout_fitWidth: true, _width: 300, _height: 300}},
+ {key: "Flashcard", creator: opts => Docs.Create.FlashcardDocument("", undefined, undefined, opts),opts: { _width: 300, _height: 300}},
{key: "Image", creator: opts => Docs.Create.ImageDocument("", opts), opts: { _width: 400, _height:400 }},
{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: opts => Docs.Create.ComparisonDocument("",opts), opts: { _width: 300, _height: 300 }},
+ {key: "Comparison", creator: opts => Docs.Create.ComparisonDocument("", opts), opts: { _width: 300, _height: 300 }},
{key: "Diagram", creator: Docs.Create.DiagramDocument, opts: { _width: 300, _height: 300, _type_collection: CollectionViewType.Freeform, layout_diagramEditor: CollectionView.LayoutString("data") }, scripts: { onPaint: `toggleDetail(documentView, "diagramEditor","")`}},
{key: "Audio", creator: opts => Docs.Create.AudioDocument(nullAudio, opts),opts: { _width: 200, _height: 100, }},
{key: "Audio", creator: opts => Docs.Create.AudioDocument(nullAudio, opts),opts: { _width: 200, _height: 100, _layout_fitWidth: true, }},
diff --git a/src/client/views/collections/CollectionCarousel3DView.tsx b/src/client/views/collections/CollectionCarousel3DView.tsx
index 05be376ca..e9ace733e 100644
--- a/src/client/views/collections/CollectionCarousel3DView.tsx
+++ b/src/client/views/collections/CollectionCarousel3DView.tsx
@@ -15,6 +15,7 @@ import { DocumentView } from '../nodes/DocumentView';
import { FocusViewOptions } from '../nodes/FocusViewOptions';
import './CollectionCarousel3DView.scss';
import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
+import { computedFn } from 'mobx-utils';
// eslint-disable-next-line @typescript-eslint/no-require-imports
const { CAROUSEL3D_CENTER_SCALE, CAROUSEL3D_SIDE_SCALE, CAROUSEL3D_TOP } = require('../global/globalCssVariables.module.scss');
@@ -53,18 +54,22 @@ export class CollectionCarousel3DView extends CollectionSubView() {
centerScale = Number(CAROUSEL3D_CENTER_SCALE);
sideScale = Number(CAROUSEL3D_SIDE_SCALE);
- panelWidth = () => this._props.PanelWidth() / 3;
- panelHeight = () => this._props.PanelHeight() * this.sideScale;
+ panelWidth = () => this._props.PanelWidth() / 3 / this.nativeScaling();
+ panelHeight = () => (this._props.PanelHeight() * this.sideScale) / this.nativeScaling();
onChildDoubleClick = () => ScriptCast(this.layoutDoc.onChildDoubleClick);
isContentActive = () => this._props.isSelected() || this._props.isContentActive() || this._props.isAnyChildContentActive();
- isChildContentActive = () =>
- this._props.isContentActive?.() === false
- ? false
- : this._props.isDocumentActive?.() && (this._props.childDocumentsActive?.() || BoolCast(this.Document.childDocumentsActive))
- ? true
- : this._props.childDocumentsActive?.() === false || this.Document.childDocumentsActive === false
+ isChildContentActive = computedFn(
+ (doc: Doc) => () =>
+ this._props.isContentActive?.() === false
? false
- : undefined;
+ : this._props.isDocumentActive?.() && (this._props.childDocumentsActive?.() || BoolCast(this.Document.childDocumentsActive))
+ ? true
+ : this._props.isContentActive?.() && this.curDoc() === doc
+ ? true
+ : this._props.childDocumentsActive?.() === false || this.Document.childDocumentsActive === false
+ ? false
+ : undefined
+ );
contentScreenToLocalXf = () => this._props.ScreenToLocalTransform().scale(this._props.NativeDimScaling?.() || 1);
childScreenLeftToLocal = () =>
this.contentScreenToLocalXf()
@@ -110,7 +115,7 @@ export class CollectionCarousel3DView extends CollectionSubView() {
LayoutTemplateString={this._props.childLayoutString}
focus={this.focus}
ScreenToLocalTransform={dxf}
- isContentActive={this.isChildContentActive}
+ isContentActive={this.isChildContentActive(child)}
isDocumentActive={this._props.childDocumentsActive?.() || this.Document._childDocumentsActive ? this._props.isDocumentActive : this.isContentActive}
PanelWidth={this.panelWidth}
PanelHeight={this.panelHeight}
@@ -125,7 +130,6 @@ export class CollectionCarousel3DView extends CollectionSubView() {
}
changeSlide = (direction: number) => {
- DocumentView.DeselectAll();
this.layoutDoc._carousel_index = !this.curDoc() ? 0 : (NumCast(this.layoutDoc._carousel_index) + direction + this.carouselItems.length) % (this.carouselItems.length || 1);
};
@@ -205,9 +209,10 @@ export class CollectionCarousel3DView extends CollectionSubView() {
docViewProps = () => ({
...this._props, //
isDocumentActive: this._props.childDocumentsActive?.() ? this._props.isDocumentActive : this._props.isContentActive,
- isContentActive: this.isChildContentActive,
+ isContentActive: this._props.isContentActive,
ScreenToLocalTransform: this.contentScreenToLocalXf,
});
+ nativeScaling = () => this._props.NativeDimScaling?.() || 1;
render() {
return (
{this.content}
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index ef66a2c83..ff587b199 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -58,18 +58,12 @@ export class CollectionCarouselView extends CollectionSubView() {
/**
* Goes to the next Doc in the stack subject to the currently selected filter option.
*/
- advance = (e?: React.MouseEvent) => {
- e?.stopPropagation();
- this.move(1);
- };
+ advance = () => this.move(1);
/**
* Goes to the previous Doc in the stack subject to the currently selected filter option.
*/
- goback = (e: React.MouseEvent) => {
- e.stopPropagation();
- this.move(-1);
- };
+ goback = () => this.move(-1);
curDoc = () => this.carouselItems[this.carouselIndex]?.layout;
@@ -78,24 +72,23 @@ export class CollectionCarouselView extends CollectionSubView() {
const childValue = doc?.['caption_' + property] ? this._props.styleProvider?.(doc, captionProps, property) : undefined;
return childValue ?? this._props.styleProvider?.(this.layoutDoc, captionProps, property);
};
- contentPanelWidth = () => this._props.PanelWidth() - 2 * NumCast(this.layoutDoc.xMargin);
- contentPanelHeight = () => this._props.PanelHeight() - (StrCast(this.layoutDoc._layout_showCaption) ? 50 : 0) - 2 * NumCast(this.layoutDoc.yMargin);
+ contentPanelWidth = () => (this._props.PanelWidth() - 2 * NumCast(this.layoutDoc.xMargin)) / this.nativeScaling();
+ contentPanelHeight = () => (this._props.PanelHeight() - (StrCast(this.layoutDoc._layout_showCaption) ? 50 : 0) - 2 * NumCast(this.layoutDoc.yMargin)) / this.nativeScaling();
onContentDoubleClick = () => ScriptCast(this.layoutDoc.onChildDoubleClick);
onContentClick = () => ScriptCast(this.layoutDoc.onChildClick);
captionWidth = () => this._props.PanelWidth() - 2 * this.captionMarginX;
contentScreenToLocalXf = () =>
this._props
- .ScreenToLocalTransform()
- .translate(-NumCast(this.layoutDoc.xMargin), -NumCast(this.layoutDoc.yMargin))
- .scale(this._props.NativeDimScaling?.() || 1);
+ .ScreenToLocalTransform() //
+ .translate(-NumCast(this.layoutDoc.xMargin) / this.nativeScaling(), -NumCast(this.layoutDoc.yMargin) / this.nativeScaling());
isChildContentActive = () =>
this._props.isContentActive?.() === false
? false
- : this._props.isDocumentActive?.() && (this._props.childDocumentsActive?.() || BoolCast(this.Document.childDocumentsActive))
+ : this._props.isContentActive()
? true
: this._props.childDocumentsActive?.() === false || this.Document.childDocumentsActive === false
? false
- : undefined;
+ : undefined; // prettier-ignore
onPassiveWheel = (e: WheelEvent) => e.stopPropagation();
renderDoc = (doc: Doc, showCaptions: boolean, overlayFunc?: (r: DocumentView | null) => void) => {
return (
@@ -202,6 +195,8 @@ export class CollectionCarouselView extends CollectionSubView() {
);
}
+ nativeScaling = () => this._props.NativeDimScaling?.() || 1;
+
docViewProps = () => ({
...this._props, //
isDocumentActive: this._props.childDocumentsActive?.() ? this._props.isDocumentActive : this._props.isContentActive,
@@ -212,20 +207,25 @@ export class CollectionCarouselView extends CollectionSubView() {
render() {
return (
-
- {this.content}
- {this.flashCardUI(this.curDoc, this.docViewProps, this.answered)}
- {this.navButtons}
+
+
+ {this.content}
+ {this.flashCardUI(this.curDoc, this.docViewProps, this.answered)}
+ {this.navButtons}
+
);
}
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index f85b0b433..ab5b70a85 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -523,7 +523,7 @@ export function CollectionSubView
() {
/**
* How much the content of the collection is being scaled based on its nesting and its fit-to-width settings
*/
- @computed get contentScaling() { return this.ScreenToLocalBoxXf().Scale * (this._props.NativeDimScaling?.() ?? 1); } // prettier-ignore
+ @computed get contentScaling() { return this.ScreenToLocalBoxXf().Scale; } // prettier-ignore
/**
* The maximum size a UI widget can be in collection coordinates based on not wanting the widget to visually obscure too much of the collection
* This takes the desired screen space size and converts into collection coordinates. It then returns the smaller of the converted
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 7418d4360..6f0833a22 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -83,7 +83,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent (this._props.renderDepth ? this.ScreenToLocalBoxXf() : this.ScreenToLocalBoxXf().scale(this._props.PanelWidth() / this.bodyPanelWidth()));
+ screenToLocalTransform = this.ScreenToLocalBoxXf;
// prettier-ignore
private renderSubView = (type: CollectionViewType | undefined, props: SubCollectionViewProps) => {
TraceMobx();
@@ -202,8 +202,6 @@ export class CollectionView extends ViewBoxAnnotatableComponent this._props.PanelWidth();
-
childLayoutTemplate = () => this._props.childLayoutTemplate?.() || Cast(this.Document.childLayoutTemplate, Doc, null);
isContentActive = () => this._isContentActive;
@@ -221,7 +219,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent togglePracticeMode(val as practiceMode)}
/>
- }
- label={StrCast(this._props.layoutDoc.revealOp)}
- onPointerDown={e =>
- setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => {
- this._props.layoutDoc.revealOp = this._props.layoutDoc.revealOp === flashcardRevealOp.HOVER ? flashcardRevealOp.FLIP : flashcardRevealOp.HOVER;
- this._props.layoutDoc.childDocumentsActive = this._props.layoutDoc.revealOp === 'hover' ? true : undefined;
- })
- }
+ multiSelect={false}
+ isToggle={false}
+ toggleStatus={!!this.practiceMode}
+ label={StrCast(this._props.layoutDoc.revealOp, flashcardRevealOp.FLIP)}
+ items={[
+ ['reveal', StrCast(this._props.layoutDoc.revealOp) === flashcardRevealOp.SLIDE ? 'expand' : 'question', StrCast(this._props.layoutDoc.revealOp, flashcardRevealOp.FLIP)],
+ ['trigger', this._props.layoutDoc.revealOp_hover ? 'hand-point-up' : 'hand', this._props.layoutDoc.revealOp_hover ? 'show on hover' : 'show on click'],
+ ].map(([item, icon, tooltip]) => ({
+ icon: ,
+ tooltip: tooltip,
+ val: item,
+ }))}
+ selectedItems={this._props.layoutDoc.revealOp_hover ? ['reveal', 'trigger'] : 'reveal'}
+ onSelectionChange={(val: (string | number) | (string | number)[]) => {
+ if (val === 'reveal') this._props.layoutDoc.revealOp = this._props.layoutDoc.revealOp === flashcardRevealOp.SLIDE ? flashcardRevealOp.FLIP : flashcardRevealOp.SLIDE;
+ if (val === 'trigger') this._props.layoutDoc.revealOp_hover = !this._props.layoutDoc.revealOp_hover;
+ }}
/>
);
diff --git a/src/client/views/nodes/ComparisonBox.scss b/src/client/views/nodes/ComparisonBox.scss
index c328ef4bf..d1cc48051 100644
--- a/src/client/views/nodes/ComparisonBox.scss
+++ b/src/client/views/nodes/ComparisonBox.scss
@@ -246,8 +246,7 @@
pointer-events: none;
}
-.comparisonBox-interactive {
- pointer-events: unset;
+.comparisonBox-slide {
cursor: ew-resize;
.slide-bar {
diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx
index 80ef126dc..38ce5f2f7 100644
--- a/src/client/views/nodes/ComparisonBox.tsx
+++ b/src/client/views/nodes/ComparisonBox.tsx
@@ -35,22 +35,21 @@ import { FormattedTextBox } from './formattedText/FormattedTextBox';
const API_URL = 'https://api.unsplash.com/search/photos';
/**
- * This view serves three distinct functions depending on the metadata field layout_isFlashcard
- * 1) it provides a before/after animated sliding transition between two Docs
- * 2) it provides a question/answer switch between two Docs (flashcard)
- * 3) it provides a quiz view that displays a question and a user answer that can be "scored" by GPT
+ * This view serves two distinct functions depending on the revealOp field ('slide' or 'flip)
+ * 1) ('slide') - provides a before/after animated sliding transition between two Docs
+ * 2) ('flip') - provides a question/answer flip between two Docs
+ * And a third function that overrides the first two if the doc's container has its 'practiceMode' set to 'quiz'
+ * 3) ('quiz') - it provides a quiz view that displays a question and a user answer that can be "scored" by GPT
+ * NOTE: this should probably be changed to passing down a prop to the flashcard telling it to render as a quiz.
*
* In each case, the two docs are stored in the
_front and _back fields
*
- * In the case of the flashcard, there is an icon that allows the user to choose between a
- * hover and a flip action to switch between cards. The transition is stored in the 'revealOp' field.
- * In addition, if a flashcard is created without data in the front/back fields, this will
- * create Text documents with placeholder text indicating to the user how to fill in the cards.
- * One option is to allow the user to enter a topic and, by clicking on the flashcard stack button,
- * convert the comparision box into a stack of comparison boxes filled in by GPT about the topic.
+ * For 'flip' and 'slide', the trigger can either be clicking, or hovering as determined by the revealOp_hover field.
+ * For 'quiz' the data of both Docs are shown in a single-view quiz display.
*
- * Quiz mode is activated when the parent collection has its 'quiz' field set when it renders a flashcard.
- * NOTE: this should probably be changed to passing down a prop to the flashcard telling it to render as a quiz.
+ * Users can create a stack of flashcards all at once (only) from an empty flashcard by entering a topic into the front card
+ * and clicking on the flashcard stack button. This will convert the comparision box into a stack of comparison boxes
+ * filled in by GPT about the topic.
*
*/
@@ -67,7 +66,6 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
*/
public static createFlashcard(tuple: string, frontKey: string, backKey: string, useDoc?: Doc) {
const [ktoken, atoken] = [ComparisonBox.ktoken, ComparisonBox.atoken];
- const newDoc = useDoc ?? Docs.Create.ComparisonDocument('', { _layout_isFlashcard: true, _width: 300, _height: 300 });
const question = (tuple.includes(ktoken) ? tuple.split(ktoken)[0] : tuple).split(atoken)[0];
const rest = tuple.replace(question, '');
// prettier-ignore
@@ -78,9 +76,14 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
rest.replace(atoken,""); // finally if there's no keyword, just get rid of answer token and take what's left
const keyword = rest.replace(atoken, '').replace(answer, '').replace(ktoken, '').trim();
const fillInFlashcard = (img?: Doc) => {
- newDoc[DocData][frontKey] = FormattedTextBox.centeredTextCreator('question', question, img);
- newDoc[DocData][backKey] = FormattedTextBox.centeredTextCreator('answer', answer);
- return newDoc;
+ const front = Docs.Create.CenteredTextCreator('question', question, {}, img);
+ const back = Docs.Create.CenteredTextCreator('answer', answer, {});
+ if (useDoc) {
+ useDoc[DocData][frontKey] = front;
+ useDoc[DocData][backKey] = back;
+ return useDoc;
+ }
+ return Docs.Create.FlashcardDocument('flashcard', front, back, { _width: 300, _height: 300 });
};
return keyword && keyword.toLowerCase() !== 'none' ? ComparisonBox.fetchImages(keyword).then(img => fillInFlashcard(img)) : fillInFlashcard();
}
@@ -98,6 +101,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
.map(tuple => ComparisonBox.createFlashcard(tuple, front, back))
).then(docs => {
return Docs.Create.CarouselDocument(docs, {
+ title: 'flashcard deck',
_width: width,
_height: height,
_layout_fitWidth: false,
@@ -116,12 +120,11 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
private _sideBtnWidth = 35;
private _closeRef = React.createRef();
private _disposers: { [key: string]: DragManager.DragDropDisposer | undefined } = {};
- private _reactDisposer: IReactionDisposer | undefined;
+ private _reactDisposer: { [key: string]: IReactionDisposer } = {};
@observable private _inputValue = '';
@observable private _outputValue = '';
@observable private _loading = false;
- @observable private _isEmpty = false;
@observable private _childActive = false;
@observable private _animating = '';
@observable private _listening = false;
@@ -135,18 +138,28 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
componentDidMount() {
this._props.setContentViewBox?.(this);
- this._reactDisposer = reaction(
- () => this._props.isSelected(), // when this reaction should update
+ this._reactDisposer.select = reaction(
+ () => this._props.isSelected(),
selected => {
- if (selected && this.isFlashcard) this.activateContent();
+ if (selected && this.revealOp !== flashcardRevealOp.SLIDE) this.activateContent();
!selected && (this._childActive = false);
}, // what it should update to
{ fireImmediately: true }
);
+ this._reactDisposer.hover = reaction(
+ () => this._props.isContentActive(),
+ hover => {
+ if (!hover) {
+ this.revealOp === flashcardRevealOp.FLIP && this.animateFlipping(this.frontKey);
+ this.revealOp === flashcardRevealOp.SLIDE && this.animateSliding(this._props.PanelWidth() - 3);
+ }
+ }, // what it should update to
+ { fireImmediately: true }
+ );
}
componentWillUnmount() {
- this._reactDisposer?.();
+ Object.values(this._reactDisposer).forEach(disposer => disposer?.());
}
protected createDropTarget = (ele: HTMLDivElement | null, fieldKey: string) => {
@@ -169,7 +182,8 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
return undefined;
}, 'internal drop');
- @computed get isQuizMode() { return DocCast(this.Document.embedContainer)?.practiceMode === practiceMode.QUIZ; } // prettier-ignore
+ @computed get containerDoc() { return this._props.docViewPath().slice(-2)[0]?.Document; } // prettier-ignore
+ @computed get isQuizMode() { return this.containerDoc?.practiceMode === practiceMode.QUIZ; } // prettier-ignore
@computed get isFlashcard() { return BoolCast(this.Document.layout_isFlashcard); } // prettier-ignore
@computed get frontKey() { return this._props.fieldKey + '_front'; } // prettier-ignore
@computed get backKey() { return this._props.fieldKey + '_back'; } // prettier-ignore
@@ -178,10 +192,10 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
@computed get revealOpKey() { return `_${this._props.fieldKey}_revealOp`; } // prettier-ignore
@computed get clipHeightKey() { return `_${this._props.fieldKey}_clipHeight`; } // prettier-ignore
@computed get clipWidthKey() { return `_${this._props.fieldKey}_clipWidth`; } // prettier-ignore
- @computed get clipWidth() { return NumCast(this.layoutDoc[this.clipWidthKey], 50); } // prettier-ignore
+ @computed get clipWidth() { return NumCast(this.layoutDoc[this.clipWidthKey], this.isFlashcard ? 100: 50); } // prettier-ignore
@computed get clipHeight() { return NumCast(this.layoutDoc[this.clipHeightKey], 200); } // prettier-ignore
- @computed get revealOp() { return StrCast(this.layoutDoc[this.revealOpKey], StrCast(this._props.docViewPath().slice(-2)[0]?.Document.revealOp)); } // prettier-ignore
- set revealOp(value:string) { this.layoutDoc[this.revealOpKey] = value; } // prettier-ignore
+ @computed get revealOp() { return StrCast(this.layoutDoc[this.revealOpKey], StrCast(this.containerDoc?.revealOp, this.isFlashcard ? flashcardRevealOp.FLIP : flashcardRevealOp.SLIDE)) as flashcardRevealOp; } // prettier-ignore
+ @computed get revealOpHover() { return BoolCast(this.layoutDoc[this.revealOpKey+"_hover"], BoolCast(this.containerDoc?.revealOp_hover)); } // prettier-ignore
@computed get loading() { return this._loading; } // prettier-ignore
set loading(value) { runInAction(() => { this._loading = value; })} // prettier-ignore
@@ -193,13 +207,13 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
onPointerDown={e =>
setupMoveUpEvents(e.target, e, returnFalse, emptyFunction, () => {
if (!this.revealOp || this.revealOp === flashcardRevealOp.FLIP) {
- this.flipFlashcard();
+ this.animateFlipping();
}
})
}
style={{
- background: this.revealOp === flashcardRevealOp.HOVER ? 'gray' : this._renderSide === this.backKey ? 'white' : 'black',
- color: this.revealOp === flashcardRevealOp.HOVER ? 'black' : this._renderSide === this.backKey ? 'black' : 'white',
+ background: this.revealOpHover ? 'gray' : this._renderSide === this.backKey ? 'white' : 'black',
+ color: this.revealOpHover ? 'black' : this._renderSide === this.backKey ? 'black' : 'white',
display: 'inline-block',
}}>
@@ -223,7 +237,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
@computed get flashcardMenu() {
return SnappingManager.HideDecorations ? null : (
- {this.revealOp === flashcardRevealOp.HOVER || !this._props.isSelected() ? null : this.overlayAlternateIcon}
+ {this.revealOpHover || !this._props.isSelected() ? null : this.overlayAlternateIcon}
{!this._props.isSelected() || this._renderSide === this.frontKey ? null : (
Ask GPT to create an answer for the question on the front
}>
this.askGPT(GPTCallType.CHATCARD)}>
@@ -296,13 +310,11 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent
()
moveDoc = (doc: Doc, addDocument: (document: Doc | Doc[]) => boolean, which: string) => this.remDoc(doc, which) && addDocument(doc);
addDoc = (doc: Doc, which: string) => {
- if (this.dataDoc[which] && !this._isEmpty) return false;
this.dataDoc[which] = doc;
return true;
};
remDoc = (doc: Doc, which: string) => {
if (this.dataDoc[which] === doc) {
- this._isEmpty = true;
this.dataDoc[which] = undefined;
return true;
}
@@ -334,6 +346,26 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
moveDocBack = (docs: Doc | Doc[], targetCol: Doc | undefined, addDoc: (doc: Doc | Doc[]) => boolean) => toList(docs).reduce((res, doc: Doc) => res && this.moveDoc(doc, addDoc, this.backKey), true);
remDocFront = (docs: Doc | Doc[]) => toList(docs).reduce((res, doc) => res && this.remDoc(doc, this.frontKey), true);
remDocBack = (docs: Doc | Doc[]) => toList(docs).reduce((res, doc) => res && this.remDoc(doc, this.backKey), true);
+ animateSliding = action((targetWidth: number) => {
+ this._animating = `all ${this._slideTiming}ms`; // on click, animate slider movement to the targetWidth
+ this.layoutDoc[this.clipWidthKey] = (targetWidth * 100) / this._props.PanelWidth();
+ setTimeout(action(() => {this._animating = ''; }), this._slideTiming); // prettier-ignore
+ });
+
+ _flipAnim: NodeJS.Timeout | undefined;
+ animateFlipping = action((side?: string) => {
+ if (side !== this._renderSide) {
+ this._renderSide = side ?? (this._renderSide === this.frontKey ? this.backKey : this.frontKey); // switches to new front
+ this._animating = '0'; // reveals old front on the bottom layer by making top layer transparent
+ setTimeout(
+ action(() => {
+ this._animating = `all ${this._slideTiming * 5}ms`; // makes new front fade in
+ clearTimeout(this._flipAnim);
+ this._flipAnim = setTimeout( action(() => { this._animating = ''; }), this._slideTiming * 5 ); // prettier-ignore
+ })
+ );
+ }
+ });
registerSliding = (e: React.PointerEvent, targetWidth: number) => {
if (e.button !== 2) {
@@ -351,13 +383,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
}),
false,
undefined,
- action(() => {
- if (!this._childActive) {
- this._animating = `all ${this._slideTiming}ms`; // on click, animate slider movement to the targetWidth
- this.layoutDoc[this.clipWidthKey] = (targetWidth * 100) / this._props.PanelWidth();
- setTimeout( action(() => {this._animating = ''; }), this._slideTiming); // prettier-ignore
- }
- })
+ action(() => !this._childActive && this.animateSliding(targetWidth))
);
}
};
@@ -584,16 +610,6 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
}
};
- @action
- flipFlashcard = () => {
- this._renderSide = this._renderSide === this.frontKey ? this.backKey : this.frontKey;
- };
-
- @action
- hoverFlip = (side: string) => {
- if (this.revealOp === flashcardRevealOp.HOVER) this._renderSide = side;
- };
-
flashcardContextMenu = () => {
const appearance = ContextMenu.Instance.findByDescription('Appearance...');
const appearanceItems = appearance?.subitems ?? [];
@@ -680,16 +696,8 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
};
displayBox = (which: string, cover: number) => (
- {
- this.registerSliding(e, cover);
- this.isFlashcard && this.activateContent();
- }}
- ref={ele => this.createDropTarget(ele, which)}>
- {!this._isEmpty ? this.displayDoc(which) : null}
+
this.registerSliding(e, cover)} ref={ele => this.createDropTarget(ele, which)}>
+ {this.displayDoc(which)}
);
@@ -727,7 +735,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent
()
Evaluate Pronunciation
-
+ this.animateFlipping(this.frontKey) : this.handleRenderGPTClick}>
{this._renderSide === this.backKey ? 'Redo the Question' : 'Submit'}
@@ -736,40 +744,33 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
);
// if flashcard is rendered that has no data, then add some placeholders for question and answer
- addPlaceholdersForEmptyFlashcard = () => {
- if (this.dataDoc.data) {
- if (!this.dataDoc[this.backKey] || !this.dataDoc[this.frontKey]) ComparisonBox.createFlashcard(StrCast(this.dataDoc.data), this.frontKey, this.backKey, this.Document);
- } else {
- // add text box to each side when comparison box is first created
- if (!this.dataDoc[this.backKey] && !this._isEmpty) {
- this.dataDoc[this.backKey] = FormattedTextBox.centeredTextCreator('answer', 'answer here', undefined, true);
- }
-
- if (!this.dataDoc[this.frontKey] && !this._isEmpty) {
- this.dataDoc[this.frontKey] = FormattedTextBox.centeredTextCreator('question', 'hint: Enter a topic, select this document and click the stack button to have GPT create a deck of cards', undefined, true);
- }
- }
- };
-
- renderAsFlashcard = () => (
+ // addPlaceholdersForEmptyFlashcard = () => {
+ // if (this.dataDoc.data) {
+ // if (!this.dataDoc[this.backKey] || !this.dataDoc[this.frontKey]) ComparisonBox.createFlashcard(StrCast(this.dataDoc.data), this.frontKey, this.backKey, this.Document);
+ // }
+ // };
+
+ // render a button that flips between front and back
+ renderAsFlip = () => (
this.hoverFlip(this.backKey)}
- onMouseLeave={() => this.hoverFlip(this.frontKey)}>
- {this.displayBox(this._renderSide, this._props.PanelWidth() - 3)}
- {this.loading ? (
-
-
-
- ) : null}
+ style={{ display: 'flex', pointerEvents: this.revealOpHover && this._props.isContentActive() ? 'unset' : undefined }} //
+ onMouseEnter={() => this.revealOpHover && this.animateFlipping(this.backKey)}
+ onMouseLeave={() => this.revealOpHover && this.animateFlipping(this.frontKey)}>
+
+ {this.displayBox(this._renderSide === this.backKey ? this.frontKey : this.backKey, 0)}
+
+
{this.displayBox(this._renderSide, 0)}
{this.flashcardMenu}
);
- // render a comparison box that compares items side by side
+ // render a slider that reveals front and back as slider is dragged horizonally
renderAsBeforeAfter = () => (
-
+
this.revealOpHover && this.animateSliding(0)}
+ onMouseLeave={() => this.revealOpHover && this.animateSliding(this._props.PanelWidth() - 3)}>
{this.displayBox(this.backKey, this._props.PanelWidth() - 3)}
{this.displayBox(this.frontKey, 0)}
@@ -789,11 +790,20 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent
()
);
render() {
- this.isFlashcard && this.addPlaceholdersForEmptyFlashcard();
- return this.isFlashcard ?
- this.isQuizMode ? this.renderAsQuiz(this.frontText) :
- this.renderAsFlashcard() :
- this.renderAsBeforeAfter(); // prettier-ignore
+ const renderMode = new Map JSX.Element>([
+ [flashcardRevealOp.FLIP, this.renderAsFlip],
+ [flashcardRevealOp.SLIDE, this.renderAsBeforeAfter]]); // prettier-ignore
+ if (this.isQuizMode) this.renderAsQuiz(this.frontText);
+ return (
+
+ {renderMode.get(this.revealOp)?.() ?? null}
+ {this.loading ? (
+
+
+
+ ) : null}
+
+ );
}
}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 9d3a899f5..29be8d285 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -76,26 +76,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent
Date: Thu, 17 Oct 2024 17:19:11 -0400
Subject: fixed quizMode to actually render and to get pointer events.
---
src/client/views/collections/CollectionCardDeckView.tsx | 14 +++++++++++++-
src/client/views/nodes/ComparisonBox.scss | 3 +++
src/client/views/nodes/ComparisonBox.tsx | 5 +++--
3 files changed, 19 insertions(+), 3 deletions(-)
(limited to 'src/client/views/nodes/ComparisonBox.scss')
diff --git a/src/client/views/collections/CollectionCardDeckView.tsx b/src/client/views/collections/CollectionCardDeckView.tsx
index 5faabacf4..b86dad9d7 100644
--- a/src/client/views/collections/CollectionCardDeckView.tsx
+++ b/src/client/views/collections/CollectionCardDeckView.tsx
@@ -4,7 +4,7 @@ import { computedFn } from 'mobx-utils';
import * as React from 'react';
import { ClientUtils, DashColor, imageUrlToBase64, returnFalse, returnNever, returnZero } from '../../../ClientUtils';
import { emptyFunction } from '../../../Utils';
-import { Doc } from '../../../fields/Doc';
+import { Doc, DocListCast, Opt } from '../../../fields/Doc';
import { Animation, DocData } from '../../../fields/DocSymbols';
import { Id } from '../../../fields/FieldSymbols';
import { List } from '../../../fields/List';
@@ -24,6 +24,7 @@ import { DocumentView, DocumentViewProps } from '../nodes/DocumentView';
import { GPTPopup, GPTPopupMode } from '../pdf/GPTPopup/GPTPopup';
import './CollectionCardDeckView.scss';
import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
+import { FocusViewOptions } from '../nodes/FocusViewOptions';
enum cardSortings {
Time = 'time',
@@ -342,6 +343,7 @@ export class CollectionCardView extends CollectionSubView() {
fitWidth={returnFalse}
waitForDoubleClickToClick={returnNever}
scriptContext={this}
+ focus={this.focus}
onDoubleClickScript={this.onChildDoubleClick}
onClickScript={this._curDoc === doc ? undefined : this._clickScript}
dontCenter="y" // Don't center it vertically, because the grid it's in is already doing that and we don't want to do it twice.
@@ -593,6 +595,16 @@ export class CollectionCardView extends CollectionSubView() {
}
});
+ focus = action((anchor: Doc, options: FocusViewOptions): Opt => {
+ const docs = DocListCast(this.Document[this.fieldKey ?? Doc.LayoutFieldKey(this.Document)]);
+ if (anchor.type !== DocumentType.CONFIG && !docs.includes(anchor)) return undefined;
+ options.didMove = true;
+ const target = DocCast(anchor.annotationOn) ?? anchor;
+ const index = docs.indexOf(target);
+ index !== -1 && (this._curDoc = target);
+ return undefined;
+ });
+
/**
* Actually renders all the cards
*/
diff --git a/src/client/views/nodes/ComparisonBox.scss b/src/client/views/nodes/ComparisonBox.scss
index d1cc48051..d2ba9796b 100644
--- a/src/client/views/nodes/ComparisonBox.scss
+++ b/src/client/views/nodes/ComparisonBox.scss
@@ -236,6 +236,9 @@
}
}
}
+.comparisonBox-interactive {
+ pointer-events: all;
+}
.comparisonBox-explain {
position: absolute;
diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx
index 3c126ea4a..f6c33d6ba 100644
--- a/src/client/views/nodes/ComparisonBox.tsx
+++ b/src/client/views/nodes/ComparisonBox.tsx
@@ -792,8 +792,9 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
const renderMode = new Map JSX.Element>([
[flashcardRevealOp.FLIP, this.renderAsFlip],
[flashcardRevealOp.SLIDE, this.renderAsBeforeAfter]]); // prettier-ignore
- if (this.isQuizMode) this.renderAsQuiz(this.frontText);
- return (
+ return this.isQuizMode ? (
+ this.renderAsQuiz(this.frontText)
+ ) : (
{renderMode.get(this.revealOp)?.() ?? null}
{this.loading ? (
--
cgit v1.2.3-70-g09d2