From 313b3d3e67689b175cdc85426ff6af809d476622 Mon Sep 17 00:00:00 2001
From: aidahosa1 <aisosa_idahosa@brown.edu>
Date: Tue, 17 Sep 2024 12:55:38 -0400
Subject: done with comments-- pull request

---
 src/client/views/PropertiesView.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'src/client/views/PropertiesView.tsx')

diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index daa8e1720..e940ba6f9 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -54,7 +54,7 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
     private _widthUndo?: UndoManager.Batch;
 
     // eslint-disable-next-line no-use-before-define
-    public static Instance: PropertiesView | undefined;
+    public static Instance: PropertiesView;
     constructor(props: PropertiesViewProps) {
         super(props);
         makeObservable(this);
-- 
cgit v1.2.3-70-g09d2


From 3c8af89e3d5370b748fea27c411b3e62758b9a45 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 19 Sep 2024 17:50:48 -0400
Subject: changed backend for filtering buttons to store only one list - the
 list of icon buttons in Doc.MyFilterHotKeys.

---
 src/client/util/CurrentUserUtils.ts                | 85 +++++++++-----------
 src/client/views/FilterPanel.tsx                   | 86 ++++++++++----------
 src/client/views/PropertiesView.tsx                |  2 +-
 src/client/views/TagsView.tsx                      | 55 ++++++-------
 .../views/collections/CollectionCardDeckView.tsx   | 27 +++----
 src/client/views/global/globalScripts.ts           | 18 +++--
 src/client/views/nodes/FontIconBox/FontIconBox.tsx |  4 +-
 src/client/views/nodes/IconTagBox.tsx              | 91 ++++++++--------------
 src/fields/Doc.ts                                  |  8 +-
 9 files changed, 168 insertions(+), 208 deletions(-)

(limited to 'src/client/views/PropertiesView.tsx')

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 262916312..21df0fd3b 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -671,38 +671,33 @@ pie title Minerals in my tap water
     }
     static cardTools(): Button[] {
         return [
-            { title: "Time",      icon:"hourglass-half", toolTip:"Sort by most recent document creation",      btnType: ButtonType.ToggleButton,  expertMode: false, toolType:"time",   funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
-            { 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: "Pile",     icon:"layer-group",        toolTip:"View the cards as a pile in the free form view !",   btnType: ButtonType.ClickButton,  expertMode: false, toolType:"pile",  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: "Time",      icon:"hourglass-half", toolTip:"Sort by most recent document creation",          btnType: ButtonType.ToggleButton, expertMode: false, toolType:"time",       funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
+            { 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: "Pile",      icon:"layer-group",    toolTip:"View the cards as a pile in the free form view!",btnType: ButtonType.ClickButton,  expertMode: false, toolType:"pile",       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: "Sort",    toolTip: "Manage sort order / lock status",   icon: "sort" ,        btnType: ButtonType.MultiToggleButton,                 toolType:"alignment", ignoreClick: true,
+            { 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_);}' }},
-                { title: "Descending",   toolTip: "Sort the cards in descending order",btnType: ButtonType.ToggleButton, icon: "sort-down",toolType:"down",ignoreClick: true,  scripts: {onClick: '{ return showFreeform(this.toolType, _readOnly_);}'} },
+                { 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_);}'} },
+                { title: "Descending",toolTip: "Sort the cards in descending order",btnType: ButtonType.ToggleButton, icon: "sort-down",toolType:"down",ignoreClick: true,  scripts: {onClick: '{ return showFreeform(this.toolType, _readOnly_);}'} },
             ]},
          ]
     }
   
     static tagGroupTools(): Button[] {
-        if (!Doc.UserDoc().activeDashboard) {
-        Doc.UserDoc().myFilterHotKeyTitles = new List<string>(['star', 'heart', 'bolt', 'cloud']);
-        
-        StrListCast(Doc.UserDoc().myFilterHotKeyTitles).forEach(key => {
-            Doc.UserDoc()[key] = key.toLowerCase();
-        });
-}
+        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 handleTags(this.toolType, _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 handleTags(this.toolType, _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 handleTags(this.toolType, _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 handleTags(this.toolType, _readOnly_);}'}},
+        ];
         // hack: if there's no dashboard, create default filters.  otherwise, just make sure that the Options button is preserved
-        return (Doc.UserDoc().activeDashboard ? [] : [
-            { title: "star",  isSystem: false,icon: "star", toolTip:"Click to toggle the star group's visibility",   btnType: ButtonType.ToggleButton,  expertMode: false, toolType:"star", funcs: {}, scripts: { onClick: '{ return handleTags(this.toolType, _readOnly_);}'}},
-            { title: "heart", isSystem: false,icon: "heart", toolTip:"Click to toggle the heart group's visibility",  btnType: ButtonType.ToggleButton,  expertMode: false, toolType:"heart", funcs: {}, scripts: { onClick: '{ return handleTags(this.toolType, _readOnly_);}'}},
-            { title: "bolt",  isSystem: false,icon: "bolt", toolTip:"Click to toggle the bolt group's visibility",    btnType: ButtonType.ToggleButton,  expertMode: false, toolType:"bolt", funcs: {}, scripts: { onClick: '{ return handleTags(this.toolType, _readOnly_);}'}},
-            { title: "cloud", isSystem: false,icon: "cloud", toolTip:"Click to toggle the cloud group's visibility",  btnType: ButtonType.ToggleButton,  expertMode: false, toolType:"cloud", funcs: {}, scripts: { onClick: '{ return handleTags(this.toolType, _readOnly_);}'}},
-        ]).concat([
-            { title: "Options", isSystem: true,icon: "gear", toolTip:"Click to customize your filter panel", btnType: ButtonType.ClickButton,  expertMode: false, toolType:"opts", funcs: {}, scripts: { onClick: '{ return handleTags(this.toolType, _readOnly_);}'}}
-        ])
+        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 handleTags(this.toolType, _readOnly_);}'}},
+            ...(Doc.UserDoc().activeDashboard ? [] : defaultTagButtonDescs)
+        ]
     }
     static viewTools(): Button[] {
         return [
@@ -720,8 +715,8 @@ pie title Minerals in my tap water
             { title: "Font",     toolTip: "Font", width: 100,     btnType: ButtonType.DropdownList,                      toolType:"font",     ignoreClick: true,   scripts: {script:  '{ return setFontAttr(this.toolType, value, _readOnly_);}'},
               btnList: new List<string>(["Roboto", "Roboto Mono", "Nunito", "Times New Roman", "Arial", "Georgia", "Comic Sans MS", "Tahoma", "Impact", "Crimson Text"]) },
             { title: "Font Size",toolTip: "Font size (%size)",    btnType: ButtonType.NumberDropdownButton,              toolType:"fontSize", ignoreClick: true,   scripts: {script:  '{ return setFontAttr(this.toolType, value, _readOnly_);}'}, numBtnMax: 200, numBtnMin: 6 },
-            { title: "Color",    toolTip: "Font color (%color)",  btnType: ButtonType.ColorButton,  icon: "font",        toolType:"fontColor",ignoreClick: true,   scripts: {script:  '{ return setFontAttr(this.toolType, value, _readOnly_);}'}},
-            { title: "Highlight",toolTip: "Font highlight",       btnType: ButtonType.ColorButton,  icon: "highlighter", toolType:"highlight",ignoreClick: true,   scripts: {script:  '{ return setFontAttr(this.toolType, value, _readOnly_);}'}},
+            { title: "Color",    toolTip: "Font color (%color)",  btnType: ButtonType.ColorButton,  icon: "font",        toolType:"fontColor",ignoreClick: true,   scripts: {script:  '{ return setFontAttr(this.toolType, value, _readOnly_);}'} },
+            { title: "Highlight",toolTip: "Font highlight",       btnType: ButtonType.ColorButton,  icon: "highlighter", toolType:"highlight",ignoreClick: true,   scripts: {script:  '{ return setFontAttr(this.toolType, value, _readOnly_);}'} },
             { title: "Bold",     toolTip: "Bold (Ctrl+B)",        btnType: ButtonType.ToggleButton, icon: "bold",        toolType:"bold",     ignoreClick: true,   scripts: {onClick: '{ return toggleCharStyle(this.toolType, _readOnly_);}'} },
             { title: "Italic",   toolTip: "Italic (Ctrl+I)",      btnType: ButtonType.ToggleButton, icon: "italic",      toolType:"italics",  ignoreClick: true,   scripts: {onClick: '{ return toggleCharStyle(this.toolType, _readOnly_);}'} },
             { title: "Under",    toolTip: "Underline (Ctrl+U)",   btnType: ButtonType.ToggleButton, icon: "underline",   toolType:"underline",ignoreClick: true,   scripts: {onClick: '{ return toggleCharStyle(this.toolType, _readOnly_);}'} },
@@ -730,12 +725,12 @@ pie title Minerals in my tap water
             { title: "Vcenter",  toolTip: "Vertical center",      btnType: ButtonType.ToggleButton, icon: "pallet",      toolType:"vcent",    ignoreClick: true,   scripts: {onClick: '{ return toggleCharStyle(this.toolType, _readOnly_);}'} },
             { title: "Align",    toolTip: "Alignment",            btnType: ButtonType.MultiToggleButton,                 toolType:"alignment", ignoreClick: true,
               subMenu: [
-                { title: "Left",     toolTip: "Left align (Cmd-[)",   btnType: ButtonType.ToggleButton, icon: "align-left",  toolType:"left",  ignoreClick: true,  scripts: {onClick: '{ return toggleCharStyle(this.toolType, _readOnly_);}' }},
+                { title: "Left",     toolTip: "Left align (Cmd-[)",   btnType: ButtonType.ToggleButton, icon: "align-left",  toolType:"left",  ignoreClick: true,  scripts: {onClick: '{ return toggleCharStyle(this.toolType, _readOnly_);}'} },
                 { title: "Center",   toolTip: "Center align (Cmd-\\)",btnType: ButtonType.ToggleButton, icon: "align-center",toolType:"center",ignoreClick: true,  scripts: {onClick: '{ return toggleCharStyle(this.toolType, _readOnly_);}'} },
                 { title: "Right",    toolTip: "Right align (Cmd-])",  btnType: ButtonType.ToggleButton, icon: "align-right", toolType:"right", ignoreClick: true,  scripts: {onClick: '{ return toggleCharStyle(this.toolType, _readOnly_);}'} },
             ]},
-            { title: "Elide",    toolTip: "Elide selection",      btnType: ButtonType.ToggleButton, icon: "eye",         toolType:"elide",     ignoreClick: true,  scripts: {onClick: '{ return toggleCharStyle(this.toolType, _readOnly_);}'}},
-            { title: "Dictate",  toolTip: "Dictate",              btnType: ButtonType.ToggleButton, icon: "microphone",  toolType:"dictation", ignoreClick: true,  scripts: {onClick: '{ return toggleCharStyle(this.toolType, _readOnly_);}'}},
+            { title: "Elide",    toolTip: "Elide selection",      btnType: ButtonType.ToggleButton, icon: "eye",         toolType:"elide",     ignoreClick: true,  scripts: {onClick: '{ return toggleCharStyle(this.toolType, _readOnly_);}'} },
+            { title: "Dictate",  toolTip: "Dictate",              btnType: ButtonType.ToggleButton, icon: "microphone",  toolType:"dictation", ignoreClick: true,  scripts: {onClick: '{ return toggleCharStyle(this.toolType, _readOnly_);}'} },
             { title: "NoLink",   toolTip: "Auto Link",            btnType: ButtonType.ToggleButton, icon: "link",        toolType:"noAutoLink", expertMode:true,   scripts: {onClick: '{ return toggleCharStyle(this.toolType, _readOnly_);}'}, funcs: {hidden: 'IsNoviceMode()'}},
             // { title: "Strikethrough", tooltip: "Strikethrough", btnType: ButtonType.ToggleButton, icon: "strikethrough", scripts: {onClick:: 'toggleStrikethrough()'}},
             // { title: "Superscript", tooltip: "Superscript", btnType: ButtonType.ToggleButton, icon: "superscript", scripts: {onClick:: 'toggleSuperscript()'}},
@@ -806,26 +801,20 @@ pie title Minerals in my tap water
             { 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: "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: "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: "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: "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: "Card",    icon: "Card",          toolTip: "Card View Tools",            subMenu: CurrentUserUtils.cardTools(),   expertMode: false, toolType:CollectionViewType.Card,     funcs: {hidden: `!SelectedDocType(this.toolType, this.expertMode)`, linearView_IsOpen: `SelectedDocType(this.toolType, this.expertMode)`} }, // Always available
-            // { title: "Create",   icon: "Create",         toolTip: "Assign card labels",   subMenu: CurrentUserUtils.labelTools(),  expertMode: false, toolType:CollectionViewType.Card,     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
-            { title: "Video",   icon: "Video",         toolTip: "Video functions",      subMenu: CurrentUserUtils.videoTools(),  expertMode: false, toolType:DocumentType.VID,            funcs: {hidden: `!SelectedDocType(this.toolType, this.expertMode)`, linearView_IsOpen: `SelectedDocType(this.toolType, this.expertMode)`} }, // Only when video is selected
-            { title: "Image",   icon: "Image",         toolTip: "Image functions",      subMenu: CurrentUserUtils.imageTools(),  expertMode: false, toolType:DocumentType.IMG,            funcs: {hidden: `!SelectedDocType(this.toolType, this.expertMode)`, linearView_IsOpen: `SelectedDocType(this.toolType, this.expertMode)`} }, // Only when image is selected
-            { title: "Schema",  icon: "Schema",linearBtnWidth:58,toolTip: "Schema functions",subMenu: CurrentUserUtils.schemaTools(),expertMode: false,toolType:CollectionViewType.Schema,funcs: {hidden: `!SelectedDocType(this.toolType, this.expertMode)`, linearView_IsOpen: `SelectedDocType(this.toolType, this.expertMode)`} }, // Only when Schema is selected
-            { title: "Filter", icon:"=",     toolTip:"Filter cards by tags", btnType: ButtonType.MultiToggleButton, width: 150, ignoreClick: true,toolType:DocumentType.COL, 
-            subMenu: this.tagGroupTools(), funcs: {hidden: '!SelectedDocType(this.toolType, this.expertMode)'}, 
-            
-        },
-           ];
-           
+            { title: "Card",    icon: "Card",   toolTip: "Card View Tools",      subMenu: CurrentUserUtils.cardTools(),   expertMode: false, toolType:CollectionViewType.Card,     funcs: {hidden: `!SelectedDocType(this.toolType, this.expertMode)`, linearView_IsOpen: `SelectedDocType(this.toolType, this.expertMode)`} }, // Always available
+         // { title: "Create",  icon: "Create", toolTip: "Assign card labels",   subMenu: CurrentUserUtils.labelTools(),  expertMode: false, toolType:CollectionViewType.Card,     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
+            { title: "Video",   icon: "Video",  toolTip: "Video functions",      subMenu: CurrentUserUtils.videoTools(),  expertMode: false, toolType:DocumentType.VID,            funcs: {hidden: `!SelectedDocType(this.toolType, this.expertMode)`, linearView_IsOpen: `SelectedDocType(this.toolType, this.expertMode)`} }, // Only when video is selected
+            { title: "Image",   icon: "Image",  toolTip: "Image functions",      subMenu: CurrentUserUtils.imageTools(),  expertMode: false, toolType:DocumentType.IMG,            funcs: {hidden: `!SelectedDocType(this.toolType, this.expertMode)`, linearView_IsOpen: `SelectedDocType(this.toolType, this.expertMode)`} }, // Only when image is selected
+            { title: "Schema",  icon: "Schema", toolTip: "Schema functions",     subMenu: CurrentUserUtils.schemaTools(), expertMode: false, toolType:CollectionViewType.Schema,   funcs: {hidden: `!SelectedDocType(this.toolType, this.expertMode)`, linearView_IsOpen: `SelectedDocType(this.toolType, this.expertMode)`}, linearBtnWidth:58 }, // Only when Schema is selected
+            { title: "Filter",  icon: "=",      toolTip: "Filter cards by tags", subMenu: CurrentUserUtils.tagGroupTools(),ignoreClick:true, toolType:DocumentType.COL,            funcs: {hidden: '!SelectedDocType(this.toolType, this.expertMode)'}, btnType: ButtonType.MultiToggleButton, width: 150},
+        ];
     }
 
     /// initializes a context menu button for the top bar context menu
diff --git a/src/client/views/FilterPanel.tsx b/src/client/views/FilterPanel.tsx
index b6bea1d4b..a3c8fc383 100644
--- a/src/client/views/FilterPanel.tsx
+++ b/src/client/views/FilterPanel.tsx
@@ -26,7 +26,7 @@ import { ObservableReactComponent } from './ObservableReactComponent';
 import { IconProp } from '@fortawesome/fontawesome-svg-core';
 
 interface HotKeyButtonProps {
-    hotKey: string;
+    hotKey: Doc;
     selected?: Doc;
 }
 
@@ -53,7 +53,7 @@ const HotKeyIconButton: React.FC<HotKeyButtonProps> = observer(({ hotKey /*, sel
             this.isEditing = false;
         },
         setHotKey(newHotKey: string) {
-            this.myHotKey = newHotKey;
+            this.myHotKey.title = newHotKey;
         },
     }));
 
@@ -64,24 +64,13 @@ const HotKeyIconButton: React.FC<HotKeyButtonProps> = observer(({ hotKey /*, sel
         state.toggleActive();
     };
 
-    const hotKeys = StrListCast(Doc.UserDoc().myFilterHotKeyTitles);
     const buttons = DocCast(Doc.UserDoc().myContextMenuBtns);
-    const filter = DocCast(buttons.Filter);
-
-    /**
-     * The doc of the button in the context menu that corresponds to the current hotkey
-     * @returns
-     */
-    const myHotKeyDoc = () => {
-        const hotKeyDocs = DocListCast(filter.data);
-        return hotKeyDocs.filter(k => StrCast(k.title) === hotKey)[0];
-    };
 
     /**
      * Removes a hotkey from list
      */
     const removeHotKey = () => {
-        Doc.RemoveDocFromList(filter, 'data', myHotKeyDoc());
+        Doc.RemFromFilterHotKeys(hotKey);
     };
 
     /**
@@ -89,11 +78,8 @@ const HotKeyIconButton: React.FC<HotKeyButtonProps> = observer(({ hotKey /*, sel
      * hotkey with the current icon
      */
     const updateFromInput = undoable(() => {
-        const myDoc = myHotKeyDoc();
-        Doc.UserDoc().myFilterHotKeyTitles = new List<string>(hotKeys.map(k => (k === hotKey ? state.myHotKey : k)));
-        Doc.UserDoc()[state.myHotKey] = StrCast(Doc.UserDoc()[hotKey]);
-        myDoc.title = state.myHotKey;
-        myDoc.toolTip = `Click to toggle the ${state.myHotKey}'s group's visibility`;
+        hotKey.title = StrCast(state.myHotKey.title);
+        hotKey.toolTip = `Click to toggle the ${StrCast(hotKey.title)}'s group's visibility`;
     }, '');
 
     /**
@@ -123,13 +109,12 @@ const HotKeyIconButton: React.FC<HotKeyButtonProps> = observer(({ hotKey /*, sel
     /**
      * Panel of icons the user can choose from to represent their tag
      */
-    const iconPanel = iconOpts.map((icon, i) => (
+    const iconPanel = iconOpts.map(icon => (
         <button
-            key={i}
-            onClick={undoable((e: React.MouseEvent) => {
+            key={icon.toString()}
+            onClick={undoable(e => {
                 e.stopPropagation;
-                Doc.UserDoc()[hotKey] = icon.toString();
-                myHotKeyDoc().icon = icon.toString();
+                hotKey[DocData].icon = icon.toString();
             }, '')}
             className="icon-panel-button">
             <FontAwesomeIcon icon={icon} color={SnappingManager.userColor} />
@@ -157,7 +142,7 @@ const HotKeyIconButton: React.FC<HotKeyButtonProps> = observer(({ hotKey /*, sel
                             e.stopPropagation();
                             handleClick();
                         }}>
-                        <FontAwesomeIcon icon={Doc.UserDoc()[hotKey] as IconProp} size="2xl" color={SnappingManager.userColor} />
+                        <FontAwesomeIcon icon={hotKey.icon as IconProp} size="2xl" color={SnappingManager.userColor} />
                     </button>
                 </Tooltip>
                 {state.isActive && <div className="icon-panel">{iconPanel}</div>}
@@ -166,7 +151,7 @@ const HotKeyIconButton: React.FC<HotKeyButtonProps> = observer(({ hotKey /*, sel
                 <input
                     ref={inputRef}
                     type="text"
-                    value={state.myHotKey.toUpperCase()}
+                    value={StrCast(state.myHotKey.title).toUpperCase()}
                     onChange={e => state.setHotKey(e.target.value)}
                     onBlur={() => {
                         state.stopEditing();
@@ -181,13 +166,12 @@ const HotKeyIconButton: React.FC<HotKeyButtonProps> = observer(({ hotKey /*, sel
                     className="hotkey-title-input"
                 />
             ) : (
-                <p className="hotkey-title">{hotKey.toUpperCase()}</p>
+                <p className="hotkey-title">{StrCast(hotKey.title).toUpperCase()}</p>
             )}
             <button
                 className="hotKey-close"
                 onClick={(e: React.MouseEvent) => {
                     e.stopPropagation();
-                    Doc.UserDoc().myFilterHotKeyTitles = new List<string>(hotKeys.filter(k => k !== hotKey));
                     removeHotKey();
                 }}>
                 <FontAwesomeIcon icon={'x' as IconProp} color={SnappingManager.userColor} />
@@ -310,7 +294,7 @@ export class FilterPanel extends ObservableReactComponent<filterProps> {
     @computed get activeRenderedFacetInfos() {
         return new Set(
             Array.from(new Set(Array.from(this._selectedFacetHeaders).concat(this.activeFacetHeaders))).map(facetHeader => {
-                const facetValues = FilterPanel.gatherFieldValues(this.targetDocChildren, facetHeader, StrListCast(this.Document.childFilters));
+                const facetValues = facetHeader.startsWith('#') ? { strings: [] } : FilterPanel.gatherFieldValues(this.targetDocChildren, facetHeader, StrListCast(this.Document.childFilters));
 
                 let nonNumbers = 0;
                 let minVal = Number.MAX_VALUE;
@@ -328,7 +312,10 @@ export class FilterPanel extends ObservableReactComponent<filterProps> {
                 if (facetHeader === 'text') {
                     return { facetHeader, renderType: 'text' };
                 }
-                if (facetHeader !== 'tags' && nonNumbers / facetValues.strings.length < 0.1) {
+                if (facetHeader.startsWith('#')) {
+                    return { facetHeader, renderType: 'togglebox' };
+                }
+                if (facetHeader !== 'tags' && !facetHeader.startsWith('#') && nonNumbers / facetValues.strings.length < 0.1) {
                     const extendedMinVal = minVal - Math.min(1, Math.floor(Math.abs(maxVal - minVal) * 0.1));
                     const extendedMaxVal = Math.max(minVal + 1, maxVal + Math.min(1, Math.ceil(Math.abs(maxVal - minVal) * 0.05)));
                     const ranged: number[] | undefined = Doc.readDocRangeFilter(this.Document, facetHeader); // not the filter range, but the zooomed in range on the filter
@@ -400,30 +387,23 @@ export class FilterPanel extends ObservableReactComponent<filterProps> {
     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: hotKey,
-            icon: 'bolt',
-            toolTip: `Click to toggle the ${hotKey}'s group's visibility`,
+            title,
+            icon: 'question',
+            toolTip: `Click to toggle the ${title}'s group's visibility`,
             btnType: ButtonType.ToggleButton,
             expertMode: false,
-            toolType: 'bolt',
+            toolType: '#' + title,
             funcs: {},
             scripts: { onClick: '{ return handleTags(this.toolType, _readOnly_);}' },
         };
 
-        const currHotKeys = StrListCast(Doc.UserDoc().myFilterHotKeyTitles);
-
-        Doc.UserDoc().myFilterHotKeyTitles = new List<string>(currHotKeys.concat(hotKey));
-
-        Doc.UserDoc()[hotKey] = 'bolt';
-
         const newBtn = CurrentUserUtils.setupContextMenuBtn(newKey, filter);
         newBtn.isSystem = newBtn[DocData].isSystem = undefined;
 
-        const subDocs = DocListCast(filter.data);
-        const opts = subDocs[subDocs.length - 1];
-        Doc.AddDocToList(filter, 'data', newBtn, opts, true);
+        Doc.AddToFilterHotKeys(newBtn);
     };
 
     /**
@@ -432,11 +412,11 @@ export class FilterPanel extends ObservableReactComponent<filterProps> {
      */
     hotKeyButtons = () => {
         const selected = DocumentView.SelectedDocs().lastElement();
-        const hotKeys = StrListCast(Doc.UserDoc().myFilterHotKeyTitles);
+        const hotKeys = Doc.MyFilterHotKeys;
 
         // Selecting a button should make it so that the icon on the top filter panel becomes said icon
         const buttons = hotKeys.map(hotKey => (
-            <Tooltip key={hotKey} title={<div className="dash-tooltip">Click to customize this hotkey&apos;s icon</div>}>
+            <Tooltip key={StrCast(hotKey.title)} title={<div className="dash-tooltip">Click to customize this hotkey&apos;s icon</div>}>
                 <HotKeyIconButton hotKey={hotKey} selected={selected} />
             </Tooltip>
         ));
@@ -561,6 +541,22 @@ export class FilterPanel extends ObservableReactComponent<filterProps> {
                         </div>
                     );
                 });
+            case 'togglebox':
+                return (
+                    <div>
+                        <input
+                            style={{ width: 20, marginLeft: 20 }}
+                            checked={['check', 'exists'].includes(
+                                StrListCast(this.Document._childFilters)
+                                    .find(filter => filter.split(Doc.FilterSep)[0] === 'tags' && filter.split(Doc.FilterSep)[1] === facetHeader)
+                                    ?.split(Doc.FilterSep)[2] ?? ''
+                            )}
+                            type={'checkbox'}
+                            onChange={undoable(e => Doc.setDocFilter(this.Document, 'tags', facetHeader, e.target.checked ? 'check' : 'remove'), 'set filter')}
+                        />
+                        -set-
+                    </div>
+                );
 
             case 'range':
                 {
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index e940ba6f9..f3221d005 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -1176,7 +1176,7 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
     @computed get filtersSubMenu() {
         return (
             // prettier-ignore
-            <PropertiesSection title="Filters" isOpen={this.openFilters} setIsOpen={bool => { this.openFilters = bool; }} onDoubleClick={this.CloseAll}>
+            <PropertiesSection title="Filters" isOpen={this.openFilters} setIsOpen={action(bool => { this.openFilters = bool; })} onDoubleClick={this.CloseAll}>
                 <div className="propertiesView-content filters" style={{ position: 'relative', height: 'auto' }}>
                     <FilterPanel Document={this.selectedDoc ?? Doc.ActiveDashboard!} />
                 </div>
diff --git a/src/client/views/TagsView.tsx b/src/client/views/TagsView.tsx
index cae30218c..2f30edb80 100644
--- a/src/client/views/TagsView.tsx
+++ b/src/client/views/TagsView.tsx
@@ -371,26 +371,20 @@ export class TagsView extends ObservableReactComponent<TagViewProps> {
                                 style={{ width: '8px' }}
                                 tooltip="Close Menu"
                                 onPointerDown={e =>
-                                    setupMoveUpEvents(
-                                        this,
-                                        e,
-                                        returnFalse,
-                                        emptyFunction,
-                                        upEv => {
-                                            this.setToEditing(!this._isEditing);
-                                            upEv.stopPropagation();
-                                        }
-                                    )
+                                    setupMoveUpEvents(this, e, returnFalse, emptyFunction, upEv => {
+                                        this.setToEditing(!this._isEditing);
+                                        upEv.stopPropagation();
+                                    })
                                 }
                                 icon={<FontAwesomeIcon icon={this._isEditing ? 'chevron-up' : 'chevron-down'} size="sm" />}
                             />
                         )}
                         <IconTagBox Views={this._props.Views} IsEditing={this._isEditing} />
                         {Array.from(tagsList)
-                            .filter(tag => tag.startsWith('#') || tag.startsWith('@'))
-                            .map((tag, i) => (
+                            .filter(tag => (tag.startsWith('#') || tag.startsWith('@')) && !Doc.MyFilterHotKeys.some(key => key.toolType === tag))
+                            .map(tag => (
                                 <TagItem
-                                    key={i}
+                                    key={tag}
                                     docs={this._props.Views.map(view => view.Document)}
                                     tag={tag}
                                     tagDoc={TagItem.findTagCollectionDoc(tag) ?? TagItem.createTagCollectionDoc(tag)}
@@ -398,8 +392,8 @@ export class TagsView extends ObservableReactComponent<TagViewProps> {
                                     showRemoveUI={this.isEditing}
                                 />
                             ))}
-                        {Array.from(facesList).map((tag, i) => (
-                            <TagItem key={i} docs={this._props.Views.map(view => view.Document)} tag={tag} tagDoc={undefined} setToEditing={this.setToEditing} showRemoveUI={this.isEditing} />
+                        {Array.from(facesList).map(tag => (
+                            <TagItem key={tag} docs={this._props.Views.map(view => view.Document)} tag={tag} tagDoc={undefined} setToEditing={this.setToEditing} showRemoveUI={this.isEditing} />
                         ))}
                     </div>
                     {this.isEditing ? (
@@ -421,9 +415,9 @@ export class TagsView extends ObservableReactComponent<TagViewProps> {
                                 />
                             </div>
                             <div className="tagsView-suggestions-box">
-                                {TagItem.AllTagCollectionDocs.filter(doc => StrCast(doc.title).startsWith('#') || StrCast(doc.title).startsWith('@')).map(doc => {
-                                    const tag = StrCast(doc.title);
-                                    return (
+                                {TagItem.AllTagCollectionDocs.map(doc => StrCast(doc.title))
+                                    .filter(tag => (tag.startsWith('#') || tag.startsWith('@')) && !Doc.MyFilterHotKeys.some(key => key.toolType === tag))
+                                    .map(tag => (
                                         <Button
                                             style={{ margin: '2px 2px', border: '1px solid black', backgroundColor: 'lightblue', color: 'black' }}
                                             text={tag}
@@ -432,20 +426,17 @@ export class TagsView extends ObservableReactComponent<TagViewProps> {
                                             onClick={() => this.submitTag(tag)}
                                             key={tag}
                                         />
-                                    );
-                                })}
-                                {Array.from(chatTagsList).map(tag => {
-                                    return (
-                                        <Button
-                                            style={{ margin: '2px 2px', border: '1px solid black', backgroundColor: 'lightpink', color: 'black' }}
-                                            text={tag}
-                                            color={SnappingManager.userVariantColor}
-                                            tooltip="Add existing tag"
-                                            onClick={() => this.submitTag(tag)}
-                                            key={tag}
-                                        />
-                                    );
-                                })}
+                                    ))}
+                                {Array.from(chatTagsList).map(tag => (
+                                    <Button
+                                        style={{ margin: '2px 2px', border: '1px solid black', backgroundColor: 'lightpink', color: 'black' }}
+                                        text={tag}
+                                        color={SnappingManager.userVariantColor}
+                                        tooltip="Add existing tag"
+                                        onClick={() => this.submitTag(tag)}
+                                        key={tag}
+                                    />
+                                ))}
                             </div>
                         </div>
                     ) : null}
diff --git a/src/client/views/collections/CollectionCardDeckView.tsx b/src/client/views/collections/CollectionCardDeckView.tsx
index cab7d51e4..bcb42111f 100644
--- a/src/client/views/collections/CollectionCardDeckView.tsx
+++ b/src/client/views/collections/CollectionCardDeckView.tsx
@@ -328,24 +328,22 @@ export class CollectionCardView extends CollectionSubView() {
      */
 
     tagValue = (doc: Doc) => {
-        const keys = StrListCast(Doc.UserDoc().myFilterHotKeyTitles);
+        const keys = Doc.MyFilterHotKeys;
 
-        const isTagActive = (buttonID: number) => {
-            return BoolCast(doc[StrCast(Doc.UserDoc()[keys[buttonID]])]);
-        };
+        const isTagActive = (key: Doc) => BoolCast(key.active);
 
         let base = '';
         let fraction = '';
 
-        for (let i = 0; i < keys.length; i++) {
-            if (isTagActive(i)) {
+        keys.forEach(key => {
+            if (isTagActive(key)) {
                 if (base === '') {
-                    base = i.toString(); // First active tag becomes the base
+                    base = StrCast(key.toolType); // First active tag becomes the base
                 } else {
-                    fraction += i.toString(); // Subsequent active tags become part of the fraction
+                    fraction += StrCast(key.toolType); // Subsequent active tags become part of the fraction
                 }
             }
-        }
+        });
 
         // If no tag was active, return 0 by default
         if (base === '') {
@@ -553,17 +551,16 @@ export class CollectionCardView extends CollectionSubView() {
                         doc.chatIndex = index;
                         break;
                     case '1': {
-                        const allHotKeys = StrListCast(Doc.UserDoc().myFilterHotKeyTitles);
+                        const allHotKeys = Doc.MyFilterHotKeys;
 
                         let myTag = '';
 
                         if (tag) {
                             for (let i = 0; i < allHotKeys.length; i++) {
-                                if (tag.includes(allHotKeys[i])) {
-                                    myTag = StrCast(Doc.UserDoc()[allHotKeys[i]]);
-                                    break;
-                                } else if (tag.includes(StrCast(Doc.UserDoc()[allHotKeys[i]]))) {
-                                    myTag = StrCast(Doc.UserDoc()[allHotKeys[i]]);
+                                // bcz: CHECK THIS CODE OUT -- SOMETHING CHANGED
+                                const keyTag = StrCast(allHotKeys[i].toolType);
+                                if (tag.includes(keyTag)) {
+                                    myTag = keyTag;
                                     break;
                                 }
                             }
diff --git a/src/client/views/global/globalScripts.ts b/src/client/views/global/globalScripts.ts
index d2cee39e2..f7a9689c9 100644
--- a/src/client/views/global/globalScripts.ts
+++ b/src/client/views/global/globalScripts.ts
@@ -270,27 +270,29 @@ ScriptingGlobals.add(function showFreeform(attr: 'hcenter' | 'vcenter' | 'grid'
 });
 
 /**
- * Applies a filter to the selected document (or, if the settins button is pressed, opens the filter panel)
+ * Applies a filter to the selected document (or, if the settings button is pressed, opens the filter panel)
  */
 // eslint-disable-next-line prefer-arrow-callback
 ScriptingGlobals.add(function handleTags(value: string, checkResult?: boolean) {
     const selected = DocumentView.SelectedDocs().lastElement();
+    const isOptions = value === '-opts-';
 
-    function isAttrFiltered(attr: string) {
-        return StrListCast(selected._childFilters).some(filter => filter.includes(attr));
-    }
+    const isAttrFiltered = (attr: string) =>
+        StrListCast(selected._childFilters)
+            .map(filter => filter.split(Doc.FilterSep))
+            .some(([key, val]) => key === 'tags' && val === attr);
 
     if (checkResult) {
-        return value === 'opts' ? PropertiesView.Instance?.openFilters : isAttrFiltered(value);
+        return isOptions ? false : isAttrFiltered(value);
     }
 
-    if (value != 'opts') {
-        isAttrFiltered(value) ? Doc.setDocFilter(selected, value, true, 'remove') : Doc.setDocFilter(selected, value, true, 'match');
+    if (!isOptions) {
+        isAttrFiltered(value) ? Doc.setDocFilter(selected, 'tags', value, 'remove') : Doc.setDocFilter(selected, 'tags', value, 'check');
     } else {
         SnappingManager.PropertiesWidth < 5 && SnappingManager.SetPropertiesWidth(0);
         SnappingManager.SetPropertiesWidth(MainView.Instance.propertiesWidth() < 15 ? 250 : 0);
         PropertiesView.Instance?.CloseAll();
-        PropertiesView.Instance.openFilters = true;
+        runInAction(() => (PropertiesView.Instance.openFilters = SnappingManager.PropertiesWidth > 5));
     }
 
     return undefined;
diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
index f53a7d163..0e4288574 100644
--- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
@@ -267,6 +267,8 @@ export class FontIconBox extends ViewBoxBaseComponent<ButtonProps>() {
         const color = this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Color) as string;
         const items = DocListCast(this.dataDoc.data);
         const multiDoc = this.Document;
+        const selectedItem = StrCast(items.find(itemDoc => ScriptCast(itemDoc.onClick).script.run({ this: itemDoc, value: undefined, _readOnly_: true }).result)?.toolType ?? StrCast(multiDoc.toolType));
+        console.log(selectedItem);
         return (
             <MultiToggle
                 tooltip={`Toggle ${tooltip}`}
@@ -282,7 +284,7 @@ export class FontIconBox extends ViewBoxBaseComponent<ButtonProps>() {
                     tooltip: StrCast(item.toolTip),
                     val: StrCast(item.toolType),
                 }))}
-                selectedVal={StrCast(items.find(itemDoc => ScriptCast(itemDoc.onClick).script.run({ this: itemDoc, value: undefined, _readOnly_: true }).result)?.toolType ?? StrCast(multiDoc.toolType))}
+                selectedVal={selectedItem}
                 setSelectedVal={(val: string | number) => {
                     const itemDoc = items.find(item => item.toolType === val);
                     itemDoc && ScriptCast(itemDoc.onClick).script.run({ this: itemDoc, value: val, _readOnly_: false });
diff --git a/src/client/views/nodes/IconTagBox.tsx b/src/client/views/nodes/IconTagBox.tsx
index 70992e28a..01dca0fbb 100644
--- a/src/client/views/nodes/IconTagBox.tsx
+++ b/src/client/views/nodes/IconTagBox.tsx
@@ -1,21 +1,21 @@
 import { IconProp } from '@fortawesome/fontawesome-svg-core';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Tooltip } from '@mui/material';
-import { computed } from 'mobx';
+import { action, computed, makeObservable } from 'mobx';
 import { observer } from 'mobx-react';
 import React from 'react';
-import { emptyFunction, numberRange } from '../../../Utils';
-import { Doc, StrListCast } from '../../../fields/Doc';
+import { returnFalse, setupMoveUpEvents } from '../../../ClientUtils';
+import { emptyFunction } from '../../../Utils';
+import { Doc } from '../../../fields/Doc';
 import { StrCast } from '../../../fields/Types';
 import { SnappingManager } from '../../util/SnappingManager';
 import { undoable } from '../../util/UndoManager';
 import { MainView } from '../MainView';
 import { ObservableReactComponent } from '../ObservableReactComponent';
 import { PropertiesView } from '../PropertiesView';
+import { TagItem } from '../TagsView';
 import { DocumentView } from './DocumentView';
 import './IconTagBox.scss';
-import { TagItem } from '../TagsView';
-import { returnFalse, setupMoveUpEvents } from '../../../ClientUtils';
 
 export interface IconTagProps {
     Views: DocumentView[];
@@ -30,48 +30,36 @@ export interface IconTagProps {
 export class IconTagBox extends ObservableReactComponent<IconTagProps> {
     constructor(props: IconTagProps) {
         super(props);
+        makeObservable(this);
     }
 
-    @computed get View() {
-        return this._props.Views.lastElement();
-    }
-    @computed get currentScale() {
-        return this.View?.screenToLocalScale();
-    }
+    @computed get View() { return this._props.Views.lastElement(); } // prettier-ignore
+    @computed get currentScale() { return this.View?.screenToLocalScale(); } // prettier-ignore
 
     /**
-     * Opens the filter panel in the properties menu
+     * Sets or removes the specified tag
+     * @param tag tag name (should begin with '#')
+     * @param state flag to add or remove the metadata
      */
-
-    openHotKeyMenu = () => {
-        SnappingManager.PropertiesWidth < 5 && SnappingManager.SetPropertiesWidth(0);
-        SnappingManager.SetPropertiesWidth(MainView.Instance.propertiesWidth() < 15 ? 250 : 0);
-
-        PropertiesView.Instance.CloseAll();
-        PropertiesView.Instance.openFilters = true;
-    };
-
-    /**
-     * @param buttonID
-     * @param doc
-     */
-    setIconTag = undoable((icon: string, state: boolean) => {
+    setIconTag = undoable((tag: string, state: boolean) => {
         this._props.Views.forEach(view => {
-            state && TagItem.addTagToDoc(view.dataDoc, icon);
-            !state && TagItem.removeTagFromDoc(view.dataDoc, icon);
-            view.dataDoc[icon] = state;
+            state && TagItem.addTagToDoc(view.dataDoc, tag);
+            !state && TagItem.removeTagFromDoc(view.dataDoc, tag);
         });
     }, 'toggle card tag');
 
     /**
-     * Determines whether or not the given icon is active depending on the doc's data
-     * @param doc
-     * @param icon
-     * @returns
+     * Returns a renderable version of the button Doc that is colorized to indicate
+     * whether the doc has the associated tag set on it or not.
+     * @param doc doc to test
+     * @param key metadata icon button
+     * @returns an icon for the metdata button
      */
-    getButtonIcon = (doc: Doc, icon: IconProp): JSX.Element => {
-        const isActive = TagItem.docHasTag(doc, icon.toString()); // doc[icon.toString()];
-        const color = isActive ? '#4476f7' : '#323232';
+    getButtonIcon = (doc: Doc, key: Doc): JSX.Element => {
+        const icon = StrCast(key.icon) as IconProp;
+        const tag = StrCast(key.toolType);
+        const isActive = TagItem.docHasTag(doc, tag);
+        const color = isActive ? '#4476f7' : '#323232'; // TODO should use theme colors
 
         return <FontAwesomeIcon icon={icon} style={{ color, height: '20px', width: '20px' }} />;
     };
@@ -80,37 +68,26 @@ export class IconTagBox extends ObservableReactComponent<IconTagProps> {
      * Renders the buttons to customize sorting depending on which group the card belongs to and the amount of total groups
      */
     render() {
-        const amButtons = StrListCast(Doc.UserDoc().myFilterHotKeyTitles).length + 1;
-
-        const keys = StrListCast(Doc.UserDoc().myFilterHotKeyTitles);
-
-        const iconMap = (buttonID: number) => {
-            return StrCast(Doc.UserDoc()[keys[buttonID]]) as IconProp;
-        };
-        const buttons = numberRange(amButtons - 1)
-            .filter(i => this._props.IsEditing || this.View.Document[iconMap(i).toString()] || (DocumentView.Selected.length === 1 && this.View.IsSelected))
-            .map(i => (
-                <Tooltip key={i} title={<div className="dash-tooltip">Click to add/remove this card from the {iconMap(i).toString()} group</div>}>
+        const buttons = Doc.MyFilterHotKeys 
+            .map(key => ({ key, tag: StrCast(key.toolType) }))
+            .filter(({ tag }) => this._props.IsEditing || this.View.Document[tag] || (DocumentView.Selected.length === 1 && this.View.IsSelected))
+            .map(({ key, tag }) => (
+                <Tooltip key={tag} title={<div className="dash-tooltip">Click to add/remove this card from the {tag} group</div>}>
                     <button
-                        key={i}
                         type="button"
                         onPointerDown={e =>
                             setupMoveUpEvents(this, e, returnFalse, emptyFunction, clickEv => {
-                                const state = TagItem.docHasTag(this.View.Document, iconMap(i).toString()); // this.View.Document[iconMap(i).toString()];
-                                this.setIconTag(iconMap(i), !state);
+                                this.setIconTag(tag, !TagItem.docHasTag(this.View.Document, tag));
                                 clickEv.stopPropagation();
                             })
                         }>
-                        {this.getButtonIcon(this.View.Document, iconMap(i))}
+                        {this.getButtonIcon(this.View.Document, key)}
                     </button>
                 </Tooltip>
-            ));
+            )); // prettier-ignore
+
         return !buttons.length ? null : (
-            <div
-                className="card-button-container"
-                style={{
-                    fontSize: '50px',
-                }}>
+            <div className="card-button-container" style={{ fontSize: '50px' }}>
                 {buttons}
             </div>
         );
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 7abba7679..f6b7708b3 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -256,7 +256,13 @@ export class Doc extends RefField {
     public static set ActivePresentation(val) { Doc.ActiveDashboard && (Doc.ActiveDashboard.activePresentation = val) } // prettier-ignore
     public static get ActiveDashboard()       { return DocCast(Doc.UserDoc().activeDashboard); } // prettier-ignore
     public static set ActiveDashboard(val: Opt<Doc>) {  Doc.UserDoc().activeDashboard = val; } // prettier-ignore
-
+    public static get MyFilterHotKeys()       { return DocListCast(DocCast(DocCast(Doc.UserDoc().myContextMenuBtns)?.Filter)?.data).filter(key => key.toolType !== "-opts-"); } // prettier-ignore
+    public static RemFromFilterHotKeys(doc: Doc) {
+        return Doc.RemoveDocFromList(DocCast(DocCast(Doc.UserDoc().myContextMenuBtns)?.Filter), 'data', doc);
+    }
+    public static AddToFilterHotKeys(doc: Doc) {
+        return Doc.AddDocToList(DocCast(DocCast(Doc.UserDoc().myContextMenuBtns)?.Filter), 'data', doc);
+    }
     public static IsInMyOverlay(doc: Doc)     { return Doc.MyOverlayDocs.includes(doc); } // prettier-ignore
     public static AddToMyOverlay(doc: Doc)    { return Doc.ActiveDashboard ? Doc.AddDocToList(Doc.ActiveDashboard, 'myOverlayDocs', doc) : Doc.AddDocToList(DocCast(Doc.UserDoc().myOverlayDocs), undefined, doc); } // prettier-ignore
     public static RemFromMyOverlay(doc: Doc)  { return Doc.ActiveDashboard ? Doc.RemoveDocFromList(Doc.ActiveDashboard,'myOverlayDocs', doc) : Doc.RemoveDocFromList(DocCast(Doc.UserDoc().myOverlayDocs), undefined, doc); } // prettier-ignore
-- 
cgit v1.2.3-70-g09d2

