From e7b0b92adf3404bc24e1b9d5d6a857a0ad6e6418 Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 28 Jan 2025 10:53:58 -0500 Subject: cleaning up card deck view. --- src/client/views/pdf/GPTPopup/GPTPopup.tsx | 49 ++++++++++++++---------------- 1 file changed, 23 insertions(+), 26 deletions(-) (limited to 'src/client/views/pdf') 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 { +export class GPTPopup extends ObservableReactComponent { // eslint-disable-next-line no-use-before-define static Instance: GPTPopup; private messagesEndRef: React.RefObject; @@ -188,16 +186,9 @@ export class GPTPopup extends ObservableReactComponent { 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 { 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 { 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 { * 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 { } 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 { 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 { // 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 { 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 { ); - 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(); -- cgit v1.2.3-70-g09d2 From 6fa61343a3e63a61747bc7123810190057d0bfe4 Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 29 Jan 2025 12:14:12 -0500 Subject: cleaned up filter icons. --- src/client/util/CurrentUserUtils.ts | 38 +++++++++++++++++----- src/client/views/MainView.tsx | 25 +++----------- .../views/collections/CollectionCardDeckView.tsx | 20 +++--------- src/client/views/pdf/GPTPopup/GPTPopup.tsx | 34 +++++++------------ 4 files changed, 48 insertions(+), 69 deletions(-) (limited to 'src/client/views/pdf') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index e806675aa..306914450 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -40,6 +40,7 @@ import { ColorScheme } from "./SettingsManager"; import { SnappingManager } from "./SnappingManager"; import { UndoManager } from "./UndoManager"; import { DocumentView } from "../views/nodes/DocumentView"; +import { IconProp } from "@fortawesome/fontawesome-svg-core"; export interface Button { // DocumentOptions fields a button can set @@ -67,6 +68,21 @@ export interface Button { subMenu?: Button[]; } +// Not really necessary, but for now, all tags should start with a capital first letter +export type TagName = + // eslint-disable-next-line @typescript-eslint/no-unused-vars + T extends `${infer First}${infer Rest}` + ? First extends Uppercase + ? First extends Lowercase + ? never // If it's the same when uppercased and lowercased, it's not a letter. + : T // Otherwise, it's a valid capitalized string. + : never + : never; +export function ToTagName(key: string):"Tag"{ + return ((str => str[0].toUpperCase() + str.slice(1))(key.startsWith('#') ? key.substring(1) : key)) as "Tag"; +} + + export let resolvedPorts: { server: number, socket: number }; export class CurrentUserUtils { @@ -703,18 +719,22 @@ pie title Minerals in my tap water ]}, ] } + + static filterBtnDesc(tag:TagName|"Tag", icon:IconProp):Button { + return { title: tag, isSystem: false, icon: icon.toString(), toolTip:`Click to toggle visibility of ${tag} tagged Docs`, btnType: ButtonType.ToggleButton, expertMode: false, toolType:`#${tag.toLowerCase()}`, funcs: {}, scripts: { onClick: '{ return setTagFilter(this.toolType, _added_, _readOnly_);}'}} + } static filterTools(): Button[] { - const defaultTagButtonDescs = [ - { title: "Star", isSystem: false,icon: "star", toolTip:"Click to toggle visibility of Star tagged Docs", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"#star", funcs: {}, scripts: { onClick: '{ return setTagFilter(this.toolType, _added_, _readOnly_);}'}}, - { title: "Like", isSystem: false,icon: "heart", toolTip:"Click to toggle visibility of Like tagged Docs", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"#like", funcs: {}, scripts: { onClick: '{ return setTagFilter(this.toolType, _added_, _readOnly_);}'}}, - { title: "Todo", isSystem: false,icon: "bolt", toolTip:"Click to toggle visibility of Todo tagged Docs", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"#todo", funcs: {}, scripts: { onClick: '{ return setTagFilter(this.toolType, _added_, _readOnly_);}'}}, - { title: "Idea", isSystem: false,icon: "cloud", toolTip:"Click to toggle visibility of Idea tagged Docs", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"#idea", funcs: {}, scripts: { onClick: '{ return setTagFilter(this.toolType, _added_, _readOnly_);}'}}, + // If there's no active dashboard, then a default set of tags are added, otherwise, the user controls which tags are kept + const tagButtonDescs = Doc.UserDoc().activeDashboard ? [] : [ + this.filterBtnDesc("Star", "star"), + this.filterBtnDesc("Like", "heart"), + this.filterBtnDesc("Todo", "bolt"), + this.filterBtnDesc("Idea", "cloud") ]; - // hack: if there's no dashboard, create default filters. otherwise, just make sure that the Options button is preserved return [ - { title:"Options",isSystem: true,icon: "gear", toolTip:"Click to customize list of filter buttons", btnType: ButtonType.ClickButton, expertMode: false, toolType:"-opts-",funcs: {}, scripts: { onClick: '{ return setTagFilter(this.toolType, false,_readOnly_);}'}}, - ...(Doc.UserDoc().activeDashboard ? [] : defaultTagButtonDescs) + { title:"Options",isSystem: true,icon: "gear", toolTip:"Click to customize list of filter buttons", btnType: ButtonType.ClickButton, expertMode: false, toolType:"-opts-",funcs: {}, scripts: { onClick: '{ return setTagFilter(this.toolType, false,_readOnly_);}'}}, + ...tagButtonDescs ] } static viewTools(): Button[] { @@ -866,7 +886,7 @@ pie title Minerals in my tap water childDontRegisterViews: true, flexGap: 0, _height: 30, _width: 30, ignoreClick: !params.scripts?.onClick, linearView_SubMenu: true, linearView_Expandable: true, embedContainer: menuDoc}; - const items = (menutBtn?:Doc) => !menutBtn ? [] : subMenu.map(sub => this.setupContextMenuBtn(sub, menutBtn) ); + const items = (menuBtn?:Doc) => !menuBtn ? [] : subMenu.map(sub => this.setupContextMenuBtn(sub, menuBtn) ); const creator = params.btnType === ButtonType.MultiToggleButton ? this.multiToggleList : this.linearButtonList; const btnDoc = DocUtils.AssignScripts( DocUtils.AssignDocField(menuDoc, StrCast(params.title), (opts) => creator(opts, items(menuBtnDoc)), reqdSubMenuOpts, items(menuBtnDoc)), params.scripts, params.funcs); diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 7abca5197..d748b70ae 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -20,7 +20,7 @@ import { CollectionViewType, DocumentType } from '../documents/DocumentTypes'; import { Docs } from '../documents/Documents'; import { CalendarManager } from '../util/CalendarManager'; import { CaptureManager } from '../util/CaptureManager'; -import { Button, CurrentUserUtils } from '../util/CurrentUserUtils'; +import { CurrentUserUtils, ToTagName } from '../util/CurrentUserUtils'; import { DocumentManager } from '../util/DocumentManager'; import { DragManager } from '../util/DragManager'; import { dropActionType } from '../util/DropActionTypes'; @@ -63,7 +63,6 @@ import { DocCreatorMenu } from './nodes/DataVizBox/DocCreatorMenu'; import { SchemaCSVPopUp } from './nodes/DataVizBox/SchemaCSVPopUp'; import { DocButtonState } from './nodes/DocumentLinksButton'; import { DocumentView, DocumentViewInternal } from './nodes/DocumentView'; -import { ButtonType } from './nodes/FontIconBox/FontIconBox'; import { ImageEditorData as ImageEditor } from './nodes/ImageBox'; import { LinkDescriptionPopup } from './nodes/LinkDescriptionPopup'; import { LinkDocPreview, LinkInfo } from './nodes/LinkDocPreview'; @@ -874,25 +873,9 @@ export class MainView extends ObservableReactComponent { * @param hotKey tite of the new hotkey */ addHotKey = (hotKey: string) => { - const buttons = DocCast(Doc.UserDoc().myContextMenuBtns); - const filter = DocCast(buttons.Filter); - const title = hotKey.startsWith('#') ? hotKey.substring(1) : hotKey; - - const newKey: Button = { - title, - icon: 'question', - toolTip: `Click to toggle the ${title}'s group's visibility`, - btnType: ButtonType.ToggleButton, - expertMode: false, - toolType: '#' + title, - funcs: {}, - scripts: { onClick: '{ return handleTags(this.toolType, _readOnly_);}' }, - }; - - const newBtn = CurrentUserUtils.setupContextMenuBtn(newKey, filter); - newBtn.isSystem = newBtn[DocData].isSystem = undefined; - - Doc.AddToFilterHotKeys(newBtn); + const filterIcons = DocCast(DocCast(Doc.UserDoc().myContextMenuBtns)?.Filter); + const menuDoc = CurrentUserUtils.setupContextMenuBtn(CurrentUserUtils.filterBtnDesc(ToTagName(hotKey), 'question'), filterIcons); + Doc.AddToFilterHotKeys(menuDoc); }; @computed get mainInnerContent() { diff --git a/src/client/views/collections/CollectionCardDeckView.tsx b/src/client/views/collections/CollectionCardDeckView.tsx index 366d0f448..b40aa37de 100644 --- a/src/client/views/collections/CollectionCardDeckView.tsx +++ b/src/client/views/collections/CollectionCardDeckView.tsx @@ -462,24 +462,13 @@ export class CollectionCardView extends CollectionSubView() { case '6': doc.chatIndex = index; break; - case '1': { - const allHotKeys = Doc.MyFilterHotKeys; - let myTag = ''; + case '1': if (tag) { - for (let i = 0; i < allHotKeys.length; i++) { - const keyTag = StrCast(allHotKeys[i].toolType); - if (keyTag.includes(tag)) { - myTag = keyTag; - break; - } - } - - if (myTag) { - TagItem.addTagToDoc(doc, myTag); - } + 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 '2': case '4': doc.chatFilter = true; @@ -584,7 +573,6 @@ export class CollectionCardView extends CollectionSubView() { * Actually renders all the cards */ @computed get renderCards() { - console.log(this.docDraggedIndex, this.childDocs[0].title, this.childDocs[1].title); // Map sorted documents to their rendered components return this.childDocs.map((doc, index) => { const cardsInRow = this.cardsInRowThatIncludesCardIndex(index); diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.tsx b/src/client/views/pdf/GPTPopup/GPTPopup.tsx index 4028ef479..c33b81eb4 100644 --- a/src/client/views/pdf/GPTPopup/GPTPopup.tsx +++ b/src/client/views/pdf/GPTPopup/GPTPopup.tsx @@ -188,8 +188,8 @@ export class GPTPopup extends ObservableReactComponent { const selected = DocumentView.SelectedDocs().lastElement(); - const questionText = 'Question: ' + StrCast(selected['gptInputText']); - const rubricText = 'Rubric: ' + StrCast(selected['gptRubric']); + const questionText = 'Question: ' + StrCast(selected.gptInputText); + const rubricText = 'Rubric: ' + StrCast(selected.gptRubric); const queryText = questionText + ' UserAnswer: ' + this.quizAnswer + '. ' + 'Rubric' + rubricText; try { @@ -262,27 +262,15 @@ export class GPTPopup extends ObservableReactComponent { try { const questionType = await gptAPICall(this.chatSortPrompt, GPTCallType.TYPE); - const questionNumber = questionType.split(' ')[0]; - let res = ''; - - switch (questionNumber) { - case '1': - case '2': - case '4': - res = await gptAPICall(this.sortDesc, GPTCallType.SUBSET, this.chatSortPrompt); - break; - case '6': - res = await gptAPICall(this.sortDesc, GPTCallType.SORT, this.chatSortPrompt); - break; - default: - { - const selected = DocumentView.SelectedDocs().lastElement(); - const questionText = StrCast(selected?.gptInputText); - - res = await gptAPICall(questionText, GPTCallType.INFO, this.chatSortPrompt); - } - break; - } + const questionNumber = questionType.split(' ')[0][0]; + const res = await (() => { + switch (questionNumber) { + case '1': + case '2': + case '4': return gptAPICall(this.sortDesc, GPTCallType.SUBSET, this.chatSortPrompt); + case '6': return gptAPICall(this.sortDesc, GPTCallType.SORT, this.chatSortPrompt); + default: return gptAPICall(StrCast(DocumentView.SelectedDocs().lastElement()?.gptInputText), GPTCallType.INFO, this.chatSortPrompt); + }})(); // prettier-ignore // Trigger the callback with the result if (this.onSortComplete) { -- cgit v1.2.3-70-g09d2 From 45aa1b54ab37d821257fb00b3be5ef5eca03ef2c Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 29 Jan 2025 15:09:10 -0500 Subject: generlized some of cardDeck ui. showTags is under View for all collections. chat popup is available for all docs, but only works for card views. added clear filters button for chat popup. --- src/client/util/CurrentUserUtils.ts | 34 +++---- .../views/collections/CollectionCardDeckView.tsx | 106 +++++++++---------- .../CollectionFreeFormLayoutEngines.tsx | 2 + .../collectionFreeForm/CollectionFreeFormView.tsx | 1 + .../views/nodes/CollectionFreeFormDocumentView.tsx | 2 +- src/client/views/pdf/GPTPopup/GPTPopup.tsx | 113 ++++++++------------- 6 files changed, 118 insertions(+), 140 deletions(-) (limited to 'src/client/views/pdf') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 306914450..b75961575 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -710,8 +710,6 @@ pie title Minerals in my tap water { title: "Type", icon:"eye", toolTip:"Sort by document type", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"docType", funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}}, { title: "Color", icon:"palette", toolTip:"Sort by document color", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"color", funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}}, { title: "Tags", icon:"bolt", toolTip:"Sort by document's tags", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"tag", funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}}, - { title: "Chat Popup",icon:"lightbulb", toolTip:"Toggle the chat popup's visibility", width: 45, btnType: ButtonType.ToggleButton, expertMode: false, toolType:"toggle-chat",funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'} }, - { title: "Show Tags", icon:"id-card", toolTip:"Toggle tag annotation panel", width: 45, btnType: ButtonType.ToggleButton, expertMode: false, toolType:"toggle-tags",funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'} }, { title: "Sort", icon: "sort" , toolTip: "Manage sort order / lock status", btnType: ButtonType.MultiToggleButton, toolType:"alignment", ignoreClick: true, subMenu: [ { title: "Ascending", toolTip: "Sort the cards in ascending order", btnType: ButtonType.ToggleButton, icon: "sort-up", toolType:"up", ignoreClick: true, scripts: {onClick: '{ return showFreeform(this.toolType, _readOnly_);}'} }, @@ -736,13 +734,15 @@ pie title Minerals in my tap water { title:"Options",isSystem: true,icon: "gear", toolTip:"Click to customize list of filter buttons", btnType: ButtonType.ClickButton, expertMode: false, toolType:"-opts-",funcs: {}, scripts: { onClick: '{ return setTagFilter(this.toolType, false,_readOnly_);}'}}, ...tagButtonDescs ] - } + } static viewTools(): Button[] { return [ - { title: "Snap", icon: "th", toolTip: "Show Snap Lines", btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"snaplines", funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform - { title: "Grid", icon: "border-all", toolTip: "Show Grid", btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"grid", funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform - { title: "Fit All", icon: "object-group", toolTip: "Fit Docs to View (double click to make sticky)",btnType: ButtonType.ToggleButton, ignoreClick:true, expertMode: false, toolType:"viewAll", funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}', onDoubleClick: '{ return showFreeform(this.toolType, _readOnly_, true);}'}}, // Only when floating document is selected in freeform - { title: "Clusters", icon: "braille", toolTip: "Show Doc Clusters", btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"clusters", funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform + { title: "Show Tags", icon: "id-card", toolTip: "Toggle tags", btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"toggle-tags",funcs: { }, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'} }, + { title: "Snap", icon: "th", toolTip: "Show Snap Lines", btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"snaplines", funcs: { hidden: `!SelectedDocType("${CollectionViewType.Freeform}", this.expertMode)`}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform + { title: "Grid", icon: "border-all", toolTip: "Show Grid", btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"grid", funcs: { hidden: `!SelectedDocType("${CollectionViewType.Freeform}", this.expertMode)`}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform + { title: "Fit All", icon: "object-group", toolTip:"Fit Docs to View (double tap to persist)", + btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"viewAll", funcs: { hidden: `!SelectedDocType("${CollectionViewType.Freeform}", this.expertMode)`}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}', onDoubleClick: '{ return showFreeform(this.toolType, _readOnly_, true);}'}}, // Only when floating document is selected in freeform + { title: "Clusters", icon: "braille", toolTip: "Show Doc Clusters", btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"clusters", funcs: { hidden: `!SelectedDocType("${CollectionViewType.Freeform}", this.expertMode)`}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform ] } static textTools():Button[] { @@ -832,21 +832,21 @@ pie title Minerals in my tap water CollectionViewType.Map, CollectionViewType.NoteTaking, CollectionViewType.Schema, CollectionViewType.Stacking, CollectionViewType.Calendar, CollectionViewType.Grid, CollectionViewType.Tree, CollectionViewType.Time, ]), title: "Perspective", toolTip: "View", btnType: ButtonType.DropdownList, ignoreClick: true, width: 100, scripts: { script: '{ return setView(value, shiftKey, _readOnly_); }'}}, - { title: "Pin", icon: "map-pin", toolTip: "Pin View to Trail", btnType: ButtonType.ClickButton, expertMode: false, width: 30, scripts: { onClick: 'pinWithView(altKey)'}, funcs: {hidden: "IsNoneSelected()"}}, - { title: "Header", icon: "heading", toolTip: "Doc Titlebar Color", btnType: ButtonType.ColorButton, expertMode: false, ignoreClick: true, scripts: { script: 'return setHeaderColor(value, _readOnly_)'} }, - { title: "Template",icon: "scroll", toolTip: "Default Note Template",btnType: ButtonType.ToggleButton, expertMode: false, toolType:DocumentType.RTF, scripts: { onClick: '{ return setDefaultTemplate(_readOnly_); }'} }, - { title: "Fill", icon: "fill-drip", toolTip: "Fill/Background Color",btnType: ButtonType.ColorButton, expertMode: false, ignoreClick: true, width: 30, scripts: { script: 'return setBackgroundColor(value, _readOnly_)'} }, // Only when a document is selected - { title: "Overlay", icon: "layer-group", toolTip: "Overlay", btnType: ButtonType.ToggleButton, expertMode: true, toolType:CollectionViewType.Freeform, funcs: {hidden: '!SelectedDocType(this.toolType, this.expertMode, true)'}, scripts: { onClick: '{ return toggleOverlay(_readOnly_); }'}}, // Only when floating document is selected in freeform - { title: "Back", icon: "chevron-left", toolTip: "Prev Animation Frame", btnType: ButtonType.ClickButton, expertMode: true, toolType:CollectionViewType.Freeform, funcs: {hidden: '!SelectedDocType(this.toolType, this.expertMode)'}, width: 30, scripts: { onClick: 'prevKeyFrame(_readOnly_)'}}, - { title: "Num", icon:"", toolTip: "Frame # (click to toggle edit mode)",btnType: ButtonType.TextButton, expertMode: true, toolType:CollectionViewType.Freeform, funcs: {hidden: '!SelectedDocType(this.toolType, this.expertMode)', buttonText: 'selectedDocs()?.lastElement()?.currentFrame?.toString()'}, width: 20, scripts: { onClick: '{ return curKeyFrame(_readOnly_);}'}}, - { title: "Fwd", icon: "chevron-right", toolTip: "Next Animation Frame", btnType: ButtonType.ClickButton, expertMode: true, toolType:CollectionViewType.Freeform, funcs: {hidden: '!SelectedDocType(this.toolType, this.expertMode)'}, width: 30, scripts: { onClick: 'nextKeyFrame(_readOnly_)'}}, - + { title: "Pin", icon: "map-pin", toolTip: "Pin View to Trail", btnType: ButtonType.ClickButton, expertMode: false, width: 30, scripts: { onClick: 'pinWithView(altKey)'}, funcs: {hidden: "IsNoneSelected()"}}, + { title: "Header", icon: "heading", toolTip: "Doc Titlebar Color", btnType: ButtonType.ColorButton, expertMode: false, ignoreClick: true, scripts: { script: 'return setHeaderColor(value, _readOnly_)'} }, + { title: "Template",icon: "scroll", toolTip: "Default Note Template",btnType: ButtonType.ToggleButton, expertMode: false, toolType:DocumentType.RTF, scripts: { onClick: '{ return setDefaultTemplate(_readOnly_); }'} }, + { title: "Fill", icon: "fill-drip", toolTip: "Fill/Background Color",btnType: ButtonType.ColorButton, expertMode: false, ignoreClick: true, width: 30, scripts: { script: 'return setBackgroundColor(value, _readOnly_)'} }, // Only when a document is selected + { title: "Overlay", icon: "layer-group", toolTip: "Overlay", btnType: ButtonType.ToggleButton, expertMode: true, toolType:CollectionViewType.Freeform, funcs: {hidden: '!SelectedDocType(this.toolType, this.expertMode, true)'}, scripts: { onClick: '{ return toggleOverlay(_readOnly_); }'}}, // Only when floating document is selected in freeform + { title: "Back", icon: "chevron-left", toolTip: "Prev Animation Frame", btnType: ButtonType.ClickButton, expertMode: true, toolType:CollectionViewType.Freeform, funcs: {hidden: '!SelectedDocType(this.toolType, this.expertMode)'}, width: 30, scripts: { onClick: 'prevKeyFrame(_readOnly_)'}}, + { title: "Num", icon:"", toolTip: "Frame # (click to toggle edit mode)",btnType: ButtonType.TextButton, expertMode: true, toolType:CollectionViewType.Freeform, funcs: {hidden: '!SelectedDocType(this.toolType, this.expertMode)', buttonText: 'selectedDocs()?.lastElement()?.currentFrame?.toString()'}, width: 20, scripts: { onClick: '{ return curKeyFrame(_readOnly_);}'}}, + { title: "Fwd", icon: "chevron-right", toolTip: "Next Animation Frame", btnType: ButtonType.ClickButton, expertMode: true, toolType:CollectionViewType.Freeform, funcs: {hidden: '!SelectedDocType(this.toolType, this.expertMode)'}, width: 30, scripts: { onClick: 'nextKeyFrame(_readOnly_)'}}, + { title: "Chat", icon:"lightbulb", toolTip: "Toggle Chat Assistant",btnType: ButtonType.ToggleButton, expertMode: false, toolType:"toggle-chat", funcs: {}, width: 30, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'} }, { title: "Filter", icon: "=", toolTip: "Filter cards by tags", subMenu: CurrentUserUtils.filterTools(), ignoreClick:true, toolType:DocumentType.COL, funcs: {hidden: '!SelectedDocType(this.toolType, this.expertMode)'}, btnType: ButtonType.MultiToggleButton, width: 30, backgroundColor: doc.userVariantColor as string}, { title: "Sort", icon: "Sort", toolTip: "Sort Documents", subMenu: CurrentUserUtils.sortTools(), expertMode: false, toolType:DocumentType.COL, funcs: {hidden: `!SelectedDocType(this.toolType, this.expertMode)`, linearView_IsOpen: `SelectedDocType(this.toolType, this.expertMode)`} }, // Always available { title: "Text", icon: "Text", toolTip: "Text functions", subMenu: CurrentUserUtils.textTools(), expertMode: false, toolType:DocumentType.RTF, funcs: { linearView_IsOpen: `SelectedDocType(this.toolType, this.expertMode)`} }, // Always available { title: "Ink", icon: "Ink", toolTip: "Ink functions", subMenu: CurrentUserUtils.inkTools(), expertMode: false, toolType:DocumentType.INK, funcs: {hidden: `IsExploreMode()`, linearView_IsOpen: `SelectedDocType(this.toolType, this.expertMode)`}, scripts: { onClick: 'setInkToolDefaults()'} }, // Always available { title: "Doc", icon: "Doc", toolTip: "Freeform Doc tools", subMenu: CurrentUserUtils.freeTools(), expertMode: false, toolType:CollectionViewType.Freeform, funcs: {hidden: `!SelectedDocType(this.toolType, this.expertMode, true)`, linearView_IsOpen: `SelectedDocType(this.toolType, this.expertMode)`} }, // Always available - { title: "View", icon: "View", toolTip: "View tools", subMenu: CurrentUserUtils.viewTools(), expertMode: false, toolType:CollectionViewType.Freeform, funcs: {hidden: `!SelectedDocType(this.toolType, this.expertMode)`, linearView_IsOpen: `SelectedDocType(this.toolType, this.expertMode)`} }, // Always available + { title: "View", icon: "View", toolTip: "View tools", subMenu: CurrentUserUtils.viewTools(), expertMode: false, toolType:DocumentType.COL, funcs: {hidden: `!SelectedDocType(this.toolType, this.expertMode)`, linearView_IsOpen: `SelectedDocType(this.toolType, this.expertMode)`} }, // Always available { title: "Stack", icon: "View", toolTip: "Stacking tools", subMenu: CurrentUserUtils.stackTools(), expertMode: false, toolType:CollectionViewType.Stacking, funcs: {hidden: `!SelectedDocType(this.toolType, this.expertMode)`, linearView_IsOpen: `SelectedDocType(this.toolType, this.expertMode)`} }, // Always available { title: "Web", icon: "Web", toolTip: "Web functions", subMenu: CurrentUserUtils.webTools(), expertMode: false, toolType:DocumentType.WEB, funcs: {hidden: `!SelectedDocType(this.toolType, this.expertMode)`, linearView_IsOpen: `SelectedDocType(this.toolType, this.expertMode)`} }, // Only when Web is selected diff --git a/src/client/views/collections/CollectionCardDeckView.tsx b/src/client/views/collections/CollectionCardDeckView.tsx index b40aa37de..f24673c39 100644 --- a/src/client/views/collections/CollectionCardDeckView.tsx +++ b/src/client/views/collections/CollectionCardDeckView.tsx @@ -56,7 +56,6 @@ export class CollectionCardView extends CollectionSubView() { constructor(props: SubCollectionViewProps) { super(props); makeObservable(this); - this.setRegenerateCallback(); } protected createDashEventsTarget = (ele: HTMLDivElement | null) => { this._dropDisposer?.(); @@ -74,23 +73,19 @@ export class CollectionCardView extends CollectionSubView() { @computed get _maxRowCount() { return Math.ceil(this.cardDeckWidth / this.cardWidth); } - /** - * Callback to ensure gpt's text versions of the child docs are updated - */ - setRegenerateCallback = () => GPTPopup.Instance.setRegenerateCallback(this.childPairStringListAndUpdateSortDesc); /** * update's gpt's doc-text list and initializes callbacks */ - @action - childPairStringListAndUpdateSortDesc = async () => { - const sortDesc = await this.childPairStringList(); // Await the promise to get the string result - GPTPopup.Instance.setSortDesc(sortDesc.join()); - GPTPopup.Instance.onSortComplete = (sortResult: string, questionType: string, tag?: string) => this.processGptOutput(sortResult, questionType, tag); - GPTPopup.Instance.onQuizRandom = () => this.quizMode(); - }; + childPairStringListAndUpdateSortDesc = () => + this.childPairStringList().then(sortDesc => { + GPTPopup.Instance.setSortDesc(sortDesc.join()); + GPTPopup.Instance.onSortComplete = this.processGptOutput; + GPTPopup.Instance.onQuizRandom = this.quizMode; + }); componentDidMount() { + GPTPopup.Instance.setRegenerateCallback(this.Document, this.childPairStringListAndUpdateSortDesc); 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 @@ -112,6 +107,10 @@ export class CollectionCardView extends CollectionSubView() { } componentWillUnmount() { + GPTPopup.Instance.setSortDesc(''); + GPTPopup.Instance.onSortComplete = undefined; + GPTPopup.Instance.onQuizRandom = undefined; + GPTPopup.Instance.setRegenerateCallback(undefined, null); Object.keys(this._disposers).forEach(key => this._disposers[key]?.()); this._dropDisposer?.(); } @@ -166,8 +165,7 @@ export class CollectionCardView extends CollectionSubView() { * When in quiz mode, randomly selects a document */ quizMode = () => { - const randomIndex = Math.floor(Math.random() * this.childDocs.length); - this.layoutDoc._card_curDoc = this.childDocs[randomIndex]; + this.layoutDoc._card_curDoc = this.childDocs[Math.floor(Math.random() * this.childDocs.length)]; }; setHoveredNodeIndex = action((index: number) => { @@ -437,49 +435,51 @@ export class CollectionCardView extends CollectionSubView() { * 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 */ - @action - processGptOutput = undoable((gptOutput: string, questionType: string, tag?: string) => { - // Split the string into individual list items - const listItems = gptOutput.split('======').filter(item => item.trim() !== ''); - - if (questionType === '2' || questionType === '4') { - this.childDocs.forEach(d => { - d.chatFilter = false; - }); - } + processGptOutput = (gptOutput: string, questionType: string, tag?: string) => + undoable(() => { + // Split the string into individual list items + const listItems = gptOutput.split('======').filter(item => item.trim() !== ''); + + if (questionType === '2' || questionType === '4') { + this.childDocs.forEach(d => { + d.chatFilter = false; + }); + } - if (questionType === '6') { - this.Document[this._props.fieldKey + '_sort'] = docSortings.Chat; - } + if (questionType === '6') { + 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 '6': - doc.chatIndex = index; - break; - case '1': - 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 '2': - case '4': - doc.chatFilter = true; - Doc.setDocFilter(DocCast(this.Document.embedContainer), 'chatFilter', true, 'match'); - break; + 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 '6': + doc.chatIndex = index; + break; + case '1': + 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 '2': + case '4': + doc.chatFilter = true; + Doc.setDocFilter(DocCast(this.Document), 'chatFilter', true, 'check'); + break; + } + } else { + console.warn(`No matching document found for item: ${normalizedItem}`); } - } 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 diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx index 272c13546..bebdbd731 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx @@ -44,6 +44,7 @@ export interface PoolData { transition?: string; highlight?: boolean; pointerEvents?: string; + showTags?: boolean; } export interface ViewDefResult { @@ -425,6 +426,7 @@ function normalizeResults( opacity: newPosRaw.opacity, color: newPosRaw.color, pair: ele[1].pair, + showTags: newPosRaw.showTags, }; if (newPosRaw.transition) newPos.transition = newPosRaw.transition; poolData.set(newPos.pair.layout[Id] + (newPos.replica || ''), { transition: 'all 1s', ...newPos }); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 796949378..69fdf52ff 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1658,6 +1658,7 @@ export class CollectionFreeFormView extends CollectionSubView { @observable private chatMode: boolean = false; private correlatedColumns: string[] = []; - @observable - public visible: boolean = false; - @action - public setVisible = (vis: boolean) => { + @observable public visible: boolean = false; + @action public setVisible = (vis: boolean) => { this.visible = vis; }; - @observable - public loading: boolean = false; - @action - public setLoading = (loading: boolean) => { + @observable public loading: boolean = false; + @action public setLoading = (loading: boolean) => { this.loading = loading; }; - @observable - public text: string = ''; - @action - public setText = (text: string) => { + @observable public text: string = ''; + @action public setText = (text: string) => { this.text = text; }; - @observable - public selectedText: string = ''; - @action - public setSelectedText = (text: string) => { + @observable public selectedText: string = ''; + @action public setSelectedText = (text: string) => { this.selectedText = text; }; - @observable - public dataJson: string = ''; + @observable public dataJson: string = ''; public dataChatPrompt: string | undefined = undefined; - @action - public setDataJson = (text: string) => { + @action public setDataJson = (text: string) => { if (text === '') this.dataChatPrompt = ''; this.dataJson = text; }; - @observable - public imgDesc: string = ''; - @action - public setImgDesc = (text: string) => { + @observable public imgDesc: string = ''; + @action public setImgDesc = (text: string) => { this.imgDesc = text; }; - @observable - public imgUrls: string[][] = []; - @action - public setImgUrls = (imgs: string[][]) => { + @observable public imgUrls: string[][] = []; + @action public setImgUrls = (imgs: string[][]) => { this.imgUrls = imgs; }; - @observable - public mode: GPTPopupMode = GPTPopupMode.SUMMARY; - @action - public setMode = (mode: GPTPopupMode) => { + @observable public mode: GPTPopupMode = GPTPopupMode.SUMMARY; + @action public setMode = (mode: GPTPopupMode) => { this.mode = mode; }; - @observable - public highlightRange: number[] = []; + @observable public highlightRange: number[] = []; @action callSummaryApi = () => {}; - @observable - private done: boolean = false; - @action - public setDone = (done: boolean) => { + @observable private done: boolean = false; + @action public setDone = (done: boolean) => { this.done = done; this.chatMode = false; }; - @observable - private sortDone: boolean = false; // this is so redundant but the og done variable was causing weird unknown problems and im just a girl - - @action - public setSortDone = (done: boolean) => { - this.sortDone = done; - }; - // change what can be a ref into a ref - @observable - private sidebarId: string = ''; - @action - public setSidebarId = (id: string) => { + @observable private sidebarId: string = ''; + @action public setSidebarId = (id: string) => { this.sidebarId = id; }; - @observable - private imgTargetDoc: Doc | undefined; - @action - public setImgTargetDoc = (anchor: Doc) => { + @observable private imgTargetDoc: Doc | undefined; + @action public setImgTargetDoc = (anchor: Doc) => { this.imgTargetDoc = anchor; }; - @observable - private textAnchor: Doc | undefined; - @action - public setTextAnchor = (anchor: Doc) => { + @observable private textAnchor: Doc | undefined; + @action public setTextAnchor = (anchor: Doc) => { this.textAnchor = anchor; }; - @observable - public sortDesc: string = ''; - + @observable public sortDesc: string = ''; @action public setSortDesc = (t: string) => { this.sortDesc = t; }; @@ -153,8 +118,12 @@ export class GPTPopup extends ObservableReactComponent { @observable onQuizRandom?: () => void; @observable cardsDoneLoading = false; + @observable collectionDoc: Doc | undefined = undefined; + @action setCollectionDoc(doc: Doc | undefined) { + this.collectionDoc = doc; + } + @action setCardsDoneLoading(done: boolean) { - console.log(done + 'HI HIHI'); this.cardsDoneLoading = done; } @@ -184,7 +153,6 @@ export class GPTPopup extends ObservableReactComponent { */ generateQuiz = async () => { this.setLoading(true); - this.setSortDone(false); const selected = DocumentView.SelectedDocs().lastElement(); @@ -203,7 +171,6 @@ export class GPTPopup extends ObservableReactComponent { this.conversationArray.push(res); this.setLoading(false); - this.setSortDone(true); } catch (err) { console.error('GPT call failed', err); } @@ -235,7 +202,8 @@ export class GPTPopup extends ObservableReactComponent { * Callback function that causes the card view to update the childpair string list * @param callback */ - @action public setRegenerateCallback(callback: () => Promise) { + @action public setRegenerateCallback(collectionDoc: Doc | undefined, callback: null | (() => Promise)) { + this.setCollectionDoc(collectionDoc); this.regenerateCallback = callback; } @@ -254,7 +222,6 @@ export class GPTPopup extends ObservableReactComponent { */ generateCard = async () => { this.setLoading(true); - this.setSortDone(false); if (this.regenerateCallback) { await this.regenerateCallback(); @@ -296,7 +263,6 @@ export class GPTPopup extends ObservableReactComponent { } this.setLoading(false); - this.setSortDone(true); }; /** @@ -554,7 +520,7 @@ export class GPTPopup extends ObservableReactComponent { }; sortBox = () => ( -
+
{this.heading(this.mode === GPTPopupMode.SORT ? 'SORTING' : 'QUIZ')} <> { @@ -727,6 +693,15 @@ export class GPTPopup extends ObservableReactComponent { {(this.mode === GPTPopupMode.SORT || this.mode === GPTPopupMode.QUIZ) && ( } onClick={() => (this.mode = GPTPopupMode.CARD)} style={{ right: '50px', position: 'absolute' }} /> )} + this.collectionDoc && (this.collectionDoc.childFilters = undefined)} + /> Date: Wed, 29 Jan 2025 15:44:07 -0500 Subject: fixing card quiz mode --- src/client/views/pdf/GPTPopup/GPTPopup.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/client/views/pdf') diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.tsx b/src/client/views/pdf/GPTPopup/GPTPopup.tsx index 63130d056..da0cbea7a 100644 --- a/src/client/views/pdf/GPTPopup/GPTPopup.tsx +++ b/src/client/views/pdf/GPTPopup/GPTPopup.tsx @@ -158,7 +158,7 @@ export class GPTPopup extends ObservableReactComponent { const questionText = 'Question: ' + StrCast(selected.gptInputText); const rubricText = 'Rubric: ' + StrCast(selected.gptRubric); - const queryText = questionText + ' UserAnswer: ' + this.quizAnswer + '. ' + 'Rubric' + rubricText; + const queryText = questionText + ' UserAnswer: ' + this.quizAnswer + '. ' + rubricText; try { const res = await gptAPICall(queryText, GPTCallType.QUIZ); @@ -189,7 +189,7 @@ export class GPTPopup extends ObservableReactComponent { generateRubric = async (inputText: string, doc: Doc) => { try { const res = await gptAPICall(inputText, GPTCallType.RUBRIC); - doc['gptRubric'] = res; + doc.gptRubric = res; return res; } catch (err) { console.error('GPT call failed', err); -- cgit v1.2.3-70-g09d2 From 1e673454f8cabf894e8dfec36734d2cb41caa7b1 Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 3 Feb 2025 10:23:56 -0500 Subject: changed filter 'check' to be exact match and 'match' to be substring. fixed GPTpopup filtering to use a #chat tag instead of a chatFilter field. --- src/client/apis/gpt/GPT.ts | 2 +- src/client/documents/DocUtils.ts | 4 +- src/client/util/CurrentUserUtils.ts | 3 +- src/client/views/TagsView.scss | 4 +- src/client/views/TagsView.tsx | 2 +- .../views/collections/CollectionCardDeckView.tsx | 11 +++-- src/client/views/global/globalScripts.ts | 4 +- src/client/views/nodes/ComparisonBox.tsx | 2 +- src/client/views/nodes/IconTagBox.scss | 2 - src/client/views/pdf/GPTPopup/GPTPopup.tsx | 57 ++++++++++------------ 10 files changed, 44 insertions(+), 47 deletions(-) (limited to 'src/client/views/pdf') diff --git a/src/client/apis/gpt/GPT.ts b/src/client/apis/gpt/GPT.ts index 269a4284a..1894bb4df 100644 --- a/src/client/apis/gpt/GPT.ts +++ b/src/client/apis/gpt/GPT.ts @@ -42,7 +42,7 @@ const callTypeMap: { [type: string]: GPTCallOpts } = { model: 'gpt-4o', maxTokens: 2048, temp: 0.7, - prompt: 'Create a stack of flashcards out of this text with each question and answer labeled as question and answer. Create a title that represents the question in just a few words and label it "title". For some questions, ask "what is this image of" but tailored to stacks theme and the image and write a keyword that represents the image and label it "keyword". Otherwise, write none. Do not label each flashcard and do not include asterisks.', + prompt: 'Create a stack of at least 10 flashcards out of this text with each question and answer labeled as question and answer. Each flashcard should have a title that represents the question in just a few words and label it "title". For some questions, ask "what is this image of" but tailored to stacks theme and the image and write a keyword that represents the image and label it "keyword". Otherwise, write none. Do not label each flashcard and do not include asterisks.', }, completion: { model: 'gpt-4-turbo', maxTokens: 256, temp: 0.5, prompt: "You are a helpful assistant. Answer the user's prompt." }, diff --git a/src/client/documents/DocUtils.ts b/src/client/documents/DocUtils.ts index d11a3e235..23032b62e 100644 --- a/src/client/documents/DocUtils.ts +++ b/src/client/documents/DocUtils.ts @@ -72,9 +72,9 @@ export namespace DocUtils { } const vals = StrListCast(fieldVal); // list typing is very imperfect. casting to a string list doesn't mean that the entries will actually be strings if (vals.length) { - return vals.some(v => typeof v === 'string' && v.includes(value as string)); // bcz: arghh: Todo: comparison should be parameterized as exact, or substring + return vals.some(v => typeof v === 'string' && v === (value as string)); // bcz: arghh: Todo: comparison should be parameterized as exact, or substring } - return Field.toString(fieldVal as FieldType).includes(value as string); // bcz: arghh: Todo: comparison should be parameterized as exact, or substring + return Field.toString(fieldVal as FieldType) === (value as string); // bcz: arghh: Todo: comparison should be parameterized as exact, or substring } /** * @param docs diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index b75961575..0fd09b479 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -728,7 +728,8 @@ pie title Minerals in my tap water this.filterBtnDesc("Star", "star"), this.filterBtnDesc("Like", "heart"), this.filterBtnDesc("Todo", "bolt"), - this.filterBtnDesc("Idea", "cloud") + this.filterBtnDesc("Idea", "cloud"), + this.filterBtnDesc("Chat", "robot") ]; return [ { title:"Options",isSystem: true,icon: "gear", toolTip:"Click to customize list of filter buttons", btnType: ButtonType.ClickButton, expertMode: false, toolType:"-opts-",funcs: {}, scripts: { onClick: '{ return setTagFilter(this.toolType, false,_readOnly_);}'}}, diff --git a/src/client/views/TagsView.scss b/src/client/views/TagsView.scss index 303a08e1e..b21d303fb 100644 --- a/src/client/views/TagsView.scss +++ b/src/client/views/TagsView.scss @@ -12,7 +12,7 @@ .tagsView-list { display: flex; flex-wrap: wrap; - height: inherit; + height: 1; .iconButton-container { min-height: unset !important; } @@ -58,7 +58,7 @@ } .tagsView-editing-box { - margin-top: 8px; + margin-top: 20px; } .tagsView-input-box { diff --git a/src/client/views/TagsView.tsx b/src/client/views/TagsView.tsx index 9e936ea54..b70e21918 100644 --- a/src/client/views/TagsView.tsx +++ b/src/client/views/TagsView.tsx @@ -146,7 +146,7 @@ export class TagItem extends ObservableReactComponent { } } } - doc[DocData].tags = new List((doc[DocData].tags as List).filter(label => label !== tag)); + doc[DocData].tags = new List(StrListCast(doc[DocData].tags).filter(label => label !== tag)); }; private _ref: React.RefObject; diff --git a/src/client/views/collections/CollectionCardDeckView.tsx b/src/client/views/collections/CollectionCardDeckView.tsx index f24673c39..43464e50c 100644 --- a/src/client/views/collections/CollectionCardDeckView.tsx +++ b/src/client/views/collections/CollectionCardDeckView.tsx @@ -85,6 +85,10 @@ export class CollectionCardView extends CollectionSubView() { }); componentDidMount() { + this._disposers.chatVis = reaction( + () => GPTPopup.Instance.Visible, + vis => !vis && this.onGptHide() + ); GPTPopup.Instance.setRegenerateCallback(this.Document, this.childPairStringListAndUpdateSortDesc); 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 @@ -106,6 +110,7 @@ export class CollectionCardView extends CollectionSubView() { ); } + onGptHide = () => Doc.setDocFilter(this.Document, 'tags', '#chat', 'remove'); componentWillUnmount() { GPTPopup.Instance.setSortDesc(''); GPTPopup.Instance.onSortComplete = undefined; @@ -445,7 +450,7 @@ export class CollectionCardView extends CollectionSubView() { if (questionType === '2' || questionType === '4') { this.childDocs.forEach(d => { - d.chatFilter = false; + TagItem.removeTagFromDoc(d, '#chat'); }); } @@ -471,8 +476,8 @@ export class CollectionCardView extends CollectionSubView() { break; case '2': case '4': - doc.chatFilter = true; - Doc.setDocFilter(DocCast(this.Document), 'chatFilter', true, 'check'); + TagItem.addTagToDoc(doc, '#chat'); + Doc.setDocFilter(this.Document, 'tags', '#chat', 'check'); break; } } else { diff --git a/src/client/views/global/globalScripts.ts b/src/client/views/global/globalScripts.ts index b02eabab0..b44292164 100644 --- a/src/client/views/global/globalScripts.ts +++ b/src/client/views/global/globalScripts.ts @@ -223,9 +223,9 @@ ScriptingGlobals.add(function showFreeform( setDoc: (doc: Doc, dv: DocumentView) => { doc[Doc.LayoutFieldKey(doc)+"_sort_desc"] = true; }, }], ['toggle-chat', { - checkResult: (doc: Doc) => GPTPopup.Instance.visible, + checkResult: (doc: Doc) => GPTPopup.Instance.Visible, setDoc: (doc: Doc, dv: DocumentView) => { - if (GPTPopup.Instance.visible){ + if (GPTPopup.Instance.Visible){ doc[Doc.LayoutFieldKey(doc)+"_sort"] = ''; GPTPopup.Instance.setVisible(false); diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx index f5291a4c1..53fbd11c5 100644 --- a/src/client/views/nodes/ComparisonBox.tsx +++ b/src/client/views/nodes/ComparisonBox.tsx @@ -510,7 +510,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent() * Calls GPT for each flashcard type. */ askGPT = async (callType: GPTCallType) => { - const questionText = 'Question: ' + this.frontText; + const questionText = this.frontText; const queryText = questionText + (callType == GPTCallType.QUIZ ? ' UserAnswer: ' + this._inputValue + '. ' + ' Rubric: ' + this.backText : ''); this.loading = true; diff --git a/src/client/views/nodes/IconTagBox.scss b/src/client/views/nodes/IconTagBox.scss index 90cc06092..c79d662f4 100644 --- a/src/client/views/nodes/IconTagBox.scss +++ b/src/client/views/nodes/IconTagBox.scss @@ -10,8 +10,6 @@ gap: 5px; padding-left: 5px; padding-right: 5px; - padding-top: 2px; - padding-bottom: 2px; button { pointer-events: auto; diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.tsx b/src/client/views/pdf/GPTPopup/GPTPopup.tsx index da0cbea7a..f5a9f9e6a 100644 --- a/src/client/views/pdf/GPTPopup/GPTPopup.tsx +++ b/src/client/views/pdf/GPTPopup/GPTPopup.tsx @@ -46,9 +46,9 @@ export class GPTPopup extends ObservableReactComponent { @observable private chatMode: boolean = false; private correlatedColumns: string[] = []; - @observable public visible: boolean = false; + @observable public Visible: boolean = false; @action public setVisible = (vis: boolean) => { - this.visible = vis; + this.Visible = vis; }; @observable public loading: boolean = false; @action public setLoading = (loading: boolean) => { @@ -114,8 +114,8 @@ export class GPTPopup extends ObservableReactComponent { this.sortDesc = t; }; - @observable onSortComplete?: (sortResult: string, questionType: string, tag?: string) => void; - @observable onQuizRandom?: () => void; + onSortComplete?: (sortResult: string, questionType: string, tag?: string) => void; + onQuizRandom?: () => void; @observable cardsDoneLoading = false; @observable collectionDoc: Doc | undefined = undefined; @@ -154,30 +154,27 @@ export class GPTPopup extends ObservableReactComponent { generateQuiz = async () => { this.setLoading(true); - const selected = DocumentView.SelectedDocs().lastElement(); + await this.regenerateCallback?.(); - const questionText = 'Question: ' + StrCast(selected.gptInputText); - const rubricText = 'Rubric: ' + StrCast(selected.gptRubric); - const queryText = questionText + ' UserAnswer: ' + this.quizAnswer + '. ' + rubricText; + const selected = DocumentView.SelectedDocs().lastElement(); + if (!StrCast(selected.gptRubric)) { + await this.generateRubric(StrCast(selected.gptInputText), selected); + } try { - const res = await gptAPICall(queryText, GPTCallType.QUIZ); - if (!res) { - console.error('GPT call failed'); - return; - } - console.log(res); - this.setQuizResp(res); - this.conversationArray.push(res); + const res = await gptAPICall('Question: ' + StrCast(selected.gptInputText) + ' UserAnswer: ' + this.quizAnswer + '. Rubric: ' + StrCast(selected.gptRubric), GPTCallType.QUIZ); + if (res) { + this.setQuizResp(res); + this.conversationArray.push(res); - this.setLoading(false); + this.setLoading(false); + this.onQuizRandom?.(); + } else { + console.error('GPT provided no response'); + } } catch (err) { console.error('GPT call failed', err); } - - if (this.onQuizRandom) { - this.onQuizRandom(); - } }; /** @@ -223,9 +220,7 @@ export class GPTPopup extends ObservableReactComponent { generateCard = async () => { this.setLoading(true); - if (this.regenerateCallback) { - await this.regenerateCallback(); - } + await this.regenerateCallback?.(); try { const questionType = await gptAPICall(this.chatSortPrompt, GPTCallType.TYPE); @@ -442,9 +437,7 @@ export class GPTPopup extends ObservableReactComponent { onClick={() => { this.conversationArray = ['Define the selected card!']; this.setMode(GPTPopupMode.QUIZ); - if (this.onQuizRandom) { - this.onQuizRandom(); - } + this.onQuizRandom?.(); }} color={StrCast(Doc.UserDoc().userVariantColor)} type={Type.TERT} @@ -694,13 +687,13 @@ export class GPTPopup extends ObservableReactComponent { } onClick={() => (this.mode = GPTPopupMode.CARD)} style={{ right: '50px', position: 'absolute' }} /> )} this.collectionDoc && (this.collectionDoc.childFilters = undefined)} + onClick={() => this.collectionDoc && Doc.setDocFilter(this.collectionDoc, 'tags', '#chat', 'remove')} /> { } return ( -
+
{content}
-- cgit v1.2.3-70-g09d2