From f6581aebd603dcdd84e033acffe46352ce5a4485 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 19 Feb 2025 15:05:47 -0500
Subject: more gptpopup cleanup.

---
 src/client/views/pdf/GPTPopup/GPTPopup.tsx | 95 ++++++++++++++----------------
 1 file changed, 45 insertions(+), 50 deletions(-)

(limited to 'src/client/views/pdf/GPTPopup/GPTPopup.tsx')

diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.tsx b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
index 72381cfad..cb3e9b2d7 100644
--- a/src/client/views/pdf/GPTPopup/GPTPopup.tsx
+++ b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
@@ -10,7 +10,7 @@ import { ClientUtils } from '../../../../ClientUtils';
 import { Doc } from '../../../../fields/Doc';
 import { NumCast, StrCast } from '../../../../fields/Types';
 import { Networking } from '../../../Network';
-import { DescriptionSeperator, DocSeperator, GPTCallType, GPTTypeStyle, gptAPICall, gptImageCall } from '../../../apis/gpt/GPT';
+import { DescriptionSeperator, DocSeperator, GPTCallType, GPTDocCommand, gptAPICall, gptImageCall } from '../../../apis/gpt/GPT';
 import { DocUtils } from '../../../documents/DocUtils';
 import { Docs } from '../../../documents/Documents';
 import { SettingsManager } from '../../../util/SettingsManager';
@@ -18,7 +18,7 @@ import { SnappingManager } from '../../../util/SnappingManager';
 import { undoable } from '../../../util/UndoManager';
 import { ObservableReactComponent } from '../../ObservableReactComponent';
 import { TagItem } from '../../TagsView';
-import { docSortings } from '../../collections/CollectionSubView';
+import { ChatSortField, docSortings } from '../../collections/CollectionSubView';
 import { DocumentView } from '../../nodes/DocumentView';
 import { AnchorMenu } from '../AnchorMenu';
 import './GPTPopup.scss';
@@ -36,6 +36,7 @@ export enum GPTPopupMode {
 export class GPTPopup extends ObservableReactComponent<object> {
     // eslint-disable-next-line no-use-before-define
     static Instance: GPTPopup;
+    static ChatTag = '#chat'; // tag used by GPT popup to filter docs
     private _messagesEndRef: React.RefObject<HTMLDivElement>;
     private _correlatedColumns: string[] = [];
     private _dataChatPrompt: string | undefined = undefined;
@@ -77,10 +78,10 @@ export class GPTPopup extends ObservableReactComponent<object> {
                 if (hasChildDocs) {
                     this._textToDocMap.clear();
                     this.setCollectionContext(selDoc.Document);
-                    this.onGptResponse = (sortResult: string, questionType: GPTTypeStyle, tag?: string) => this.processGptResponse(selDoc, this._textToDocMap, sortResult, questionType, tag);
+                    this.onGptResponse = (sortResult: string, questionType: GPTDocCommand, args?: string) => this.processGptResponse(selDoc, this._textToDocMap, sortResult, questionType, args);
                     this.onQuizRandom = () => this.randomlyChooseDoc(selDoc.Document, hasChildDocs());
                     this._documentDescriptions = Promise.all(hasChildDocs().map(doc =>
-                            Doc.getDescription(doc).then(text => this._textToDocMap.set(text, doc) && `${DescriptionSeperator}${text}${DescriptionSeperator}`)
+                            Doc.getDescription(doc).then(text => this._textToDocMap.set(text.trim(), doc) && `${DescriptionSeperator}${text}${DescriptionSeperator}`)
                         )).then(docDescriptions => docDescriptions.join()); // prettier-ignore
                 }
             },
@@ -110,8 +111,8 @@ export class GPTPopup extends ObservableReactComponent<object> {
     @action public setMode = (mode: GPTPopupMode) => (this._mode = mode);
 
     onQuizRandom?: () => void;
-    onGptResponse?: (sortResult: string, questionType: GPTTypeStyle, tag?: string) => void;
-    questionTypeNumberToStyle = (questionType: string) => +questionType.split(' ')[0][0];
+    onGptResponse?: (sortResult: string, questionType: GPTDocCommand, args?: string) => void;
+    NumberToCommandType = (questionType: string) => +questionType.split(' ')[0][0];
 
     /**
      * Processes gpt's output depending on the type of question the user asked. Converts gpt's string output to
@@ -120,44 +121,38 @@ export class GPTPopup extends ObservableReactComponent<object> {
      * @param questionType
      * @param tag
      */
-    processGptResponse = (docView: DocumentView, textToDocMap: Map<string, Doc>, gptOutput: string, questionType: GPTTypeStyle, tag?: string) =>
+    processGptResponse = (docView: DocumentView, textToDocMap: Map<string, Doc>, gptOutput: string, questionType: GPTDocCommand, args?: string) =>
         undoable(() => {
-            // Split the string into individual list items
-            const listItems = gptOutput.split('======').filter(item => item.trim() !== '');
-
-            if (questionType === GPTTypeStyle.Filter) {
-                docView.ComponentView?.hasChildDocs?.().forEach(d => TagItem.removeTagFromDoc(d, '#chat'));
-            }
-
-            if (questionType === GPTTypeStyle.SortDocs) {
-                docView.Document[docView.ComponentView?.fieldKey + '_sort'] = docSortings.Chat;
-            }
-
-            listItems.forEach((item, index) => {
-                const normalizedItem = item.replace(/\n/g, ' ').trim();
-                // find the corresponding Doc in the textToDoc map
-                const doc = textToDocMap.get(normalizedItem);
-                if (doc) {
+            switch (questionType) { // reset collection based on question typefc
+                case GPTDocCommand.Sort:
+                    docView.Document[docView.ComponentView?.fieldKey + '_sort'] = docSortings.Chat;
+                    break;
+                case GPTDocCommand.Filter:
+                    docView.ComponentView?.hasChildDocs?.().forEach(d => TagItem.removeTagFromDoc(d, GPTPopup.ChatTag));
+                    break;
+            } // prettier-ignore
+
+            gptOutput.split('======').filter(item => item.trim() !== '')  // Split output into individual document contents
+                .map(docContentRaw => textToDocMap.get(docContentRaw.replace(/\n/g, ' ').trim())) // the find the corresponding Doc using textToDoc map
+                .filter(doc => doc).map(doc => doc!) // filter out undefined values
+                .forEach((doc, index) => {
                     switch (questionType) {
-                        case GPTTypeStyle.SortDocs:
-                            doc.chatIndex = index;
+                        case GPTDocCommand.Sort:
+                            doc[ChatSortField] = 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;
+                        case GPTDocCommand.AssignTags:
+                            if (args) {
+                                const hashTag = args.startsWith('#') ? args : '#' + args[0].toLowerCase() + args.slice(1);
+                                const filterTag = Doc.MyFilterHotKeys.map(key => StrCast(key.toolType)).find(key => key.includes(args)) ?? hashTag;
                                 TagItem.addTagToDoc(doc, filterTag);
                             }
                             break;
-                        case GPTTypeStyle.Filter:
-                            TagItem.addTagToDoc(doc, '#chat');
-                            Doc.setDocFilter(docView.Document, 'tags', '#chat', 'check');
+                        case GPTDocCommand.Filter:
+                            TagItem.addTagToDoc(doc, GPTPopup.ChatTag);
+                            Doc.setDocFilter(docView.Document, 'tags', GPTPopup.ChatTag, 'check');
                             break;
                     }
-                } else {
-                    console.warn(`No matching document found for item: ${normalizedItem}`);
-                }
-            });
+                }); // prettier-ignore
         }, '')();
 
     /**
@@ -173,7 +168,7 @@ export class GPTPopup extends ObservableReactComponent<object> {
         StrCast(doc.gptRubric)
             ? Promise.resolve(StrCast(doc.gptRubric))
             : Doc.getDescription(doc).then(desc =>
-                  gptAPICall(desc, GPTCallType.RUBRIC)
+                  gptAPICall(desc, GPTCallType.MAKERUBRIC)
                       .then(res => (doc.gptRubric = res))
                       .catch(err => console.error('GPT call failed', err))
               );
@@ -191,7 +186,7 @@ export class GPTPopup extends ObservableReactComponent<object> {
                     `Question: ${desc}; 
                     UserAnswer: ${quizAnswer};
                     Rubric: ${StrCast(doc.gptRubric)}`,
-                    GPTCallType.QUIZ
+                    GPTCallType.QUIZDOC
                 ).then(res => {
                     this._conversationArray.push(res || 'GPT provided no answer');
                     this.onQuizRandom?.();
@@ -205,20 +200,20 @@ export class GPTPopup extends ObservableReactComponent<object> {
      * @param userPrompt the user's input that chat will respond to
      */
     generateUserPromptResponse = (userPrompt: string) =>
-        gptAPICall(userPrompt, GPTCallType.TYPE).then(questionType =>
+        gptAPICall(userPrompt, GPTCallType.COMMANDTYPE, undefined, true).then((commandType, args = commandType.split(' ').slice(1).join(' ')) =>
             (async () => {
-                switch (this.questionTypeNumberToStyle(questionType)) {
-                    case GPTTypeStyle.AssignTags:
-                    case GPTTypeStyle.Filter:    return this._documentDescriptions?.then(descs => gptAPICall(descs, GPTCallType.SUBSET, userPrompt)) ?? "";
-                    case GPTTypeStyle.SortDocs:  return this._documentDescriptions?.then(descs => gptAPICall(descs, GPTCallType.SORT, userPrompt)) ?? "";
-                    default:                     return Doc.getDescription(DocumentView.SelectedDocs().lastElement()).then(desc => gptAPICall(desc, GPTCallType.INFO, userPrompt));
+                switch (this.NumberToCommandType(commandType)) {
+                    case GPTDocCommand.AssignTags:
+                    case GPTDocCommand.Filter:    return this._documentDescriptions?.then(descs => gptAPICall(userPrompt, GPTCallType.SUBSETDOCS, descs)) ?? "";
+                    case GPTDocCommand.Sort:      return this._documentDescriptions?.then(descs => gptAPICall(userPrompt, GPTCallType.SORTDOCS, descs)) ?? "";
+                    default:                      return Doc.getDescription(DocumentView.SelectedDocs().lastElement()).then(desc => gptAPICall(userPrompt, GPTCallType.DOCINFO, desc));
                 } // prettier-ignore
             })().then(
                 action(res => {
                     // Trigger the callback with the result
-                    this.onGptResponse?.(res || 'Something went wrong :(', this.questionTypeNumberToStyle(questionType), questionType.split(' ').slice(1).join(' '));
+                    this.onGptResponse?.(res || 'Something went wrong :(', this.NumberToCommandType(commandType), args);
                     this._conversationArray.push(
-                        [GPTTypeStyle.GeneralInfo, GPTTypeStyle.DocInfo].includes(this.questionTypeNumberToStyle(questionType)) ? res:
+                        this.NumberToCommandType(commandType) === GPTDocCommand.GetInfo ? res:
                         // Extract explanation surrounded by the DocSeperator string (defined in GPT.ts) at the top or both at the top and bottom
                         (res.match(new RegExp(`${DocSeperator}\\s*([\\s\\S]*?)\\s*(?:${DocSeperator}|$)`)) ?? [])[1]?.trim() ?? 'No explanation found'
                     );
@@ -583,10 +578,10 @@ export class GPTPopup extends ObservableReactComponent<object> {
                         tooltip="Clear Chat filter"
                         toggleType={ToggleType.BUTTON}
                         type={Type.PRIM}
-                        toggleStatus={Doc.hasDocFilter(this._collectionContext, 'tags', '#chat')}
-                        text={Doc.hasDocFilter(this._collectionContext, 'tags', '#chat') ? 'filtered' : ''}
-                        color={Doc.hasDocFilter(this._collectionContext, 'tags', '#chat') ? 'red' : 'transparent'}
-                        onClick={() => this._collectionContext && Doc.setDocFilter(this._collectionContext, 'tags', '#chat', 'remove')}
+                        toggleStatus={Doc.hasDocFilter(this._collectionContext, 'tags', GPTPopup.ChatTag)}
+                        text={Doc.hasDocFilter(this._collectionContext, 'tags', GPTPopup.ChatTag) ? 'filtered' : ''}
+                        color={Doc.hasDocFilter(this._collectionContext, 'tags', GPTPopup.ChatTag) ? 'red' : 'transparent'}
+                        onClick={() => this._collectionContext && Doc.setDocFilter(this._collectionContext, 'tags', GPTPopup.ChatTag, 'remove')}
                     />
                     {(this._mode === GPTPopupMode.USER_PROMPT || this._mode === GPTPopupMode.QUIZ_RESPONSE) && (
                         <IconButton color={StrCast(SettingsManager.userVariantColor)} tooltip="back" icon={<CgCornerUpLeft size="16px" />} onClick={() => (this._mode = GPTPopupMode.GPT_MENU)} />
-- 
cgit v1.2.3-70-g09d2

