diff options
author | bobzel <zzzman@gmail.com> | 2025-01-28 10:53:58 -0500 |
---|---|---|
committer | bobzel <zzzman@gmail.com> | 2025-01-28 10:53:58 -0500 |
commit | e7b0b92adf3404bc24e1b9d5d6a857a0ad6e6418 (patch) | |
tree | 20f38655f9b594d457fe6af146b63ddcf130f148 /src | |
parent | 406706ab42bc1fdac81b3bffdcadfcfe05cfc8a0 (diff) |
cleaning up card deck view.
Diffstat (limited to 'src')
-rw-r--r-- | src/client/apis/gpt/GPT.ts | 26 | ||||
-rw-r--r-- | src/client/views/TagsView.tsx | 2 | ||||
-rw-r--r-- | src/client/views/collections/CollectionCardDeckView.scss | 5 | ||||
-rw-r--r-- | src/client/views/collections/CollectionCardDeckView.tsx | 60 | ||||
-rw-r--r-- | src/client/views/pdf/GPTPopup/GPTPopup.tsx | 49 |
5 files changed, 101 insertions, 41 deletions
diff --git a/src/client/apis/gpt/GPT.ts b/src/client/apis/gpt/GPT.ts index cf3a28a8e..d96972784 100644 --- a/src/client/apis/gpt/GPT.ts +++ b/src/client/apis/gpt/GPT.ts @@ -16,10 +16,10 @@ enum GPTCallType { PRONUNCIATION = 'pronunciation', DRAW = 'draw', COLOR = 'color', - RUBRIC = 'rubric', - TYPE = 'type', - SUBSET = 'subset', - INFO = 'info', + RUBRIC = 'rubric', // needs to be filled in below + TYPE = 'type', // needs to be filled in below + SUBSET = 'subset', // needs to be filled in below + INFO = 'info', // needs to be filled in below TEMPLATE = 'template', VIZSUM = 'vizsum', VIZSUM2 = 'vizsum2', @@ -84,6 +84,18 @@ const callTypeMap: { [type: string]: GPTCallOpts } = { temp: 0.1, //0.3 prompt: "BRIEFLY (<50 words) describe any differences between the rubric and the user's answer answer in second person. If there are no differences, say correct", }, + type: { + model: 'gpt-4-turbo', + maxTokens: 512, + temp: 0.5, + prompt: '', + }, + info: { + model: 'gpt-4-turbo', + maxTokens: 512, + temp: 0.5, + prompt: '', + }, template: { model: 'gpt-4-turbo', maxTokens: 512, @@ -131,8 +143,12 @@ let lastResp = ''; * @returns AI Output */ const gptAPICall = async (inputTextIn: string, callType: GPTCallType, prompt?: string, dontCache?: boolean) => { - const inputText = [GPTCallType.SUMMARY, GPTCallType.FLASHCARD, GPTCallType.QUIZ, GPTCallType.STACK].includes(callType) ? inputTextIn + '.' : inputTextIn; + const inputText = inputTextIn + ([GPTCallType.SUMMARY, GPTCallType.FLASHCARD, GPTCallType.QUIZ, GPTCallType.STACK].includes(callType) ? '.' : ''); const opts = callTypeMap[callType]; + if (!opts) { + console.log('The query type:' + callType + ' requires a configuration.'); + return 'Error connecting with API.'; + } if (lastCall === inputText && dontCache !== true) return lastResp; try { lastCall = inputText; diff --git a/src/client/views/TagsView.tsx b/src/client/views/TagsView.tsx index 73f404596..9e936ea54 100644 --- a/src/client/views/TagsView.tsx +++ b/src/client/views/TagsView.tsx @@ -9,7 +9,7 @@ import { emptyFunction } from '../../Utils'; import { Doc, DocListCast, Field, Opt, StrListCast } from '../../fields/Doc'; import { DocData } from '../../fields/DocSymbols'; import { List } from '../../fields/List'; -import { DocCast, NumCast, StrCast } from '../../fields/Types'; +import { DocCast, StrCast } from '../../fields/Types'; import { DocumentType } from '../documents/DocumentTypes'; import { DragManager } from '../util/DragManager'; import { SnappingManager } from '../util/SnappingManager'; diff --git a/src/client/views/collections/CollectionCardDeckView.scss b/src/client/views/collections/CollectionCardDeckView.scss index 5283601bf..79c53db08 100644 --- a/src/client/views/collections/CollectionCardDeckView.scss +++ b/src/client/views/collections/CollectionCardDeckView.scss @@ -28,11 +28,14 @@ .collectionCardView-cardwrapper { display: grid; - grid-template-columns: repeat(10, 1fr); transform-origin: left 50%; align-items: center; z-index: 0; // so that setting z-index of active card doesn't make it land on top of things outside of the card-wrapper } +.collectionCardView-cardSizeDragger { + position: absolute; + top: 0; +} .no-card-span { position: relative; diff --git a/src/client/views/collections/CollectionCardDeckView.tsx b/src/client/views/collections/CollectionCardDeckView.tsx index 836a5a2c3..800eb4914 100644 --- a/src/client/views/collections/CollectionCardDeckView.tsx +++ b/src/client/views/collections/CollectionCardDeckView.tsx @@ -1,8 +1,9 @@ -import { IReactionDisposer, ObservableMap, action, computed, makeObservable, observable, reaction, trace } from 'mobx'; +import { IReactionDisposer, ObservableMap, action, computed, makeObservable, observable, reaction, runInAction, trace } from 'mobx'; import { observer } from 'mobx-react'; import { computedFn } from 'mobx-utils'; import * as React from 'react'; -import { ClientUtils, DashColor, imageUrlToBase64, returnFalse, returnNever, returnZero } from '../../../ClientUtils'; +import * as CSS from 'csstype'; +import { ClientUtils, DashColor, imageUrlToBase64, returnFalse, returnNever, returnZero, setupMoveUpEvents } from '../../../ClientUtils'; import { emptyFunction } from '../../../Utils'; import { Doc, DocListCast, Opt } from '../../../fields/Doc'; import { Animation, DocData } from '../../../fields/DocSymbols'; @@ -18,7 +19,7 @@ import { DragManager } from '../../util/DragManager'; import { dropActionType } from '../../util/DropActionTypes'; import { SnappingManager } from '../../util/SnappingManager'; import { Transform } from '../../util/Transform'; -import { undoable } from '../../util/UndoManager'; +import { undoable, UndoManager } from '../../util/UndoManager'; import { PinDocView, PinProps } from '../PinFuncs'; import { StyleProp } from '../StyleProp'; import { TagItem } from '../TagsView'; @@ -27,6 +28,8 @@ import { FocusViewOptions } from '../nodes/FocusViewOptions'; import { GPTPopup, GPTPopupMode } from '../pdf/GPTPopup/GPTPopup'; import './CollectionCardDeckView.scss'; import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { SettingsManager } from '../../util/SettingsManager'; enum cardSortings { Time = 'time', @@ -52,12 +55,13 @@ export class CollectionCardView extends CollectionSubView() { private _oldWheel: HTMLElement | null = null; private _dropped = false; // set when a card doc has just moved and the drop method has been called - prevents the pointerUp method from hiding doc decorations (which needs to be done when clicking on a card to animate it to front/center) private _setCurDocScript = () => ScriptField.MakeScript('scriptContext.layoutDoc._card_curDoc=this', { scriptContext: 'any' })!; + private _draggerRef = React.createRef<HTMLDivElement>(); @observable _forceChildXf = 0; @observable _hoveredNodeIndex = -1; @observable _docRefs = new ObservableMap<Doc, DocumentView>(); - @observable _maxRowCount = 10; @observable _docDraggedIndex: number = -1; + @observable _cursor: CSS.Property.Cursor = 'ew-resize'; constructor(props: SubCollectionViewProps) { super(props); @@ -74,6 +78,12 @@ export class CollectionCardView extends CollectionSubView() { // prevent wheel events from passively propagating up through containers and prevents containers from preventDefault which would block scrolling ele?.addEventListener('wheel', this.onPassiveWheel, { passive: false }); }; + @computed get cardWidth() { + return NumCast(this.layoutDoc._cardWidth, 50); + } + @computed get _maxRowCount() { + return Math.ceil(this.cardDeckWidth / this.cardWidth); + } /** * Callback to ensure gpt's text versions of the child docs are updated */ @@ -167,6 +177,14 @@ export class CollectionCardView extends CollectionSubView() { return this._props.NativeDimScaling?.() || 1; } + @computed get xMargin() { + return NumCast(this.layoutDoc._xMargin, Math.max(3, 0.05 * this._props.PanelWidth())); + } + + @computed get cardDeckWidth() { + return this._props.PanelWidth() - 2 * this.xMargin; + } + /** * When in quiz mode, randomly selects a document */ @@ -505,7 +523,6 @@ export class CollectionCardView extends CollectionSubView() { const normalizedItem = item.trim(); // find the corresponding Doc in the textToDoc map const doc = this._textToDoc.get(normalizedItem); - if (doc) { switch (questionType) { case '6': @@ -513,9 +530,7 @@ export class CollectionCardView extends CollectionSubView() { break; case '1': { const allHotKeys = Doc.MyFilterHotKeys; - let myTag = ''; - if (tag) { for (let i = 0; i < allHotKeys.length; i++) { // bcz: CHECK THIS CODE OUT -- SOMETHING CHANGED @@ -526,7 +541,7 @@ export class CollectionCardView extends CollectionSubView() { } } - if (myTag != '') { + if (myTag) { doc[myTag] = true; } } @@ -591,6 +606,26 @@ export class CollectionCardView extends CollectionSubView() { } }); + cardSizerDown = (e: React.PointerEvent) => { + runInAction(() => { + this._cursor = 'grabbing'; + }); + const batch = UndoManager.StartBatch('card view size'); + setupMoveUpEvents( + this, + e, + (e: PointerEvent, down: number[], delta: number[]) => { + this.layoutDoc._cardWidth = Math.max(10, delta[0] < 0 ? Math.floor(this.cardWidth + delta[0]) : Math.ceil(this.cardWidth + delta[0])); + return false; + }, + action(() => { + this._cursor = 'ew-resize'; + batch.end(); + }), + emptyFunction + ); + }; + /** * turns off the _dropped flag at the end of a drag/drop, or releases the focused Doc if a different Doc is clicked */ @@ -701,6 +736,7 @@ export class CollectionCardView extends CollectionSubView() { <div className="collectionCardView-cardwrapper" style={{ + gridTemplateColumns: `repeat(${this._maxRowCount}, 1fr)`, gridAutoRows: `${100 / this.numRows}%`, height: `${this.cardSpacing}%`, }}> @@ -717,6 +753,14 @@ export class CollectionCardView extends CollectionSubView() { {this.flashCardUI(this.curDoc, this.docViewProps, this.answered)} </div> </div> + + <div + className="collectionCardView-cardSizeDragger" + onPointerDown={this.cardSizerDown} + ref={this._draggerRef} + style={{ display: this._props.isContentActive() ? undefined : 'none', cursor: this._cursor, color: SettingsManager.userColor, left: `${this.cardWidth + this.xMargin}px` }}> + <FontAwesomeIcon icon="arrows-alt-h" /> + </div> </div> ); } diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.tsx b/src/client/views/pdf/GPTPopup/GPTPopup.tsx index 22ca9dcd7..4028ef479 100644 --- a/src/client/views/pdf/GPTPopup/GPTPopup.tsx +++ b/src/client/views/pdf/GPTPopup/GPTPopup.tsx @@ -1,6 +1,6 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Button, IconButton, Type } from '@dash/components'; -import { action, makeObservable, observable } from 'mobx'; +import { action, makeObservable, observable, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { CgClose, CgCornerUpLeft } from 'react-icons/cg'; @@ -37,10 +37,8 @@ export enum GPTQuizType { MULTIPLE = 2, } -interface GPTPopupProps {} - @observer -export class GPTPopup extends ObservableReactComponent<GPTPopupProps> { +export class GPTPopup extends ObservableReactComponent<object> { // eslint-disable-next-line no-use-before-define static Instance: GPTPopup; private messagesEndRef: React.RefObject<HTMLDivElement>; @@ -188,16 +186,9 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> { this.setLoading(true); this.setSortDone(false); - const quizType = this.quizMode; - const selected = DocumentView.SelectedDocs().lastElement(); const questionText = 'Question: ' + StrCast(selected['gptInputText']); - - if (StrCast(selected['gptRubric']) === '') { - const rubricText = 'Rubric: ' + (await this.generateRubric(StrCast(selected['gptInputText']), selected)); - } - const rubricText = 'Rubric: ' + StrCast(selected['gptRubric']); const queryText = questionText + ' UserAnswer: ' + this.quizAnswer + '. ' + 'Rubric' + rubricText; @@ -214,7 +205,7 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> { this.setLoading(false); this.setSortDone(true); } catch (err) { - console.error('GPT call failed'); + console.error('GPT call failed', err); } if (this.onQuizRandom) { @@ -234,7 +225,7 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> { doc['gptRubric'] = res; return res; } catch (err) { - console.error('GPT call failed'); + console.error('GPT call failed', err); } }; @@ -262,7 +253,6 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> { * Generates a response to the user's question depending on the type of their question */ generateCard = async () => { - console.log(this.chatSortPrompt + 'USER PROMPT'); this.setLoading(true); this.setSortDone(false); @@ -271,10 +261,8 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> { } try { - // const res = await gptAPICall(this.sortDesc, GPTCallType.SORT, this.chatSortPrompt); const questionType = await gptAPICall(this.chatSortPrompt, GPTCallType.TYPE); const questionNumber = questionType.split(' ')[0]; - console.log(questionType); let res = ''; switch (questionNumber) { @@ -287,10 +275,12 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> { res = await gptAPICall(this.sortDesc, GPTCallType.SORT, this.chatSortPrompt); break; default: - const selected = DocumentView.SelectedDocs().lastElement(); - const questionText = StrCast(selected!['gptInputText']); + { + const selected = DocumentView.SelectedDocs().lastElement(); + const questionText = StrCast(selected?.gptInputText); - res = await gptAPICall(questionText, GPTCallType.INFO, this.chatSortPrompt); + res = await gptAPICall(questionText, GPTCallType.INFO, this.chatSortPrompt); + } break; } @@ -308,7 +298,7 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> { // Set the extracted explanation to sortRespText this.setSortRespText(explanation); - this.conversationArray.push(this.sortRespText); + runInAction(() => this.conversationArray.push(this.sortRespText)); this.scrollToBottom(); console.log(res); @@ -448,7 +438,7 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> { private getPreviewUrl = (source: string) => source.split('.').join('_m.'); - constructor(props: GPTPopupProps) { + constructor(props: object) { super(props); makeObservable(this); GPTPopup.Instance = this; @@ -515,18 +505,25 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> { </div> ); - handleKeyPress = async (e: React.KeyboardEvent, isSort: boolean) => { + @action + handleKeyPress = (e: React.KeyboardEvent, isSort: boolean) => { if (e.key === 'Enter') { e.stopPropagation(); if (isSort) { this.conversationArray.push(this.chatSortPrompt); - await this.generateCard(); - this.chatSortPrompt = ''; + this.generateCard().then( + action(() => { + this.chatSortPrompt = ''; + }) + ); } else { this.conversationArray.push(this.quizAnswer); - await this.generateQuiz(); - this.quizAnswer = ''; + this.generateQuiz().then( + action(() => { + this.quizAnswer = ''; + }) + ); } this.scrollToBottom(); |