From b7105063030ee29ba644d8fbb5878c8ee41fd389 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 16 Dec 2024 11:52:42 -0500
Subject: fixed api for creating an image using another image as a structure
 reference.

---
 .../collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx | 3 ---
 1 file changed, 3 deletions(-)

(limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx')

diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index 79aad0ef2..272c13546 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -102,7 +102,6 @@ export function computePassLayout(poolData: Map<string, PoolData>, pivotDoc: Doc
             replica: '',
         });
     });
-    // eslint-disable-next-line no-use-before-define
     return normalizeResults(panelDim, 12, docMap, poolData, viewDefsToJSX, [], 0, []);
 }
 
@@ -272,7 +271,6 @@ export function computePivotLayout(poolData: Map<string, PoolData>, pivotDoc: Do
         payload: pivotColumnGroups.get(key)?.filters,
     }));
     groupNames.push(...dividers);
-    // eslint-disable-next-line no-use-before-define
     return normalizeResults(panelDim, maxText, docMap, poolData, viewDefsToJSX, groupNames, 0, []);
 }
 
@@ -347,7 +345,6 @@ export function computeTimelineLayout(poolData: Map<string, PoolData>, pivotDoc:
         if (!stack && (curTime === undefined || Math.abs(x - (curTime - minTime) * scaling) > pivotAxisWidth)) {
             groupNames.push({ type: 'text', text: toLabel(key), x: x, y: stack * 25, height: fontHeight, fontSize, payload: undefined });
         }
-        // eslint-disable-next-line no-use-before-define
         layoutDocsAtTime(keyDocs, key);
     });
     if (sortedKeys.length && curTime !== undefined && curTime > sortedKeys[sortedKeys.length - 1]) {
-- 
cgit v1.2.3-70-g09d2


From 45aa1b54ab37d821257fb00b3be5ef5eca03ef2c Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
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/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx')

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<Partial<collection
             width: _width,
             height: _height,
             transition: StrCast(childDocLayout.dataTransition),
+            showTags: BoolCast(childDocLayout.showTags) || BoolCast(this.Document.showChildTags) || BoolCast(this.Document._layout_showTags),
             pointerEvents: Cast(childDoc.pointerEvents, 'string', null),
             pair,
             replica: '',
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 3cbfb1796..68105f2f1 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -28,7 +28,7 @@ export enum GroupActive { // flags for whether a view is activate because of its
 }
 /// Ugh, typescript has no run-time way of iterating through the keys of an interface.  so we need
 /// manaully keep this list of keys in synch wih the fields of the freeFormProps interface
-const freeFormPropsKeys = ['x', 'y', 'z', 'zIndex', 'rotation', 'opacity', 'backgroundColor', 'color', 'highlight', 'width', 'height', 'autoDim', 'transition'];
+const freeFormPropsKeys = ['x', 'y', 'z', 'width', 'height', 'zIndex', 'autoDim', 'rotation', 'color', 'backgroundColor', 'opacity', 'highlight', 'transition'];
 interface freeFormProps {
     x: number;
     y: number;
diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.tsx b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
index c33b81eb4..63130d056 100644
--- a/src/client/views/pdf/GPTPopup/GPTPopup.tsx
+++ b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
@@ -1,5 +1,5 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { Button, IconButton, Type } from '@dash/components';
+import { Button, IconButton, Toggle, ToggleType, Type } from '@dash/components';
 import { action, makeObservable, observable, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
@@ -46,105 +46,70 @@ export class GPTPopup extends ObservableReactComponent<object> {
     @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<object> {
     @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<object> {
      */
     generateQuiz = async () => {
         this.setLoading(true);
-        this.setSortDone(false);
 
         const selected = DocumentView.SelectedDocs().lastElement();
 
@@ -203,7 +171,6 @@ export class GPTPopup extends ObservableReactComponent<object> {
             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<object> {
      * Callback function that causes the card view to update the childpair string list
      * @param callback
      */
-    @action public setRegenerateCallback(callback: () => Promise<void>) {
+    @action public setRegenerateCallback(collectionDoc: Doc | undefined, callback: null | (() => Promise<void>)) {
+        this.setCollectionDoc(collectionDoc);
         this.regenerateCallback = callback;
     }
 
@@ -254,7 +222,6 @@ export class GPTPopup extends ObservableReactComponent<object> {
      */
     generateCard = async () => {
         this.setLoading(true);
-        this.setSortDone(false);
 
         if (this.regenerateCallback) {
             await this.regenerateCallback();
@@ -296,7 +263,6 @@ export class GPTPopup extends ObservableReactComponent<object> {
         }
 
         this.setLoading(false);
-        this.setSortDone(true);
     };
 
     /**
@@ -554,7 +520,7 @@ export class GPTPopup extends ObservableReactComponent<object> {
     };
 
     sortBox = () => (
-        <div style={{ height: '80%' }}>
+        <div className="gptPopup-sortBox" style={{ height: '80%' }}>
             {this.heading(this.mode === GPTPopupMode.SORT ? 'SORTING' : 'QUIZ')}
             <>
                 {
@@ -727,6 +693,15 @@ export class GPTPopup extends ObservableReactComponent<object> {
                     {(this.mode === GPTPopupMode.SORT || this.mode === GPTPopupMode.QUIZ) && (
                         <IconButton color={StrCast(SettingsManager.userVariantColor)} tooltip="back" icon={<CgCornerUpLeft size="16px" />} onClick={() => (this.mode = GPTPopupMode.CARD)} style={{ right: '50px', position: 'absolute' }} />
                     )}
+                    <Toggle
+                        tooltip="clear filters"
+                        toggleType={ToggleType.BUTTON}
+                        type={Type.PRIM}
+                        toggleStatus={this.collectionDoc?.childFilters ? true : false}
+                        text={this.collectionDoc?.childFilters ? 'filtered' : ''}
+                        color="red"
+                        onClick={() => this.collectionDoc && (this.collectionDoc.childFilters = undefined)}
+                    />
                     <IconButton
                         color={StrCast(SettingsManager.userVariantColor)}
                         tooltip="close"
-- 
cgit v1.2.3-70-g09d2

