aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/client/views/nodes/ComparisonBox.tsx98
-rw-r--r--src/fields/RichTextField.ts23
2 files changed, 68 insertions, 53 deletions
diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx
index 111fabca3..672008968 100644
--- a/src/client/views/nodes/ComparisonBox.tsx
+++ b/src/client/views/nodes/ComparisonBox.tsx
@@ -34,6 +34,22 @@ import { FormattedTextBox } from './formattedText/FormattedTextBox';
const API_URL = 'https://api.unsplash.com/search/photos';
+/**
+ * This view serves two 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)
+ *
+ * In either case, the two docs are stored in the <fieldKey>_front and <fieldKey>_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.
+ *
+ */
+
@observer
export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
public static LayoutString(fieldKey: string) {
@@ -129,7 +145,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
)}
{DocCast(this.Document.embedContainer)?.type_collection !== CollectionViewType.Freeform || this._renderSide === this.backKey ? null : (
<Tooltip title={<div className="dash-tooltip">Create new flashcard stack based on text</div>}>
- <div className="comparisonBox-button" onClick={this.gptFlashcardPile}>
+ <div className="comparisonBox-button" onClick={() => this.askGPT(GPTCallType.STACK).then(this.createFlashcardDeck)}>
<FontAwesomeIcon icon="layer-group" size="xl" />
</div>
</Tooltip>
@@ -404,50 +420,12 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
return response.data.transcription;
};
- 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,
- _xMargin: 5,
- _yMargin: 5,
- x: NumCast(this.layoutDoc.x),
- y: NumCast(this.layoutDoc.y) + 50,
- });
-
- if (gpt) {
- this._props.DocumentView?.()._props.addDocument?.(newCol);
- this._props.removeDocument?.(this.Document);
- } else {
- this._props.addDocument?.(newCol);
- this._props.removeDocument?.(this.Document);
- Doc.SetContainer(this.Document, newCol);
- }
- }
-
- textToRtf = (text: string, img?: Doc) =>
- new RichTextField(
- JSON.stringify({
- // this is a RichText json that has the question text placed above a related image
- doc: {
- type: 'doc',
- content: [
- {
- type: 'paragraph',
- attrs: { align: 'center', color: null, id: null, indent: null, inset: null, lineSpacing: null, paddingBottom: null, paddingTop: null },
- content: [
- ...(text ? [{ type: 'text', text }] : []), //
- ...(img ? [{ type: 'dashDoc', attrs: { width: '200px', height: '200px', title: 'dashDoc', float: 'unset', hidden: false, docId: img[Id] } }] : []),
- ],
- },
- ],
- },
- selection: { type: 'text', anchor: 2, head: 2 },
- }),
- text
- );
-
+ /**
+ * Creates a flashcard (or fills in flashcard data to a specified Doc) from a control string containing a question and answer
+ * @param tuple string containing Question:, Answer: and optionally a Keyword:
+ * @param useDoc doc to fill in instead of creating a Doc
+ * @returns the resulting flashcard Doc
+ */
createFlashcard = (tuple: string, useDoc?: Doc) => {
const [ktoken, atoken] = [ComparisonBox.ktoken, ComparisonBox.atoken];
const newDoc = useDoc ?? Docs.Create.ComparisonDocument('', { _layout_isFlashcard: true, _width: 300, _height: 300 });
@@ -465,22 +443,36 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
newDoc[DocData][this.backKey] = this.textCreator('answer', answer);
return newDoc;
};
- return keyword && keyword !== 'none' ? this.fetchImages(keyword).then(img => fillInFlashcard(img)) : fillInFlashcard();
+ return keyword && keyword.toLowerCase() !== 'none' ? this.fetchImages(keyword).then(img => fillInFlashcard(img)) : fillInFlashcard();
};
+ /**
+ * Create a carousel of flashcards from a GPT response string where questions and answers are given in a format loosely defined by:
+ * Question: ... Answer: ... Keyword: ...
+ * Note that Keyword or Answer may not be present, or their orders may be reversed.
+ */
createFlashcardDeck = (text: string) => {
Promise.all(
text
.split(ComparisonBox.qtoken)
.filter(t => t)
.map(tuple => this.createFlashcard(tuple))
- ).then(docs => this.createFlashcardPile(docs, true));
- };
+ ).then(docs => {
+ const newCol = Docs.Create.CarouselDocument(docs, {
+ _width: NumCast(this.layoutDoc._width, 250) + 50,
+ _height: NumCast(this.layoutDoc._height, 200) + 50,
+ _layout_fitWidth: false,
+ _layout_autoHeight: true,
+ _xMargin: 5,
+ _yMargin: 5,
+ x: NumCast(this.layoutDoc.x),
+ y: NumCast(this.layoutDoc.y),
+ });
- /**
- * queries GPT about a topic and then creates a flashcard deck from the results.
- */
- gptFlashcardPile = () => this.askGPT(GPTCallType.STACK).then(this.createFlashcardDeck);
+ this._props.DocumentView?.()._props.addDocument?.(newCol);
+ this._props.removeDocument?.(this.Document);
+ });
+ };
/**
* Calls GPT for each flashcard type.
@@ -658,7 +650,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
return layoutTemplateString;
};
textCreator = (title: string, text: string, img?: Doc) => {
- const newDoc = Docs.Create.TextDocument(this.textToRtf(text, img), {
+ const newDoc = Docs.Create.TextDocument(RichTextField.textToRtf(text, img?.[Id]), {
title, //
_layout_autoHeight: true,
_layout_centered: true,
diff --git a/src/fields/RichTextField.ts b/src/fields/RichTextField.ts
index 613bb0fd1..dc636031a 100644
--- a/src/fields/RichTextField.ts
+++ b/src/fields/RichTextField.ts
@@ -48,4 +48,27 @@ export class RichTextField extends ObjectField {
''
);
}
+
+ public static textToRtf(text: string, imgDocId?: string) {
+ return new RichTextField(
+ JSON.stringify({
+ // this is a RichText json that has the question text placed above a related image
+ doc: {
+ type: 'doc',
+ content: [
+ {
+ type: 'paragraph',
+ attrs: { align: 'center', color: null, id: null, indent: null, inset: null, lineSpacing: null, paddingBottom: null, paddingTop: null },
+ content: [
+ ...(text ? [{ type: 'text', text }] : []), //
+ ...(imgDocId ? [{ type: 'dashDoc', attrs: { width: '200px', height: '200px', title: 'dashDoc', float: 'unset', hidden: false, docId: imgDocId } }] : []),
+ ],
+ },
+ ],
+ },
+ selection: { type: 'text', anchor: 2, head: 2 },
+ }),
+ text
+ );
+ }
}