diff options
Diffstat (limited to 'src/client/views/collections')
| -rw-r--r-- | src/client/views/collections/CollectionCardDeckView.tsx | 157 | ||||
| -rw-r--r-- | src/client/views/collections/CollectionSubView.tsx | 1 | ||||
| -rw-r--r-- | src/client/views/collections/CollectionView.tsx | 4 |
3 files changed, 20 insertions, 142 deletions
diff --git a/src/client/views/collections/CollectionCardDeckView.tsx b/src/client/views/collections/CollectionCardDeckView.tsx index e00aa65d7..d7f4251f3 100644 --- a/src/client/views/collections/CollectionCardDeckView.tsx +++ b/src/client/views/collections/CollectionCardDeckView.tsx @@ -1,22 +1,22 @@ -import { IReactionDisposer, ObservableMap, action, computed, makeObservable, observable, reaction, runInAction } from 'mobx'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import * as CSS from 'csstype'; +import { action, computed, IReactionDisposer, makeObservable, observable, ObservableMap, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import { computedFn } from 'mobx-utils'; import * as React from 'react'; -import * as CSS from 'csstype'; -import { ClientUtils, imageUrlToBase64, returnFalse, returnNever, returnZero, setupMoveUpEvents } from '../../../ClientUtils'; +import { ClientUtils, returnFalse, returnNever, returnZero, setupMoveUpEvents } from '../../../ClientUtils'; import { emptyFunction } from '../../../Utils'; import { Doc, DocListCast, Opt } from '../../../fields/Doc'; -import { Animation, DocData } from '../../../fields/DocSymbols'; +import { Animation } from '../../../fields/DocSymbols'; import { Id } from '../../../fields/FieldSymbols'; import { List } from '../../../fields/List'; import { ScriptField } from '../../../fields/ScriptField'; -import { BoolCast, DocCast, NumCast, RTFCast, ScriptCast, StrCast } from '../../../fields/Types'; -import { URLField } from '../../../fields/URLField'; -import { gptImageLabel, GPTTypeStyle } from '../../apis/gpt/GPT'; +import { BoolCast, DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types'; import { DocumentType } from '../../documents/DocumentTypes'; import { Docs } from '../../documents/Documents'; import { DragManager } from '../../util/DragManager'; import { dropActionType } from '../../util/DropActionTypes'; +import { SettingsManager } from '../../util/SettingsManager'; import { SnappingManager } from '../../util/SnappingManager'; import { Transform } from '../../util/Transform'; import { undoable, UndoManager } from '../../util/UndoManager'; @@ -25,11 +25,8 @@ import { StyleProp } from '../StyleProp'; import { TagItem } from '../TagsView'; import { DocumentView, DocumentViewProps } from '../nodes/DocumentView'; import { FocusViewOptions } from '../nodes/FocusViewOptions'; -import { GPTPopup } from '../pdf/GPTPopup/GPTPopup'; import './CollectionCardDeckView.scss'; -import { CollectionSubView, docSortings, SubCollectionViewProps } from './CollectionSubView'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { SettingsManager } from '../../util/SettingsManager'; +import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView'; /** * New view type specifically for studying more dynamically. Allows you to reorder docs however you see fit, easily @@ -42,7 +39,6 @@ import { SettingsManager } from '../../util/SettingsManager'; export class CollectionCardView extends CollectionSubView() { private _dropDisposer?: DragManager.DragDropDisposer; private _disposers: { [key: string]: IReactionDisposer } = {}; - private _textToDoc = new Map<string, Doc>(); 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' })!; @@ -74,22 +70,7 @@ export class CollectionCardView extends CollectionSubView() { return Math.ceil(this.cardDeckWidth / this.cardWidth); } - /** - * update's gpt's doc-text list and initialize callbacks to respond to GPT output - */ - updateDocumentDescriptions = () => - this.childPairStringList().then(docDescriptions => { - GPTPopup.Instance.setDocumentDescriptions(docDescriptions.join()); - GPTPopup.Instance.onGptResponse = this.processGptResponse; - GPTPopup.Instance.onQuizRandom = this.randomlyChooseDoc; - }); - componentDidMount() { - this._disposers.chatVis = reaction( - () => GPTPopup.Instance.Visible, - vis => !vis && this.onGptHide() - ); - GPTPopup.Instance.setRetrieveDocDescriptionsCallback(this.Document, this.updateDocumentDescriptions); this._props.setContentViewBox?.(this); // if card deck moves, then the child doc views are hidden so their screen to local transforms will return empty rectangles // when inquired from the dom (below in childScreenToLocal). When the doc is actually rendered, we need to act like the @@ -110,10 +91,7 @@ export class CollectionCardView extends CollectionSubView() { ); } - onGptHide = () => Doc.setDocFilter(this.Document, 'tags', '#chat', 'remove'); componentWillUnmount() { - GPTPopup.Instance.onGptResponse = undefined; - GPTPopup.Instance.onQuizRandom = undefined; Object.keys(this._disposers).forEach(key => this._disposers[key]?.()); this._dropDisposer?.(); } @@ -128,7 +106,7 @@ export class CollectionCardView extends CollectionSubView() { * Circle arc size, in radians, to layout cards */ @computed get archAngle() { - return NumCast(this.layoutDoc.card_arch, 90) * (Math.PI / 180) * (this.childCards.length < this._maxRowCount ? this.childCards.length / this._maxRowCount : 1); + return NumCast(this.layoutDoc.card_arch, 90) * (Math.PI / 180) * (this.childDocsNoInk.length < this._maxRowCount ? this.childDocsNoInk.length / this._maxRowCount : 1); } /** * Spacing card rows as a percent of Doc size. 100 means rows spread out to fill 100% of the Doc vertically. Default is 60% @@ -140,7 +118,7 @@ export class CollectionCardView extends CollectionSubView() { /** * The child documents to be rendered-- everything other than ink/link docs (which are marks as being svg's) */ - @computed get childCards() { + @computed get childDocsNoInk() { return this.childLayoutPairs.filter(pair => !pair.layout.layout_isSvg); } @@ -148,7 +126,7 @@ export class CollectionCardView extends CollectionSubView() { * how much to scale down the contents of the view so that everything will fit */ @computed get fitContentScale() { - const length = Math.min(this.childCards.length, this._maxRowCount); + const length = Math.min(this.childDocsNoInk.length, this._maxRowCount); return (this.childPanelWidth() * length) / this._props.PanelWidth(); } @@ -164,17 +142,12 @@ export class CollectionCardView extends CollectionSubView() { return this._props.PanelWidth() - 2 * this.xMargin; } - /** - * When in quiz mode, randomly selects a document - */ - randomlyChooseDoc = () => (this.layoutDoc._card_curDoc = this.childDocs[Math.floor(Math.random() * this.childDocs.length)]); - setHoveredNodeIndex = action((index: number) => { if (!SnappingManager.IsDragging) this._hoveredNodeIndex = index; }); isSelected = (doc: Doc) => this._docRefs.get(doc)?.IsSelected; - childPanelWidth = () => NumCast(this.layoutDoc.childPanelWidth, Math.max(150, this._props.PanelWidth() / (this.childCards.length > this._maxRowCount ? this._maxRowCount : this.childCards.length) / this.nativeScaling)); + childPanelWidth = () => NumCast(this.layoutDoc.childPanelWidth, Math.max(150, this._props.PanelWidth() / (this.childDocsNoInk.length > this._maxRowCount ? this._maxRowCount : this.childDocsNoInk.length) / this.nativeScaling)); childPanelHeight = () => this._props.PanelHeight() * this.fitContentScale; onChildDoubleClick = () => ScriptCast(this.layoutDoc.onChildDoubleClick); isContentActive = () => this._props.isSelected() || this._props.isContentActive() || this.isAnyChildContentActive(); @@ -318,10 +291,10 @@ export class CollectionCardView extends CollectionSubView() { * @returns number of cards in row that contains index */ cardsInRowThatIncludesCardIndex = (index: number) => { - if (this.childCards.length < this._maxRowCount) { - return this.childCards.length; + if (this.childDocsNoInk.length < this._maxRowCount) { + return this.childDocsNoInk.length; } - const totalCards = this.childCards.length; + const totalCards = this.childDocsNoInk.length; if (index < totalCards - (totalCards % this._maxRowCount)) { return this._maxRowCount; } @@ -385,102 +358,6 @@ export class CollectionCardView extends CollectionSubView() { : this.translateY(index); }; - /** - * A list of the text content of all the child docs. RTF documents will have just their text and pdf documents will have the first 50 words. - * Image documents are converted to bse64 and gpt generates a description for them. all other documents use their title. This string is - * inputted into the gpt prompt to sort everything together - * @returns - */ - childPairStringList = () => { - const docToText = (doc: Doc) => { - switch (doc.type) { - case DocumentType.PDF: return StrCast(doc.text).split(/\s+/).slice(0, 50).join(' '); // first 50 words of pdf text - case DocumentType.IMG: return this.getImageDesc(doc); - case DocumentType.RTF: return StrCast(RTFCast(doc.text).Text); - default: return StrCast(doc.title); - } // prettier-ignore - }; - const docTextPromises = this.childCards - .map(pair => pair.layout) - .map(async doc => { - const docText = (await docToText(doc)) ?? ''; - doc.gptInputText = docText; - this._textToDoc.set(docText.replace(/\n/g, ' ').trim(), doc); - return `======${docText.replace(/\n/g, ' ').trim()}======`; - }); - return Promise.all<string>(docTextPromises); - }; - - /** - * Calls the gpt API to generate descriptions for the images in the view - * @param image - * @returns - */ - getImageDesc = async (image: Doc) => { - if (StrCast(image.description)) return StrCast(image.description); // Return existing description - const { href } = (image.data as URLField).url; - const hrefParts = href.split('.'); - const hrefComplete = `${hrefParts[0]}_o.${hrefParts[1]}`; - try { - const hrefBase64 = await imageUrlToBase64(hrefComplete); - const response = await gptImageLabel(hrefBase64, 'Give three to five labels to describe this image.'); - image[DocData].description = response.trim(); - return response; // Return the response from gptImageLabel - } catch (error) { - console.log(error); - } - return ''; - }; - - /** - * Processes gpt's output depending on the type of question the user asked. Converts gpt's string output to - * usable code - * @param gptOutput - * @param questionType - * @param tag - */ - processGptResponse = (gptOutput: string, questionType: GPTTypeStyle, tag?: string) => - undoable(() => { - // Split the string into individual list items - const listItems = gptOutput.split('======').filter(item => item.trim() !== ''); - - if (questionType === GPTTypeStyle.Filter) { - this.childDocs.forEach(d => { - TagItem.removeTagFromDoc(d, '#chat'); - }); - } - - if (questionType === GPTTypeStyle.SortDocs) { - this.Document[this._props.fieldKey + '_sort'] = docSortings.Chat; - } - - listItems.forEach((item, index) => { - const normalizedItem = item.trim(); - // find the corresponding Doc in the textToDoc map - const doc = this._textToDoc.get(normalizedItem); - if (doc) { - switch (questionType) { - case GPTTypeStyle.SortDocs: - doc.chatIndex = index; - break; - case GPTTypeStyle.AssignTags: - if (tag) { - const hashTag = tag.startsWith('#') ? tag : '#' + tag[0].toLowerCase() + tag.slice(1); - const filterTag = Doc.MyFilterHotKeys.map(key => StrCast(key.toolType)).find(key => key.includes(tag)) ?? hashTag; - TagItem.addTagToDoc(doc, filterTag); - } - break; - case GPTTypeStyle.Filter: - TagItem.addTagToDoc(doc, '#chat'); - Doc.setDocFilter(this.Document, 'tags', '#chat', 'check'); - break; - } - } else { - console.warn(`No matching document found for item: ${normalizedItem}`); - } - }); - }, '')(); - childScreenToLocal = computedFn((doc: Doc, index: number, isSelected: boolean) => () => { // need to explicitly trigger an invalidation since we're reading everything from the Dom this._forceChildXf; @@ -620,7 +497,7 @@ export class CollectionCardView extends CollectionSubView() { curDoc = () => DocCast(this.layoutDoc._card_curDoc); render() { - const fitContentScale = this.childCards.length === 0 ? 1 : this.fitContentScale; + const fitContentScale = this.childDocsNoInk.length === 0 ? 1 : this.fitContentScale; return ( <div className="collectionCardView-outer" @@ -652,7 +529,7 @@ export class CollectionCardView extends CollectionSubView() { <div className="collectionCardView-flashcardUI" style={{ - pointerEvents: this.childCards.length === 0 ? undefined : 'none', + pointerEvents: this.childDocsNoInk.length === 0 ? undefined : 'none', height: `${100 / this.nativeScaling / fitContentScale}%`, width: `${100 / this.nativeScaling / fitContentScale}%`, transform: `scale(${this.nativeScaling * fitContentScale})`, diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 5e99bec39..954f9aa4c 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -121,6 +121,7 @@ export function CollectionSubView<X>() { return this.dataDoc[this._props.fieldKey]; // this used to be 'layoutDoc', but then template fields will get ignored since the template is not a proto of the layout. hopefully nothing depending on the previous code. } + hasChildDocs = () => this.childLayoutPairs.map(pair => pair.layout); @computed get childLayoutPairs(): { layout: Doc; data: Doc }[] { const { Document, TemplateDataDocument } = this._props; const validPairs = this.childDocs diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 6f0833a22..a4900e9d7 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -89,6 +89,8 @@ export class CollectionView extends ViewBoxAnnotatableComponent<CollectionViewPr TraceMobx(); if (type === undefined) return null; switch (type) { + default: + case CollectionViewType.Freeform: return <CollectionFreeFormView key="collview" {...props} />; case CollectionViewType.Schema: return <CollectionSchemaView key="collview" {...props} />; case CollectionViewType.Calendar: return <CalendarBox key="collview" {...props} />; case CollectionViewType.Docking: return <CollectionDockingView key="collview" {...props} />; @@ -105,8 +107,6 @@ export class CollectionView extends ViewBoxAnnotatableComponent<CollectionViewPr case CollectionViewType.Time: return <CollectionTimeView key="collview" {...props} />; case CollectionViewType.Grid: return <CollectionGridView key="collview" {...props} />; case CollectionViewType.Card: return <CollectionCardView key="collview" {...props} />; - case CollectionViewType.Freeform: - default: return <CollectionFreeFormView key="collview" {...props} />; } }; |
