From ad32c1606395cdc71ba50eb9b51d3a9d3b707ca0 Mon Sep 17 00:00:00 2001
From: alyssaf16 <alyssa_feinberg@brown.edu>
Date: Tue, 18 Jun 2024 15:23:12 -0400
Subject: working on various image flashcard ideas

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

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

diff --git a/src/client/views/collections/CollectionCardDeckView.tsx b/src/client/views/collections/CollectionCardDeckView.tsx
index de46180e6..50af9df9e 100644
--- a/src/client/views/collections/CollectionCardDeckView.tsx
+++ b/src/client/views/collections/CollectionCardDeckView.tsx
@@ -373,7 +373,7 @@ export class CollectionCardView extends CollectionSubView() {
         const hrefComplete = `${hrefParts[0]}_o.${hrefParts[1]}`;
         try {
             const hrefBase64 = await CollectionCardView.imageUrlToBase64(hrefComplete);
-            const response = await gptImageLabel(hrefBase64);
+            const response = await gptImageLabel(hrefBase64, 'Give three to five labels to describe this image.');
             image[DocData].description = response.trim();
             return response; // Return the response from gptImageLabel
         } catch (error) {
-- 
cgit v1.2.3-70-g09d2


From cd7e213e7e0b9b6606ae068c29412bb64cdf6f2d Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 7 Oct 2024 10:43:48 -0400
Subject: comment cleanup

---
 .../views/collections/CollectionCardDeckView.tsx   | 24 ++++++++++------------
 1 file changed, 11 insertions(+), 13 deletions(-)

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

diff --git a/src/client/views/collections/CollectionCardDeckView.tsx b/src/client/views/collections/CollectionCardDeckView.tsx
index a9ab9de26..0c2bcc580 100644
--- a/src/client/views/collections/CollectionCardDeckView.tsx
+++ b/src/client/views/collections/CollectionCardDeckView.tsx
@@ -411,15 +411,14 @@ export class CollectionCardView extends CollectionSubView() {
     translateOverflowX = (realIndex: number, calcRowCards: number) => (realIndex < this._maxRowCount ? 0 : (this._maxRowCount - calcRowCards) * (this.childPanelWidth() / 2));
 
     /**
-     * Determines how far to translate a card in the y direction depending on its index, whether or not its being hovered, or if it's selected
-     * @param isHovered
-     * @param isActive
-     * @param realIndex
-     * @param amCards
-     * @param calcRowIndex
-     * @returns
+     * Determines how far to translate a card in the y direction depending on its index and if it's selected
+     * @param isActive whether the card is focused for interaction
+     * @param realIndex index of card from start of deck
+     * @param amCards ??
+     * @param calcRowIndex index of card from start of row
+     * @returns Y translation of card
      */
-    calculateTranslateY = (isHovered: boolean, isActive: boolean, realIndex: number, amCards: number, calcRowIndex: number) => {
+    calculateTranslateY = (isActive: boolean, realIndex: number, amCards: number, calcRowIndex: number) => {
         const rowHeight = (this._props.PanelHeight() * this.fitContentScale) / this.numRows;
         const rowIndex = Math.trunc(realIndex / this._maxRowCount);
         const rowToCenterShift = this.numRows / 2 - rowIndex;
@@ -592,9 +591,8 @@ export class CollectionCardView extends CollectionSubView() {
 
         // Map sorted documents to their rendered components
         return this.sortedDocs.map((doc, index) => {
-            const realIndex = this.sortedDocs.indexOf(doc);
-            const calcRowIndex = this.overflowIndexCalc(realIndex);
-            const amCards = this.overflowAmCardsCalc(realIndex);
+            const calcRowIndex = this.overflowIndexCalc(index);
+            const amCards = this.overflowAmCardsCalc(index);
             const view = DocumentView.getDocumentView(doc, this.DocumentView?.());
 
             const childScreenToLocal = this.childScreenToLocal(doc, index, calcRowIndex, !!view?.IsContentActive, amCards);
@@ -617,8 +615,8 @@ export class CollectionCardView extends CollectionSubView() {
                     style={{
                         width: this.childPanelWidth(),
                         height: 'max-content',
-                        transform: `translateY(${this.calculateTranslateY(this._hoveredNodeIndex === index, !!view?.IsContentActive, realIndex, amCards, calcRowIndex)}px)
-                                    translateX(calc(${view?.IsContentActive ? translateIfSelected() : 0}% + ${this.translateOverflowX(realIndex, amCards)}px))
+                        transform: `translateY(${this.calculateTranslateY(!!view?.IsContentActive, index, amCards, calcRowIndex)}px)
+                                    translateX(calc(${view?.IsContentActive ? translateIfSelected() : 0}% + ${this.translateOverflowX(index, amCards)}px))
                                     rotate(${!view?.IsContentActive ? this.rotate(amCards, calcRowIndex) : 0}deg)
                                     scale(${view?.IsContentActive ? `${Math.min(hscale, vscale) * 100}%` : this._hoveredNodeIndex === index ? 1.1 : 1})`,
                     }} // prettier-ignore
-- 
cgit v1.2.3-70-g09d2


From efaa2991ac2670c73a81ab07f61c1626a4300507 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 7 Oct 2024 22:03:54 -0400
Subject: refactored imageUrlToBase64 into clientUtils.  lint fixes.

---
 src/ClientUtils.ts                                 | 21 ++++++++++++
 src/client/views/InkTranscription.tsx              | 36 +++++----------------
 .../views/collections/CollectionCardDeckView.tsx   | 23 ++------------
 .../collectionFreeForm/ImageLabelBox.tsx           | 22 +++++++------
 src/client/views/nodes/ComparisonBox.tsx           | 37 ++++++----------------
 src/client/views/nodes/LabelBox.tsx                |  3 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx | 21 ++----------
 src/client/views/smartdraw/SmartDrawHandler.tsx    |  4 +--
 8 files changed, 59 insertions(+), 108 deletions(-)

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

diff --git a/src/ClientUtils.ts b/src/ClientUtils.ts
index 972910071..e8165d5ab 100644
--- a/src/ClientUtils.ts
+++ b/src/ClientUtils.ts
@@ -655,6 +655,27 @@ export function dateRangeStrToDates(dateStr: string) {
     return { start: new Date(dateRangeParts[0]), end: new Date(dateRangeParts[1]) };
 }
 
+/**
+ * converts the image to base url formate
+ * @param imageUrl imageurl taken from the collection icon
+ */
+export async function imageUrlToBase64(imageUrl: string): Promise<string> {
+    try {
+        const response = await fetch(imageUrl);
+        const blob = await response.blob();
+
+        return new Promise((resolve, reject) => {
+            const reader = new FileReader();
+            reader.readAsDataURL(blob);
+            reader.onloadend = () => resolve(reader.result as string);
+            reader.onerror = error => reject(error);
+        });
+    } catch (error) {
+        console.error('Error:', error);
+        throw error;
+    }
+}
+
 function replaceCanvases(oldDiv: HTMLElement, newDiv: HTMLElement) {
     if (oldDiv.childNodes && newDiv) {
         for (let i = 0; i < oldDiv.childNodes.length; i++) {
diff --git a/src/client/views/InkTranscription.tsx b/src/client/views/InkTranscription.tsx
index 24d53a8c8..c5a9d3ba4 100644
--- a/src/client/views/InkTranscription.tsx
+++ b/src/client/views/InkTranscription.tsx
@@ -1,19 +1,19 @@
 import * as iink from 'iink-ts';
 import { action, observable } from 'mobx';
 import * as React from 'react';
+import { imageUrlToBase64 } from '../../ClientUtils';
+import { aggregateBounds } from '../../Utils';
 import { Doc, DocListCast } from '../../fields/Doc';
 import { InkData, InkField, InkTool } from '../../fields/InkField';
 import { Cast, DateCast, ImageCast, NumCast } from '../../fields/Types';
-import { aggregateBounds } from '../../Utils';
+import { ImageField, URLField } from '../../fields/URLField';
+import { gptHandwriting } from '../apis/gpt/GPT';
 import { DocumentType } from '../documents/DocumentTypes';
-import { CollectionFreeFormView, MarqueeView } from './collections/collectionFreeForm';
-import { InkingStroke } from './InkingStroke';
-import './InkTranscription.scss';
 import { Docs } from '../documents/Documents';
+import './InkTranscription.scss';
+import { InkingStroke } from './InkingStroke';
+import { CollectionFreeFormView, MarqueeView } from './collections/collectionFreeForm';
 import { DocumentView } from './nodes/DocumentView';
-import { ImageField } from '../../fields/URLField';
-import { gptHandwriting } from '../apis/gpt/GPT';
-import { URLField } from '../../fields/URLField';
 /**
  * Class component that handles inking in writing mode
  */
@@ -260,7 +260,7 @@ export class InkTranscription extends React.Component {
                     const hrefComplete = `${hrefParts[0]}_o.${hrefParts[1]}`;
                     let response;
                     try {
-                        const hrefBase64 = await this.imageUrlToBase64(hrefComplete);
+                        const hrefBase64 = await imageUrlToBase64(hrefComplete);
                         response = await gptHandwriting(hrefBase64);
                     } catch {
                         console.error('Error getting image');
@@ -291,26 +291,6 @@ export class InkTranscription extends React.Component {
         }
         return undefined;
     }
-    /**
-     * converts the image to base url formate
-     * @param imageUrl imageurl taken from the collection icon
-     */
-    imageUrlToBase64 = async (imageUrl: string): Promise<string> => {
-        try {
-            const response = await fetch(imageUrl);
-            const blob = await response.blob();
-
-            return new Promise((resolve, reject) => {
-                const reader = new FileReader();
-                reader.readAsDataURL(blob);
-                reader.onloadend = () => resolve(reader.result as string);
-                reader.onerror = error => reject(error);
-            });
-        } catch (error) {
-            console.error('Error:', error);
-            throw error;
-        }
-    };
 
     /**
      * Creates the ink grouping once the user leaves the writing mode.
diff --git a/src/client/views/collections/CollectionCardDeckView.tsx b/src/client/views/collections/CollectionCardDeckView.tsx
index 842724e39..c61b0b4dd 100644
--- a/src/client/views/collections/CollectionCardDeckView.tsx
+++ b/src/client/views/collections/CollectionCardDeckView.tsx
@@ -1,7 +1,8 @@
 import { IReactionDisposer, ObservableMap, action, computed, makeObservable, observable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
+import { computedFn } from 'mobx-utils';
 import * as React from 'react';
-import { ClientUtils, DashColor, returnFalse, returnZero } from '../../../ClientUtils';
+import { ClientUtils, DashColor, imageUrlToBase64, returnFalse, returnZero } from '../../../ClientUtils';
 import { Doc } from '../../../fields/Doc';
 import { DocData } from '../../../fields/DocSymbols';
 import { Id } from '../../../fields/FieldSymbols';
@@ -21,7 +22,6 @@ import { DocumentView } from '../nodes/DocumentView';
 import { GPTPopup, GPTPopupMode } from '../pdf/GPTPopup/GPTPopup';
 import './CollectionCardDeckView.scss';
 import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
-import { computedFn } from 'mobx-utils';
 
 enum cardSortings {
     Time = 'time',
@@ -52,23 +52,6 @@ export class CollectionCardView extends CollectionSubView() {
     @observable _maxRowCount = 10;
     @observable _docDraggedIndex: number = -1;
 
-    static imageUrlToBase64 = async (imageUrl: string): Promise<string> => {
-        try {
-            const response = await fetch(imageUrl);
-            const blob = await response.blob();
-
-            return new Promise((resolve, reject) => {
-                const reader = new FileReader();
-                reader.readAsDataURL(blob);
-                reader.onloadend = () => resolve(reader.result as string);
-                reader.onerror = error => reject(error);
-            });
-        } catch (error) {
-            console.error('Error:', error);
-            throw error;
-        }
-    };
-
     constructor(props: SubCollectionViewProps) {
         super(props);
         makeObservable(this);
@@ -462,7 +445,7 @@ export class CollectionCardView extends CollectionSubView() {
         const hrefParts = href.split('.');
         const hrefComplete = `${hrefParts[0]}_o.${hrefParts[1]}`;
         try {
-            const hrefBase64 = await CollectionCardView.imageUrlToBase64(hrefComplete);
+            const hrefBase64 = await imageUrlToBase64(hrefComplete);
             const response = await gptImageLabel(hrefBase64, 'Give three to five labels to describe this image.');
             image[DocData].description = response.trim();
             return response; // Return the response from gptImageLabel
diff --git a/src/client/views/collections/collectionFreeForm/ImageLabelBox.tsx b/src/client/views/collections/collectionFreeForm/ImageLabelBox.tsx
index 753685b97..583f2e656 100644
--- a/src/client/views/collections/collectionFreeForm/ImageLabelBox.tsx
+++ b/src/client/views/collections/collectionFreeForm/ImageLabelBox.tsx
@@ -6,6 +6,7 @@ import 'ldrs/ring';
 import { action, computed, makeObservable, observable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
 import React from 'react';
+import { imageUrlToBase64 } from '../../../../ClientUtils';
 import { Utils, numberRange } from '../../../../Utils';
 import { Doc, NumListCast, Opt } from '../../../../fields/Doc';
 import { DocData } from '../../../../fields/DocSymbols';
@@ -22,13 +23,13 @@ import { MainView } from '../../MainView';
 import { DocumentView } from '../../nodes/DocumentView';
 import { FieldView, FieldViewProps } from '../../nodes/FieldView';
 import { OpenWhere } from '../../nodes/OpenWhere';
-import { CollectionCardView } from '../CollectionCardDeckView';
 import './ImageLabelBox.scss';
 import { MarqueeOptionsMenu } from './MarqueeOptionsMenu';
 
 export class ImageInformationItem {}
 
 export class ImageLabelBoxData {
+    // eslint-disable-next-line no-use-before-define
     static _instance: ImageLabelBoxData;
     @observable _docs: Doc[] = [];
     @observable _labelGroups: string[] = [];
@@ -47,8 +48,8 @@ export class ImageLabelBoxData {
     };
 
     @action
-    addLabel = (label: string) => {
-        label = label.toUpperCase().trim();
+    addLabel = (labelIn: string) => {
+        const label = labelIn.toUpperCase().trim();
         if (label.length > 0) {
             if (!this._labelGroups.includes(label)) {
                 this._labelGroups = [...this._labelGroups, label.startsWith('#') ? label : '#' + label];
@@ -68,9 +69,10 @@ export class ImageLabelBox extends ViewBoxBaseComponent<FieldViewProps>() {
     public static LayoutString(fieldKey: string) {
         return FieldView.LayoutString(ImageLabelBox, fieldKey);
     }
+    // eslint-disable-next-line no-use-before-define
+    public static Instance: ImageLabelBox;
 
     private _dropDisposer?: DragManager.DragDropDisposer;
-    public static Instance: ImageLabelBox;
     private _inputRef = React.createRef<HTMLInputElement>();
     @observable _loading: boolean = false;
     private _currentLabel: string = '';
@@ -99,7 +101,7 @@ export class ImageLabelBox extends ViewBoxBaseComponent<FieldViewProps>() {
     }
     @observable _displayImageInformation: boolean = false;
 
-    constructor(props: any) {
+    constructor(props: FieldViewProps) {
         super(props);
         makeObservable(this);
         ring.register();
@@ -165,7 +167,7 @@ export class ImageLabelBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const imageInfos = this._selectedImages.map(async doc => {
             if (!doc[DocData].tags_chat) {
                 const [name, type] = ImageCast(doc[Doc.LayoutFieldKey(doc)]).url.href.split('.');
-                return CollectionCardView.imageUrlToBase64(`${name}_o.${type}`).then(hrefBase64 =>
+                return imageUrlToBase64(`${name}_o.${type}`).then(hrefBase64 =>
                 !hrefBase64 ? undefined : 
                     gptImageLabel(hrefBase64,'Give three labels to describe this image.').then(labels => 
                         ({ doc, labels }))) ; // prettier-ignore
@@ -178,13 +180,13 @@ export class ImageLabelBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
                 const labels = imageInfo.labels.split('\n');
                 labels.forEach(label => {
-                    label =
+                    const hashLabel =
                         '#' +
                         label
                             .replace(/^\d+\.\s*|-|f\*/, '')
                             .replace(/^#/, '')
                             .trim();
-                    (imageInfo.doc[DocData].tags_chat as List<string>).push(label);
+                    (imageInfo.doc[DocData].tags_chat as List<string>).push(hashLabel);
                 });
             }
         });
@@ -214,7 +216,7 @@ export class ImageLabelBox extends ViewBoxBaseComponent<FieldViewProps>() {
         // most similar one.
         this._selectedImages.forEach(doc => {
             const embedLists = numberRange((doc[DocData].tags_chat as List<string>).length).map(n => Array.from(NumListCast(doc[DocData][`tags_embedding_${n + 1}`])));
-            const bestEmbedScore = (embedding: Opt<number[]>) => Math.max(...embedLists.map((l, index) => (embedding && similarity(Array.from(embedding), l)!) || 0));
+            const bestEmbedScore = (embedding: Opt<number[]>) => Math.max(...embedLists.map(l => (embedding && similarity(Array.from(embedding), l)!) || 0));
             const {label: mostSimilarLabelCollect} = 
                 this._labelGroups.map(label => ({ label, similarityScore: bestEmbedScore(labelToEmbedding.get(label)) }))
                            .reduce((prev, cur) => cur.similarityScore < 0.3 || cur.similarityScore <= prev.similarityScore ? prev: cur,
@@ -243,7 +245,7 @@ export class ImageLabelBox extends ViewBoxBaseComponent<FieldViewProps>() {
         if (this._selectedImages.length === 0) {
             return (
                 <div className="searchBox-container" style={{ pointerEvents: 'all', color: SnappingManager.userColor, background: SnappingManager.userBackgroundColor }} ref={ele => this.createDropTarget(ele!)}>
-                    <p style={{ fontSize: 'large' }}>In order to classify and sort images, marquee select the desired images and press the 'Classify and Sort Images' button. Then, add the desired groups for the images to be put in.</p>
+                    <p style={{ fontSize: 'large' }}>In order to classify and sort images, marquee select the desired images and press the &apos;Classify and Sort Images&apos; button. Then, add the desired groups for the images to be put in.</p>
                 </div>
             );
         }
diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx
index c32bbc803..f7c478729 100644
--- a/src/client/views/nodes/ComparisonBox.tsx
+++ b/src/client/views/nodes/ComparisonBox.tsx
@@ -5,7 +5,7 @@ import { IReactionDisposer, action, computed, makeObservable, observable, reacti
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import ReactLoading from 'react-loading';
-import { returnFalse, returnNone, returnTrue, returnZero, setupMoveUpEvents } from '../../../ClientUtils';
+import { imageUrlToBase64, returnFalse, returnNone, returnTrue, returnZero, setupMoveUpEvents } from '../../../ClientUtils';
 import { emptyFunction } from '../../../Utils';
 import { Doc, Opt } from '../../../fields/Doc';
 import { DocData } from '../../../fields/DocSymbols';
@@ -365,7 +365,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
                 'Content-Type': 'application/json',
             },
         });
-        this.Document.phoneticTranscription = response.data['transcription'];
+        this.Document.phoneticTranscription = response.data.transcription;
     };
 
     /**
@@ -374,7 +374,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
      * @returns
      */
     getYouTubeVideoId = (url: string) => {
-        const regExp = /^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|\&v=|\?v=)([^#\&\?]*).*/;
+        const regExp = /^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|&v=|\?v=)([^#&?]*).*/;
         const match = url.match(regExp);
         return match && match[2].length === 11 ? match[2] : null;
     };
@@ -392,7 +392,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
                 'Content-Type': 'application/json',
             },
         });
-        return response.data['transcription'];
+        return response.data.transcription;
     };
 
     createFlashcardPile(collectionArr: Doc[], gpt: boolean) {
@@ -402,9 +402,9 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
             _layout_fitWidth: false,
             _layout_autoHeight: true,
         });
-        newCol['x'] = this.layoutDoc['x'];
-        newCol['y'] = NumCast(this.layoutDoc['y']) + 50;
-        newCol.type_collection = 'carousel';
+        newCol.x = this.layoutDoc.x;
+        newCol.y = NumCast(this.layoutDoc.y) + 50;
+        newCol.type_collection = CollectionViewType.Carousel as string;
         for (let i = 0; i < collectionArr.length; i++) {
             DocCast(collectionArr[i])[DocData].embedContainer = newCol;
         }
@@ -606,34 +606,17 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
                 _height: 150,
                 title: '--snapshot' + NumCast(this.layoutDoc._layout_currentTimecode) + ' image-',
             });
-            imageSnapshot['x'] = this.layoutDoc['x'];
-            imageSnapshot['y'] = this.layoutDoc['y'];
+            imageSnapshot.x = this.layoutDoc.x;
+            imageSnapshot.y = this.layoutDoc.y;
             return imageSnapshot;
         } catch (error) {
             console.log(error);
         }
     };
 
-    static imageUrlToBase64 = async (imageUrl: string): Promise<string> => {
-        try {
-            const response = await fetch(imageUrl);
-            const blob = await response.blob();
-
-            return new Promise((resolve, reject) => {
-                const reader = new FileReader();
-                reader.readAsDataURL(blob);
-                reader.onloadend = () => resolve(reader.result as string);
-                reader.onerror = error => reject(error);
-            });
-        } catch (error) {
-            console.error('Error:', error);
-            throw error;
-        }
-    };
-
     getImageDesc = async (u: string) => {
         try {
-            const hrefBase64 = await ComparisonBox.imageUrlToBase64(u);
+            const hrefBase64 = await imageUrlToBase64(u);
             const response = await gptImageLabel(hrefBase64, 'Answer the following question as a short flashcard response. Do not include a label.' + (this.dataDoc.text as RichTextField)?.Text);
 
             DocCast(this.dataDoc[this.props.fieldKey + '_0'])[DocData].text = response;
diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx
index 07c0a114a..696ba5697 100644
--- a/src/client/views/nodes/LabelBox.tsx
+++ b/src/client/views/nodes/LabelBox.tsx
@@ -1,5 +1,5 @@
 import { Property } from 'csstype';
-import { action, computed, makeObservable, observable } from 'mobx';
+import { action, computed, makeObservable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import * as textfit from 'textfit';
@@ -22,7 +22,6 @@ export class LabelBox extends ViewBoxBaseComponent<FieldViewProps>() {
     }
     private dropDisposer?: DragManager.DragDropDisposer;
     private _timeout: NodeJS.Timeout | undefined;
-    @observable private _editLabel = false;
     _divRef: HTMLDivElement | null = null;
 
     constructor(props: FieldViewProps) {
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 037b6315c..c89737e1e 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -13,7 +13,7 @@ import { EditorState, NodeSelection, Plugin, Selection, TextSelection, Transacti
 import { EditorView, NodeViewConstructor } from 'prosemirror-view';
 import * as React from 'react';
 import { BsMarkdownFill } from 'react-icons/bs';
-import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, ClientUtils, DivWidth, returnFalse, returnZero, setupMoveUpEvents, simMouseEvent, smoothScroll, StopEvent } from '../../../../ClientUtils';
+import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, ClientUtils, DivWidth, imageUrlToBase64, returnFalse, returnZero, setupMoveUpEvents, simMouseEvent, smoothScroll, StopEvent } from '../../../../ClientUtils';
 import { DateField } from '../../../../fields/DateField';
 import { CreateLinkToActiveAudio, Doc, DocListCast, Field, FieldType, Opt, StrListCast } from '../../../../fields/Doc';
 import { AclAdmin, AclAugment, AclEdit, AclSelfEdit, DocCss, DocData, ForceServerWrite, UpdatingFromServer } from '../../../../fields/DocSymbols';
@@ -1008,30 +1008,13 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
         // console.log('HI' + this.ProseRef?.getElementsByTagName('img'));
     };
 
-    static imageUrlToBase64 = async (imageUrl: string): Promise<string> => {
-        try {
-            const response = await fetch(imageUrl);
-            const blob = await response.blob();
-
-            return new Promise((resolve, reject) => {
-                const reader = new FileReader();
-                reader.readAsDataURL(blob);
-                reader.onloadend = () => resolve(reader.result as string);
-                reader.onerror = error => reject(error);
-            });
-        } catch (error) {
-            console.error('Error:', error);
-            throw error;
-        }
-    };
-
     getImageDesc = async (u: string) => {
         // if (StrCast(this.dataDoc.description)) return StrCast(this.dataDoc.description); // Return existing description
         // const { href } = (u as URLField).url;
         const hrefParts = u.split('.');
         const hrefComplete = `${hrefParts[0]}_o.${hrefParts[1]}`;
         try {
-            const hrefBase64 = await FormattedTextBox.imageUrlToBase64(u);
+            const hrefBase64 = await imageUrlToBase64(u);
             const response = await gptImageLabel(
                 hrefBase64,
                 'Make flashcards out of this text and image with each question and answer labeled as question and answer. Do not label each flashcard and do not include asterisks: ' + (this.dataDoc.text as RichTextField)?.Text
diff --git a/src/client/views/smartdraw/SmartDrawHandler.tsx b/src/client/views/smartdraw/SmartDrawHandler.tsx
index 75ef55060..b4635673c 100644
--- a/src/client/views/smartdraw/SmartDrawHandler.tsx
+++ b/src/client/views/smartdraw/SmartDrawHandler.tsx
@@ -7,6 +7,7 @@ import React from 'react';
 import { AiOutlineSend } from 'react-icons/ai';
 import ReactLoading from 'react-loading';
 import { INode, parse } from 'svgson';
+import { imageUrlToBase64 } from '../../../ClientUtils';
 import { unimplementedFunction } from '../../../Utils';
 import { Doc, DocListCast } from '../../../fields/Doc';
 import { DocData } from '../../../fields/DocSymbols';
@@ -19,7 +20,6 @@ import { undoable } from '../../util/UndoManager';
 import { SVGToBezier, SVGType } from '../../util/bezierFit';
 import { InkingStroke } from '../InkingStroke';
 import { ObservableReactComponent } from '../ObservableReactComponent';
-import { CollectionCardView } from '../collections/CollectionCardDeckView';
 import { MarqueeView } from '../collections/collectionFreeForm';
 import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, ActiveIsInkMask, DocumentView } from '../nodes/DocumentView';
 import './SmartDrawHandler.scss';
@@ -310,7 +310,7 @@ export class SmartDrawHandler extends ObservableReactComponent<object> {
         const hrefParts = href.split('.');
         const hrefComplete = `${hrefParts[0]}_o.${hrefParts[1]}`;
         try {
-            const hrefBase64 = await CollectionCardView.imageUrlToBase64(hrefComplete);
+            const hrefBase64 = await imageUrlToBase64(hrefComplete);
             const strokes = DocListCast(drawing[DocData].data);
             const coords: string[] = [];
             strokes.forEach((stroke, i) => {
-- 
cgit v1.2.3-70-g09d2


From b9fda86731a01ebfc3f21ebdd4eaf43a1c9eccc6 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 8 Oct 2024 23:07:52 -0400
Subject: yet another fix to cardView to make animations smooth - need to
 specifically target hidingDecorations without going using  IsResizing as a
 hack

---
 src/client/util/SnappingManager.ts                               | 3 +++
 src/client/views/DocumentDecorations.tsx                         | 9 +++------
 src/client/views/collections/CollectionCardDeckView.tsx          | 5 +++--
 .../collections/collectionFreeForm/CollectionFreeFormView.tsx    | 2 +-
 4 files changed, 10 insertions(+), 9 deletions(-)

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

diff --git a/src/client/util/SnappingManager.ts b/src/client/util/SnappingManager.ts
index 95ccc7735..5f6c7d9ac 100644
--- a/src/client/util/SnappingManager.ts
+++ b/src/client/util/SnappingManager.ts
@@ -28,6 +28,7 @@ export class SnappingManager {
     @observable _lastBtnId: string = '';
     @observable _propertyWid: number = 0;
     @observable _printToConsole: boolean = false;
+    @observable _hideDecorations: boolean = false;
 
     private constructor() {
         SnappingManager._manager = this;
@@ -59,6 +60,7 @@ export class SnappingManager {
     public static get LastPressedBtn() { return this.Instance._lastBtnId; } // prettier-ignore
     public static get PropertiesWidth(){ return this.Instance._propertyWid; } // prettier-ignore
     public static get PrintToConsole() { return this.Instance._printToConsole; } // prettier-ignore
+    public static get HideDecorations(){ return this.Instance._hideDecorations; } // prettier-ignore
 
     public static SetLongPress      = (press: boolean) => runInAction(() => {this.Instance._longPress = press}); // prettier-ignore
     public static SetShiftKey       = (down: boolean) => runInAction(() => {this.Instance._shiftKey = down}); // prettier-ignore
@@ -75,6 +77,7 @@ export class SnappingManager {
     public static SetLastPressedBtn = (id:string)      =>runInAction(() => {this.Instance._lastBtnId = id}); // prettier-ignore
     public static SetPropertiesWidth= (wid:number)     =>runInAction(() => {this.Instance._propertyWid = wid}); // prettier-ignore
     public static SetPrintToConsole = (state:boolean)  =>runInAction(() => {this.Instance._printToConsole = state}); // prettier-ignore
+    public static SetHideDecorations= (state:boolean)  =>runInAction(() => {this.Instance._hideDecorations = state}); // prettier-ignore
 
     public static userColor: string | undefined;
     public static userVariantColor: string | undefined;
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 1c0d51e17..62f2de776 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -63,7 +63,6 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
     @observable private _accumulatedTitle = '';
     @observable private _titleControlString: string = '$title';
     @observable private _editingTitle = false;
-    @observable private _hidden = false;
     @observable private _isRotating: boolean = false;
     @observable private _isRounding: boolean = false;
     @observable private _showLayoutAcl: boolean = false;
@@ -202,16 +201,14 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
         dragData.removeDocument = dragDocView._props.removeDocument;
         dragData.isDocDecorationMove = true;
         dragData.canEmbed = dragTitle;
-        this._hidden = true;
+        SnappingManager.SetHideDecorations(true);
         DragManager.StartDocumentDrag(
             DocumentView.Selected().map(dv => dv.ContentDiv!),
             dragData,
             e.x,
             e.y,
             {
-                dragComplete: action(() => {
-                    this._hidden = false;
-                }),
+                dragComplete: () => SnappingManager.SetHideDecorations(false),
                 hideSource: true,
             }
         );
@@ -653,7 +650,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
         this._forceRender;
         const { b, r, x, y } = this.Bounds;
         const seldocview = DocumentView.Selected().lastElement();
-        if (SnappingManager.IsDragging || r - x < 1 || x === Number.MAX_VALUE || !seldocview || this._hidden || isNaN(r) || isNaN(b) || isNaN(x) || isNaN(y)) {
+        if (SnappingManager.IsDragging || r - x < 1 || x === Number.MAX_VALUE || !seldocview || SnappingManager.HideDecorations || isNaN(r) || isNaN(b) || isNaN(x) || isNaN(y)) {
             setTimeout(
                 action(() => {
                     this._editingTitle = false;
diff --git a/src/client/views/collections/CollectionCardDeckView.tsx b/src/client/views/collections/CollectionCardDeckView.tsx
index 0c2bcc580..92c69c3cf 100644
--- a/src/client/views/collections/CollectionCardDeckView.tsx
+++ b/src/client/views/collections/CollectionCardDeckView.tsx
@@ -22,6 +22,7 @@ import { GPTPopup, GPTPopupMode } from '../pdf/GPTPopup/GPTPopup';
 import './CollectionCardDeckView.scss';
 import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
 import { computedFn } from 'mobx-utils';
+import { DocumentDecorations } from '../DocumentDecorations';
 
 enum cardSortings {
     Time = 'time',
@@ -566,10 +567,10 @@ export class CollectionCardView extends CollectionSubView() {
         } else {
             // otherwise, turn off documentDecorations becase we're in a selection transition and want to avoid artifacts.
             // Turn them back on when the animation has completed and the render and backend structures are in synch
-            SnappingManager.SetIsResizing(doc[Id]);
+            SnappingManager.SetHideDecorations(true);
             setTimeout(
                 action(() => {
-                    SnappingManager.SetIsResizing(undefined);
+                    SnappingManager.SetHideDecorations(false);
                     this._forceChildXf++;
                 }),
                 1000
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 0cc63d632..d8678eebc 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -138,7 +138,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         return this._props.layoutEngine?.() || StrCast(this.layoutDoc._layoutEngine);
     }
     @computed get childPointerEvents() {
-        return SnappingManager.IsResizing
+        return falseSnappingManager.IsResizing
             ? 'none'
             : (this._props.childPointerEvents?.() ??
                   (this._props.viewDefDivClick || //
-- 
cgit v1.2.3-70-g09d2


From c97d849a56d4642d9eb750beccb748b7d8ac15bd Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 8 Oct 2024 23:43:43 -0400
Subject: extended flashcard UI to cardDecks

---
 .../views/collections/CollectionCardDeckView.scss  | 12 ++++
 .../views/collections/CollectionCardDeckView.tsx   | 66 +++++++++++++++++++---
 .../views/collections/FlashcardPracticeUI.scss     |  6 ++
 .../views/collections/FlashcardPracticeUI.tsx      |  8 +--
 4 files changed, 81 insertions(+), 11 deletions(-)

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

diff --git a/src/client/views/collections/CollectionCardDeckView.scss b/src/client/views/collections/CollectionCardDeckView.scss
index d1731c244..0520e38d4 100644
--- a/src/client/views/collections/CollectionCardDeckView.scss
+++ b/src/client/views/collections/CollectionCardDeckView.scss
@@ -48,3 +48,15 @@
 .card-item-active {
     z-index: 100;
 }
+
+.collectionCardDeckView-flashcards {
+    width: 100%;
+    height: 100%;
+    position: absolute;
+    top: 0;
+    left: 0;
+    display: flex;
+    transform-origin: top left;
+    pointer-events: none;
+    z-index: 100;
+}
diff --git a/src/client/views/collections/CollectionCardDeckView.tsx b/src/client/views/collections/CollectionCardDeckView.tsx
index 5d39dc1ca..7272b22e2 100644
--- a/src/client/views/collections/CollectionCardDeckView.tsx
+++ b/src/client/views/collections/CollectionCardDeckView.tsx
@@ -22,6 +22,7 @@ import { DocumentView } from '../nodes/DocumentView';
 import { GPTPopup, GPTPopupMode } from '../pdf/GPTPopup/GPTPopup';
 import './CollectionCardDeckView.scss';
 import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
+import { FlashcardPracticeUI } from './FlashcardPracticeUI';
 
 enum cardSortings {
     Time = 'time',
@@ -46,6 +47,8 @@ export class CollectionCardView extends CollectionSubView() {
     private _textToDoc = new Map<string, Doc>();
     private _dropped = false; // set when a card doc has just moved and the drop method has been called - prevents the pointerUp method from hiding doc decorations (which needs to be done when clicking on a card to animate it to front/center)
 
+    _sideBtnWidth = 35;
+    @observable _filterFunc: ((doc: Doc) => boolean) | undefined = undefined;
     @observable _forceChildXf = 0;
     @observable _hoveredNodeIndex = -1;
     @observable _docRefs = new ObservableMap<Doc, DocumentView>();
@@ -117,7 +120,7 @@ export class CollectionCardView extends CollectionSubView() {
      * The child documents to be rendered-- everything other than ink/link docs (which are marks as being svg's)
      */
     @computed get childDocsWithoutLinks() {
-        return this.childDocs.filter(l => !l.layout_isSvg);
+        return this.childDocs.filter(l => !l.layout_isSvg).filter(doc => !this._filterFunc?.(doc));
     }
 
     /**
@@ -565,11 +568,7 @@ export class CollectionCardView extends CollectionSubView() {
      */
     @computed get renderCards() {
         if (!this.childDocsWithoutLinks.length) {
-            return (
-                <span className="no-card-span" style={{ width: ` ${this._props.PanelWidth()}px`, height: ` ${this._props.PanelHeight()}px` }}>
-                    Sorry ! There are no cards in this group
-                </span>
-            );
+            return null;
         }
 
         // Map sorted documents to their rendered components
@@ -611,6 +610,34 @@ export class CollectionCardView extends CollectionSubView() {
         });
     }
 
+    contentScreenToLocalXf = () => this._props.ScreenToLocalTransform().scale(this._props.NativeDimScaling?.() || 1);
+    docViewProps = () => ({
+        ...this._props, //
+        isDocumentActive: this._props.childDocumentsActive?.() ? this._props.isDocumentActive : this._props.isContentActive,
+        isContentActive: this.isChildContentActive,
+        ScreenToLocalTransform: this.contentScreenToLocalXf,
+    });
+    carouselItemsFunc = () => this.childDocsWithoutLinks;
+    @action setFilterFunc = (func?: (doc: Doc) => boolean) => { this._filterFunc = func; }; // prettier-ignore
+    answered = (correct: boolean) => !correct || !this.curDoc();
+    curDoc = () => this.sortedDocs.find(doc => DocumentView.getDocumentView(doc, this.DocumentView?.())?.IsSelected);
+    /**
+     * How much the content of the carousel view is being scaled based on its nesting and its fit-to-width settings
+     */
+    @computed get contentScaling() { return this.ScreenToLocalBoxXf().Scale * (this._props.NativeDimScaling?.() ?? 1); } // prettier-ignore
+
+    /**
+     * The maximum size a UI widget can be scaled so that it won't be bigger in screen pixels than its normal 35 pixel size.
+     */
+    @computed get maxWidgetScale() {
+        const maxWidgetSize = Math.min(this._sideBtnWidth * this.contentScaling, 0.1 * NumCast(this.layoutDoc.width, 1));
+        return Math.max(maxWidgetSize / this._sideBtnWidth, 1);
+    }
+    /**
+     * How much to reactively scale a UI element so that it is as big as it can be (up to its normal 35pixel size) without being too big for the Doc content
+     */
+    @computed get uiBtnScaleTransform() {  return this.maxWidgetScale * Math.min(1, this.contentScaling); } // prettier-ignore
+
     render() {
         const isEmpty = this.childDocsWithoutLinks.length === 0;
 
@@ -629,10 +656,35 @@ export class CollectionCardView extends CollectionSubView() {
                     className="card-wrapper"
                     style={{
                         ...(!isEmpty && { transform: `scale(${1 / this.fitContentScale})` }),
-                        ...(!isEmpty && { height: `${100 * this.fitContentScale}%` }),
+                        ...{ height: `${100 * (isEmpty ? 1 : this.fitContentScale)}%` },
+                        ...{ width: `${100 * (isEmpty ? 1 : this.fitContentScale)}%` },
                         gridAutoRows: `${100 / this.numRows}%`,
                     }}>
                     {this.renderCards}
+                    <div
+                        className="collectionCardDeckView-flashcards"
+                        style={{
+                            transform: `scale(${this.fitContentScale || 1})`,
+                            width: `${100 / (this.fitContentScale || 1)}%`,
+                            height: `${100 / (this.fitContentScale || 1)}%`,
+                            pointerEvents: !this.childDocsWithoutLinks.length ? 'unset' : undefined,
+                        }}>
+                        <FlashcardPracticeUI
+                            setFilterFunc={this.setFilterFunc}
+                            fieldKey={this.fieldKey}
+                            sideBtnWidth={this._sideBtnWidth}
+                            carouselItems={this.carouselItemsFunc}
+                            childDocs={this.childDocs}
+                            advance={this.answered}
+                            curDoc={this.curDoc}
+                            layoutDoc={this.layoutDoc}
+                            maxWidgetScale={this.maxWidgetScale}
+                            uiBtnScaleTransform={this.uiBtnScaleTransform}
+                            ScreenToLocalBoxXf={this.ScreenToLocalBoxXf}
+                            renderDepth={this._props.renderDepth}
+                            docViewProps={this.docViewProps}
+                        />
+                    </div>
                 </div>
             </div>
         );
diff --git a/src/client/views/collections/FlashcardPracticeUI.scss b/src/client/views/collections/FlashcardPracticeUI.scss
index 53c26ad34..2f99500f8 100644
--- a/src/client/views/collections/FlashcardPracticeUI.scss
+++ b/src/client/views/collections/FlashcardPracticeUI.scss
@@ -13,6 +13,11 @@
         color: white;
     }
 }
+.FlashcardPracticeUI-practice {
+    position: absolute;
+    width: 100%;
+    pointer-events: all;
+}
 .FlashcardPracticeUI-remove {
     left: 52%;
 }
@@ -30,6 +35,7 @@
     transform-origin: top left;
     border-radius: 5px;
     color: rgba(255, 255, 255, 0.5);
+    pointer-events: all;
     background: rgba(0, 0, 0, 0.1);
     .FlashcardPracticeUI-practiceModes {
         width: 100%;
diff --git a/src/client/views/collections/FlashcardPracticeUI.tsx b/src/client/views/collections/FlashcardPracticeUI.tsx
index 032a405bf..072a2edef 100644
--- a/src/client/views/collections/FlashcardPracticeUI.tsx
+++ b/src/client/views/collections/FlashcardPracticeUI.tsx
@@ -25,7 +25,7 @@ interface PracticeUIProps {
     layoutDoc: Doc;
     carouselItems: () => Doc[];
     childDocs: Doc[];
-    curDoc: () => Doc;
+    curDoc: () => Doc | undefined;
     advance: (correct: boolean) => void;
     renderDepth: number;
     sideBtnWidth: number;
@@ -100,8 +100,8 @@ export class FlashcardPracticeUI extends ObservableReactComponent<PracticeUIProp
             this._props.advance?.(val === practiceVal.CORRECT);
         };
 
-        return this.practiceMode == practiceMode.PRACTICE ? (
-            <div style={{ transform: `scale(${this._props.uiBtnScaleTransform})`, bottom: `${this._props.practiceBtnOffset ?? this._props.sideBtnWidth}px`, height: `${this._props.sideBtnWidth}px`, position: 'absolute', width: `100%` }}>
+        return this.practiceMode == practiceMode.PRACTICE && this._props.curDoc() ? (
+            <div className="FlashcardPracticeUI-practice" style={{ transform: `scale(${this._props.uiBtnScaleTransform})`, bottom: `${this._props.practiceBtnOffset ?? this._props.sideBtnWidth}px`, height: `${this._props.sideBtnWidth}px` }}>
                 <Tooltip title="Incorrect. View again later.">
                     <div key="remove" className="FlashcardPracticeUI-remove" onClick={e => setPracticeVal(e, practiceVal.MISSED)}>
                         <FontAwesomeIcon icon="xmark" color="red" size="1x" />
@@ -119,7 +119,7 @@ export class FlashcardPracticeUI extends ObservableReactComponent<PracticeUIProp
         const setColor = (mode: practiceMode) => (StrCast(this.practiceMode) === mode ? 'white' : 'light gray');
         const togglePracticeMode = (mode: practiceMode) => this.setPracticeMode(mode === this.practiceMode ? undefined : mode);
 
-        return !this._props.curDoc()?._layout_isFlashcard ? null : (
+        return !this._props.childDocs.some(doc => doc._layout_isFlashcard) ? null : (
             <div
                 className="FlashcardPracticeUI-practiceModes"
                 style={{
-- 
cgit v1.2.3-70-g09d2


From 01db98ebdd02729729222bdd20ab65b57cbbe94c Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 9 Oct 2024 13:27:56 -0400
Subject: more refactoring to of collection flashcards into CollectioSubView to
 simplify using it in diferent collection views.

---
 .../views/collections/CollectionCardDeckView.scss  | 13 +---
 .../views/collections/CollectionCardDeckView.tsx   | 81 ++++++----------------
 .../views/collections/CollectionCarousel3DView.tsx | 72 +++++--------------
 .../views/collections/CollectionCarouselView.scss  |  1 +
 .../views/collections/CollectionCarouselView.tsx   | 53 ++------------
 src/client/views/collections/CollectionSubView.tsx | 51 +++++++++++++-
 .../views/collections/FlashcardPracticeUI.scss     |  2 +-
 .../views/collections/FlashcardPracticeUI.tsx      | 27 ++++----
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  2 +-
 src/client/views/nodes/ComparisonBox.tsx           | 18 ++---
 10 files changed, 113 insertions(+), 207 deletions(-)

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

diff --git a/src/client/views/collections/CollectionCardDeckView.scss b/src/client/views/collections/CollectionCardDeckView.scss
index 0520e38d4..0637cd4e9 100644
--- a/src/client/views/collections/CollectionCardDeckView.scss
+++ b/src/client/views/collections/CollectionCardDeckView.scss
@@ -6,6 +6,7 @@
     position: relative;
     background-color: white;
     overflow: hidden;
+    display: flex;
 
     button {
         border-radius: 50%;
@@ -48,15 +49,3 @@
 .card-item-active {
     z-index: 100;
 }
-
-.collectionCardDeckView-flashcards {
-    width: 100%;
-    height: 100%;
-    position: absolute;
-    top: 0;
-    left: 0;
-    display: flex;
-    transform-origin: top left;
-    pointer-events: none;
-    z-index: 100;
-}
diff --git a/src/client/views/collections/CollectionCardDeckView.tsx b/src/client/views/collections/CollectionCardDeckView.tsx
index 7272b22e2..8f351d8a7 100644
--- a/src/client/views/collections/CollectionCardDeckView.tsx
+++ b/src/client/views/collections/CollectionCardDeckView.tsx
@@ -22,7 +22,6 @@ import { DocumentView } from '../nodes/DocumentView';
 import { GPTPopup, GPTPopupMode } from '../pdf/GPTPopup/GPTPopup';
 import './CollectionCardDeckView.scss';
 import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
-import { FlashcardPracticeUI } from './FlashcardPracticeUI';
 
 enum cardSortings {
     Time = 'time',
@@ -47,8 +46,6 @@ export class CollectionCardView extends CollectionSubView() {
     private _textToDoc = new Map<string, Doc>();
     private _dropped = false; // set when a card doc has just moved and the drop method has been called - prevents the pointerUp method from hiding doc decorations (which needs to be done when clicking on a card to animate it to front/center)
 
-    _sideBtnWidth = 35;
-    @observable _filterFunc: ((doc: Doc) => boolean) | undefined = undefined;
     @observable _forceChildXf = 0;
     @observable _hoveredNodeIndex = -1;
     @observable _docRefs = new ObservableMap<Doc, DocumentView>();
@@ -119,15 +116,15 @@ export class CollectionCardView extends CollectionSubView() {
     /**
      * The child documents to be rendered-- everything other than ink/link docs (which are marks as being svg's)
      */
-    @computed get childDocsWithoutLinks() {
-        return this.childDocs.filter(l => !l.layout_isSvg).filter(doc => !this._filterFunc?.(doc));
+    @computed get childCards() {
+        return this.childLayoutPairs.filter(pair => !pair.layout.layout_isSvg);
     }
 
     /**
      * how much to scale down the contents of the view so that everything will fit
      */
     @computed get fitContentScale() {
-        const length = Math.min(this.childDocsWithoutLinks.length, this._maxRowCount);
+        const length = Math.min(this.childCards.length, this._maxRowCount);
         return (this.childPanelWidth() * length) / this._props.PanelWidth();
     }
 
@@ -280,7 +277,12 @@ export class CollectionCardView extends CollectionSubView() {
     );
 
     @computed get sortedDocs() {
-        return this.sort(this.childDocsWithoutLinks, this.cardSort, BoolCast(this.Document.cardSort_isDesc), this._docDraggedIndex);
+        return this.sort(
+            this.childCards.map(card => card.layout),
+            this.cardSort,
+            BoolCast(this.Document.cardSort_isDesc),
+            this._docDraggedIndex
+        );
     }
 
     /**
@@ -428,12 +430,14 @@ export class CollectionCardView extends CollectionSubView() {
                 default:                return StrCast(doc.title);
             } // prettier-ignore
         };
-        const docTextPromises = this.childDocsWithoutLinks.map(async doc => {
-            const docText = (await docToText(doc)) ?? '';
-            doc.gptInputText = docText;
-            this._textToDoc.set(docText.replace(/\n/g, ' ').trim(), doc);
-            return `======${docText.replace(/\n/g, ' ').trim()}======`;
-        });
+        const docTextPromises = this.childCards
+            .map(pair => pair.layout)
+            .map(async doc => {
+                const docText = (await docToText(doc)) ?? '';
+                doc.gptInputText = docText;
+                this._textToDoc.set(docText.replace(/\n/g, ' ').trim(), doc);
+                return `======${docText.replace(/\n/g, ' ').trim()}======`;
+            });
         return Promise.all<string>(docTextPromises);
     };
 
@@ -567,7 +571,7 @@ export class CollectionCardView extends CollectionSubView() {
      * Actually renders all the cards
      */
     @computed get renderCards() {
-        if (!this.childDocsWithoutLinks.length) {
+        if (!this.childCards.length) {
             return null;
         }
 
@@ -611,36 +615,16 @@ export class CollectionCardView extends CollectionSubView() {
     }
 
     contentScreenToLocalXf = () => this._props.ScreenToLocalTransform().scale(this._props.NativeDimScaling?.() || 1);
+    curDoc = () => this.childCards.find(card => DocumentView.getDocumentView(card.layout, this.DocumentView?.())?.IsSelected)?.layout;
     docViewProps = () => ({
         ...this._props, //
         isDocumentActive: this._props.childDocumentsActive?.() ? this._props.isDocumentActive : this._props.isContentActive,
         isContentActive: this.isChildContentActive,
         ScreenToLocalTransform: this.contentScreenToLocalXf,
     });
-    carouselItemsFunc = () => this.childDocsWithoutLinks;
-    @action setFilterFunc = (func?: (doc: Doc) => boolean) => { this._filterFunc = func; }; // prettier-ignore
-    answered = (correct: boolean) => !correct || !this.curDoc();
-    curDoc = () => this.sortedDocs.find(doc => DocumentView.getDocumentView(doc, this.DocumentView?.())?.IsSelected);
-    /**
-     * How much the content of the carousel view is being scaled based on its nesting and its fit-to-width settings
-     */
-    @computed get contentScaling() { return this.ScreenToLocalBoxXf().Scale * (this._props.NativeDimScaling?.() ?? 1); } // prettier-ignore
-
-    /**
-     * The maximum size a UI widget can be scaled so that it won't be bigger in screen pixels than its normal 35 pixel size.
-     */
-    @computed get maxWidgetScale() {
-        const maxWidgetSize = Math.min(this._sideBtnWidth * this.contentScaling, 0.1 * NumCast(this.layoutDoc.width, 1));
-        return Math.max(maxWidgetSize / this._sideBtnWidth, 1);
-    }
-    /**
-     * How much to reactively scale a UI element so that it is as big as it can be (up to its normal 35pixel size) without being too big for the Doc content
-     */
-    @computed get uiBtnScaleTransform() {  return this.maxWidgetScale * Math.min(1, this.contentScaling); } // prettier-ignore
 
     render() {
-        const isEmpty = this.childDocsWithoutLinks.length === 0;
-
+        const isEmpty = this.childCards.length === 0;
         return (
             <div
                 className="collectionCardView-outer"
@@ -661,31 +645,8 @@ export class CollectionCardView extends CollectionSubView() {
                         gridAutoRows: `${100 / this.numRows}%`,
                     }}>
                     {this.renderCards}
-                    <div
-                        className="collectionCardDeckView-flashcards"
-                        style={{
-                            transform: `scale(${this.fitContentScale || 1})`,
-                            width: `${100 / (this.fitContentScale || 1)}%`,
-                            height: `${100 / (this.fitContentScale || 1)}%`,
-                            pointerEvents: !this.childDocsWithoutLinks.length ? 'unset' : undefined,
-                        }}>
-                        <FlashcardPracticeUI
-                            setFilterFunc={this.setFilterFunc}
-                            fieldKey={this.fieldKey}
-                            sideBtnWidth={this._sideBtnWidth}
-                            carouselItems={this.carouselItemsFunc}
-                            childDocs={this.childDocs}
-                            advance={this.answered}
-                            curDoc={this.curDoc}
-                            layoutDoc={this.layoutDoc}
-                            maxWidgetScale={this.maxWidgetScale}
-                            uiBtnScaleTransform={this.uiBtnScaleTransform}
-                            ScreenToLocalBoxXf={this.ScreenToLocalBoxXf}
-                            renderDepth={this._props.renderDepth}
-                            docViewProps={this.docViewProps}
-                        />
-                    </div>
                 </div>
+                {this.flashCardUI(this.curDoc, this.docViewProps)}
             </div>
         );
     }
diff --git a/src/client/views/collections/CollectionCarousel3DView.tsx b/src/client/views/collections/CollectionCarousel3DView.tsx
index 3bcf3450f..9ccac0e0f 100644
--- a/src/client/views/collections/CollectionCarousel3DView.tsx
+++ b/src/client/views/collections/CollectionCarousel3DView.tsx
@@ -1,5 +1,5 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, makeObservable, observable } from 'mobx';
+import { computed, makeObservable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { returnZero } from '../../../ClientUtils';
@@ -15,26 +15,19 @@ import { DocumentView } from '../nodes/DocumentView';
 import { FocusViewOptions } from '../nodes/FocusViewOptions';
 import './CollectionCarousel3DView.scss';
 import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
-import { FlashcardPracticeUI } from './FlashcardPracticeUI';
 
 // eslint-disable-next-line @typescript-eslint/no-require-imports
 const { CAROUSEL3D_CENTER_SCALE, CAROUSEL3D_SIDE_SCALE, CAROUSEL3D_TOP } = require('../global/globalCssVariables.module.scss');
 
 @observer
 export class CollectionCarousel3DView extends CollectionSubView() {
-    @computed get scrollSpeed() {
-        return this.layoutDoc._autoScrollSpeed ? NumCast(this.layoutDoc._autoScrollSpeed) : 1000; // default scroll speed
-    }
-    _sideBtnWidth = 35;
-    @observable _filterFunc: ((doc: Doc) => boolean) | undefined = undefined;
+    private _dropDisposer?: DragManager.DragDropDisposer;
 
     constructor(props: SubCollectionViewProps) {
         super(props);
         makeObservable(this);
     }
 
-    private _dropDisposer?: DragManager.DragDropDisposer;
-
     componentWillUnmount() {
         this._dropDisposer?.();
     }
@@ -46,8 +39,11 @@ export class CollectionCarousel3DView extends CollectionSubView() {
         }
     };
 
+    @computed get scrollSpeed() {
+        return this.layoutDoc._autoScrollSpeed ? NumCast(this.layoutDoc._autoScrollSpeed) : 1000; // default scroll speed
+    }
     @computed get carouselItems() {
-        return this.childLayoutPairs.filter(pair => pair.layout.type !== DocumentType.LINK).filter(pair => !this._filterFunc?.(pair.layout));
+        return this.childLayoutPairs.filter(pair => !pair.layout.layout_isSvg);
     }
 
     centerScale = Number(CAROUSEL3D_CENTER_SCALE);
@@ -86,11 +82,11 @@ export class CollectionCarousel3DView extends CollectionSubView() {
 
     @computed get content() {
         const currentIndex = NumCast(this.layoutDoc._carousel_index);
-        const displayDoc = (childPair: { layout: Doc; data: Doc }, dxf: () => Transform) => (
+        const displayDoc = (child: Doc, dxf: () => Transform) => (
             <DocumentView
                 {...this._props}
-                Document={childPair.layout}
-                TemplateDataDocument={childPair.data}
+                Document={child}
+                TemplateDataDocument={undefined}
                 // suppressSetHeight={true}
                 NativeWidth={returnZero}
                 NativeHeight={returnZero}
@@ -109,9 +105,9 @@ export class CollectionCarousel3DView extends CollectionSubView() {
             />
         );
 
-        return this.carouselItems.map((childPair, index) => (
-            <div key={childPair.layout[Id]} className={`collectionCarousel3DView-item${index === currentIndex ? '-active' : ''} ${index}`} style={{ width: this.panelWidth() }}>
-                {displayDoc(childPair, index < currentIndex ? this.childScreenLeftToLocal : index === currentIndex ? this.childCenterScreenToLocal : this.childScreenRightToLocal)}
+        return this.carouselItems.map((child, index) => (
+            <div key={child.layout[Id]} className={`collectionCarousel3DView-item${index === currentIndex ? '-active' : ''} ${index}`} style={{ width: this.panelWidth() }}>
+                {displayDoc(child.layout, index < currentIndex ? this.childScreenLeftToLocal : index === currentIndex ? this.childCenterScreenToLocal : this.childScreenRightToLocal)}
             </div>
         ));
     }
@@ -191,35 +187,14 @@ export class CollectionCarousel3DView extends CollectionSubView() {
         return this.panelWidth() * (1 - index);
     }
 
-    /**
-     * How much the content of the carousel view is being scaled based on its nesting and its fit-to-width settings
-     */
-    @computed get contentScaling() { return this.ScreenToLocalBoxXf().Scale * (this._props.NativeDimScaling?.() ?? 1); } // prettier-ignore
-
-    /**
-     * The maximum size a UI widget can be scaled so that it won't be bigger in screen pixels than its normal 35 pixel size.
-     */
-    @computed get maxWidgetScale() {
-        const maxWidgetSize = Math.min(this._sideBtnWidth * this.contentScaling, 0.1 * NumCast(this.layoutDoc.width, 1));
-        return Math.max(maxWidgetSize / this._sideBtnWidth, 1);
-    }
-    /**
-     * How much to reactively scale a UI element so that it is as big as it can be (up to its normal 35pixel size) without being too big for the Doc content
-     */
-    @computed get uiBtnScaleTransform() {  return this.maxWidgetScale * Math.min(1, this.contentScaling); } // prettier-ignore
-    screenXPadding = () => (this.uiBtnScaleTransform * this._sideBtnWidth - NumCast(this.layoutDoc.xMargin)) / this._props.ScreenToLocalTransform().Scale;
-
+    curDoc = () => this.carouselItems[NumCast(this.layoutDoc._carousel_index)]?.layout;
+    answered = (correct: boolean) => (!correct || !this.curDoc()) && this.changeSlide(1);
     docViewProps = () => ({
         ...this._props, //
         isDocumentActive: this._props.childDocumentsActive?.() ? this._props.isDocumentActive : this._props.isContentActive,
         isContentActive: this.isChildContentActive,
         ScreenToLocalTransform: this.contentScreenToLocalXf,
     });
-    carouselItemsFunc = () => this.carouselItems.map(pair => pair.layout);
-    @action setFilterFunc = (func?: (doc: Doc) => boolean) => { this._filterFunc = func; }; // prettier-ignore
-    answered = (correct: boolean) => (!correct || !this.curDoc()) && this.changeSlide(1);
-    curDoc = () => this.carouselItems[NumCast(this.layoutDoc._carousel_index)]?.layout;
-
     render() {
         return (
             <div
@@ -233,25 +208,10 @@ export class CollectionCarousel3DView extends CollectionSubView() {
                     {this.content}
                 </div>
                 {this.buttons}
-                <div className="dot-bar" style={{ transform: `scale(${this.uiBtnScaleTransform})` }}>
+                <div className="dot-bar" style={{ transform: `scale(${this.uiBtnScaling})` }}>
                     {this.dots}
                 </div>
-                <FlashcardPracticeUI
-                    setFilterFunc={this.setFilterFunc}
-                    fieldKey={this.fieldKey}
-                    sideBtnWidth={this._sideBtnWidth}
-                    carouselItems={this.carouselItemsFunc}
-                    childDocs={this.childDocs}
-                    advance={this.answered}
-                    curDoc={this.curDoc}
-                    practiceBtnOffset={this._sideBtnWidth * 4}
-                    layoutDoc={this.layoutDoc}
-                    maxWidgetScale={this.maxWidgetScale}
-                    uiBtnScaleTransform={this.uiBtnScaleTransform}
-                    ScreenToLocalBoxXf={this.ScreenToLocalBoxXf}
-                    renderDepth={this._props.renderDepth}
-                    docViewProps={this.docViewProps}
-                />
+                {this.flashCardUI(this.curDoc, this.docViewProps, this.answered)}
             </div>
         );
     }
diff --git a/src/client/views/collections/CollectionCarouselView.scss b/src/client/views/collections/CollectionCarouselView.scss
index 757072453..544b3e262 100644
--- a/src/client/views/collections/CollectionCarouselView.scss
+++ b/src/client/views/collections/CollectionCarouselView.scss
@@ -2,6 +2,7 @@
     height: 100%;
     position: relative;
     overflow: hidden;
+    display: flex;
     .collectionCarouselView-caption {
         height: 50;
         display: inline-block;
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index 64ddaac79..538eba356 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -5,7 +5,6 @@ import * as React from 'react';
 import { StopEvent, returnOne, returnZero } from '../../../ClientUtils';
 import { Doc, Opt } from '../../../fields/Doc';
 import { BoolCast, DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
-import { DocumentType } from '../../documents/DocumentTypes';
 import { DragManager } from '../../util/DragManager';
 import { StyleProp } from '../StyleProp';
 import { DocumentView } from '../nodes/DocumentView';
@@ -13,15 +12,12 @@ import { FieldViewProps } from '../nodes/FieldView';
 import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
 import './CollectionCarouselView.scss';
 import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
-import { FlashcardPracticeUI } from './FlashcardPracticeUI';
 
 @observer
 export class CollectionCarouselView extends CollectionSubView() {
     private _dropDisposer?: DragManager.DragDropDisposer;
 
     _fadeTimer: NodeJS.Timeout | undefined;
-    _sideBtnWidth = 35;
-    @observable _filterFunc: ((doc: Doc) => boolean) | undefined = undefined;
     @observable _last_index = this.carouselIndex;
     @observable _last_opacity = 1;
 
@@ -43,28 +39,7 @@ export class CollectionCarouselView extends CollectionSubView() {
 
     @computed get captionMarginX(){ return NumCast(this.layoutDoc.caption_xMargin, 50); } // prettier-ignore
     @computed get carouselIndex() { return NumCast(this.layoutDoc._carousel_index) % this.carouselItems.length; } // prettier-ignore
-    @computed get carouselItems() { return this.childDocs
-        .filter(doc => doc.type !== DocumentType.LINK)
-        .filter(doc => !this._filterFunc?.(doc))
-    } // prettier-ignore
-
-    /**
-     * How much the content of the carousel view is being scaled based on its nesting and its fit-to-width settings
-     */
-    @computed get contentScaling() { return this.ScreenToLocalBoxXf().Scale * (this._props.NativeDimScaling?.() ?? 1); } // prettier-ignore
-
-    /**
-     * The maximum size a UI widget can be scaled so that it won't be bigger in screen pixels than its normal 35 pixel size.
-     */
-    @computed get maxWidgetScale() {
-        const maxWidgetSize = Math.min(this._sideBtnWidth * this.contentScaling, 0.1 * NumCast(this.layoutDoc.width, 1));
-        return Math.max(maxWidgetSize / this._sideBtnWidth, 1);
-    }
-    /**
-     * How much to reactively scale a UI element so that it is as big as it can be (up to its normal 35pixel size) without being too big for the Doc content
-     */
-    @computed get uiBtnScaleTransform() {  return this.maxWidgetScale * Math.min(1, this.contentScaling); } // prettier-ignore
-    screenXPadding = () => (this.uiBtnScaleTransform * this._sideBtnWidth - NumCast(this.layoutDoc.xMargin)) / this._props.ScreenToLocalTransform().Scale;
+    @computed get carouselItems() { return this.childLayoutPairs.filter(pair => !pair.layout.layout_isSvg); } // prettier-ignore
 
     /**
      * Move forward or backward the specified number of Docs
@@ -91,7 +66,7 @@ export class CollectionCarouselView extends CollectionSubView() {
         this.move(-1);
     };
 
-    curDoc = () => this.carouselItems[this.carouselIndex];
+    curDoc = () => this.carouselItems[this.carouselIndex]?.layout;
 
     captionStyleProvider = (doc: Doc | undefined, captionProps: Opt<FieldViewProps>, property: string) => {
         // first look for properties on the document in the carousel, then fallback to properties on the container
@@ -153,7 +128,7 @@ export class CollectionCarouselView extends CollectionSubView() {
      */
     @computed get overlay() {
         const fadeTime = 500;
-        const lastDoc = this.carouselItems?.[this._last_index];
+        const lastDoc = this.carouselItems?.[this._last_index]?.layout;
         return !lastDoc || this.carouselIndex === this._last_index ? null : (
             <div className="collectionCarouselView-image" style={{ opacity: this._last_opacity, transition: `opacity ${fadeTime}ms` }}>
                 {this.renderDoc(
@@ -211,10 +186,10 @@ export class CollectionCarouselView extends CollectionSubView() {
     @computed get navButtons() {
         return this.Document._chromeHidden || !this.curDoc() ? null : (
             <>
-                <div key="back" className="carouselView-back" style={{ transform: `scale(${this.uiBtnScaleTransform})` }} onClick={this.goback}>
+                <div key="back" className="carouselView-back" style={{ transform: `scale(${this.uiBtnScaling})` }} onClick={this.goback}>
                     <FontAwesomeIcon icon="chevron-left" size="2x" />
                 </div>
-                <div key="fwd" className="carouselView-fwd" style={{ transform: `scale(${this.uiBtnScaleTransform})` }} onClick={this.advance}>
+                <div key="fwd" className="carouselView-fwd" style={{ transform: `scale(${this.uiBtnScaling})` }} onClick={this.advance}>
                     <FontAwesomeIcon icon="chevron-right" size="2x" />
                 </div>
             </>
@@ -227,9 +202,7 @@ export class CollectionCarouselView extends CollectionSubView() {
         isContentActive: this.isChildContentActive,
         ScreenToLocalTransform: this.contentScreenToLocalXf,
     });
-    carouselItemsFunc = () => this.carouselItems;
     answered = () => this.advance();
-    @action setFilterFunc = (func?: (doc: Doc) => boolean) => { this._filterFunc = func; }; // prettier-ignore
 
     render() {
         return (
@@ -245,21 +218,7 @@ export class CollectionCarouselView extends CollectionSubView() {
                     top: NumCast(this.layoutDoc._yMargin),
                 }}>
                 {this.content}
-                <FlashcardPracticeUI
-                    setFilterFunc={this.setFilterFunc}
-                    fieldKey={this.fieldKey}
-                    sideBtnWidth={this._sideBtnWidth}
-                    carouselItems={this.carouselItemsFunc}
-                    childDocs={this.childDocs}
-                    advance={this.answered}
-                    curDoc={this.curDoc}
-                    layoutDoc={this.layoutDoc}
-                    maxWidgetScale={this.maxWidgetScale}
-                    uiBtnScaleTransform={this.uiBtnScaleTransform}
-                    ScreenToLocalBoxXf={this.ScreenToLocalBoxXf}
-                    renderDepth={this._props.renderDepth}
-                    docViewProps={this.docViewProps}
-                />
+                {this.flashCardUI(this.curDoc, this.docViewProps, this.answered)}
                 {this.navButtons}
             </div>
         );
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 581201a20..c057d2402 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -9,7 +9,7 @@ import { Id } from '../../../fields/FieldSymbols';
 import { List } from '../../../fields/List';
 import { listSpec } from '../../../fields/Schema';
 import { ScriptField } from '../../../fields/ScriptField';
-import { BoolCast, Cast, ScriptCast, StrCast } from '../../../fields/Types';
+import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
 import { WebField } from '../../../fields/URLField';
 import { GetEffectiveAcl, TraceMobx } from '../../../fields/util';
 import { GestureUtils } from '../../../pen-gestures/GestureUtils';
@@ -25,7 +25,8 @@ import { SnappingManager } from '../../util/SnappingManager';
 import { UndoManager } from '../../util/UndoManager';
 import { ViewBoxBaseComponent } from '../DocComponent';
 import { FieldViewProps } from '../nodes/FieldView';
-import { DocumentView } from '../nodes/DocumentView';
+import { DocumentView, DocumentViewProps } from '../nodes/DocumentView';
+import { FlashcardPracticeUI } from './FlashcardPracticeUI';
 
 export interface CollectionViewProps extends React.PropsWithChildren<FieldViewProps> {
     isAnnotationOverlay?: boolean; // is the collection an annotation overlay (eg an overlay on an image/video/etc)
@@ -119,7 +120,8 @@ export function CollectionSubView<X>() {
                     pair =>
                         // filter out any documents that have a proto that we don't have permissions to
                         !pair.layout?.hidden && pair.layout && (!pair.layout.proto || (pair.layout.proto instanceof Doc && GetEffectiveAcl(pair.layout.proto) !== AclPrivate))
-                );
+                )
+                .filter(pair => !this._filterFunc?.(pair.layout!));
             return validPairs.map(({ data, layout }) => ({ data: data as Doc, layout: layout! })); // this mapping is a bit of a hack to coerce types
         }
         /**
@@ -515,6 +517,49 @@ export function CollectionSubView<X>() {
                 alert('Document upload failed - possibly an unsupported file type.');
             }
         };
+
+        protected _sideBtnWidth = 35;
+        @observable _filterFunc: ((doc: Doc) => boolean) | undefined = undefined;
+        /**
+         * How much the content of the collection is being scaled based on its nesting and its fit-to-width settings
+         */
+        @computed get contentScaling() { return this.ScreenToLocalBoxXf().Scale * (this._props.NativeDimScaling?.() ?? 1); } // prettier-ignore
+        /**
+         * The maximum size a UI widget can be in collection coordinates based on not wanting the widget to visually obscure too much of the collection
+         * This takes the desired screen space size and converts into collection coordinates.  It then returns the smaller of the converted
+         * size or a fraction of the collection view.
+         */
+        @computed get maxWidgetSize() { return Math.min(this._sideBtnWidth * this.contentScaling, 0.25 * NumCast(this.layoutDoc.width, 1));  } // prettier-ignore
+        /**
+         * This computes a scale factor for UI elements so that they shrink and grow as the collection does in screen space.
+         * Note, the scale factor does not allow for elements to grow larger than their native screen space size.
+         */
+        @computed get uiBtnScaling() { return this.maxWidgetSize / this._sideBtnWidth; } // prettier-ignore
+
+        screenXPadding = () => (this.uiBtnScaling * this._sideBtnWidth - NumCast(this.layoutDoc.xMargin)) / this._props.ScreenToLocalTransform().Scale;
+        filteredChildDocs = () => this.childLayoutPairs.map(pair => pair.layout);
+        childDocsFunc = () => this.childDocs;
+        @action setFilterFunc = (func?: (doc: Doc) => boolean) => { this._filterFunc = func; }; // prettier-ignore
+
+        public flashCardUI = (curDoc: () => Doc | undefined, docViewProps: () => DocumentViewProps, answered?: (correct: boolean) => void) => {
+            return (
+                <FlashcardPracticeUI
+                    setFilterFunc={this.setFilterFunc}
+                    fieldKey={this.fieldKey}
+                    sideBtnWidth={this._sideBtnWidth}
+                    allChildDocs={this.childDocsFunc}
+                    filteredChildDocs={this.filteredChildDocs}
+                    advance={answered}
+                    curDoc={curDoc}
+                    practiceBtnOffset={this._sideBtnWidth * 4}
+                    layoutDoc={this.layoutDoc}
+                    uiBtnScaling={this.uiBtnScaling}
+                    ScreenToLocalBoxXf={this.ScreenToLocalBoxXf}
+                    renderDepth={this._props.renderDepth}
+                    docViewProps={docViewProps}
+                />
+            );
+        };
     }
 
     return CollectionSubViewInternal;
diff --git a/src/client/views/collections/FlashcardPracticeUI.scss b/src/client/views/collections/FlashcardPracticeUI.scss
index 2f99500f8..c5252bbfa 100644
--- a/src/client/views/collections/FlashcardPracticeUI.scss
+++ b/src/client/views/collections/FlashcardPracticeUI.scss
@@ -50,7 +50,7 @@
             height: 20px;
             align-items: center;
             margin: auto;
-            padding: 3px;
+            // padding: 3px;
             &:hover {
                 color: white;
             }
diff --git a/src/client/views/collections/FlashcardPracticeUI.tsx b/src/client/views/collections/FlashcardPracticeUI.tsx
index a643c95b0..7bf4d86d1 100644
--- a/src/client/views/collections/FlashcardPracticeUI.tsx
+++ b/src/client/views/collections/FlashcardPracticeUI.tsx
@@ -23,15 +23,14 @@ enum practiceVal {
 interface PracticeUIProps {
     fieldKey: string;
     layoutDoc: Doc;
-    carouselItems: () => Doc[];
-    childDocs: Doc[];
+    filteredChildDocs: () => Doc[];
+    allChildDocs: () => Doc[];
     curDoc: () => Doc | undefined;
-    advance: (correct: boolean) => void;
+    advance?: (correct: boolean) => void;
     renderDepth: number;
     sideBtnWidth: number;
-    uiBtnScaleTransform: number;
+    uiBtnScaling: number;
     ScreenToLocalBoxXf: () => Transform;
-    maxWidgetScale: number;
     docViewProps: () => DocumentViewProps;
     setFilterFunc: (func?: (doc: Doc) => boolean) => void;
     practiceBtnOffset?: number;
@@ -51,7 +50,7 @@ export class FlashcardPracticeUI extends ObservableReactComponent<PracticeUIProp
     get practiceField() { return this._props.fieldKey + "_practice"; } // prettier-ignore
 
     @computed get filterDoc() { return DocListCast(Doc.MyContextMenuBtns.data).find(doc => doc.title === 'Filter'); } // prettier-ignore
-    @computed get practiceMode() {  return this._props.childDocs.some(doc => doc._layout_isFlashcard) ? StrCast(this._props.layoutDoc.practiceMode) : ''; } // prettier-ignore
+    @computed get practiceMode() {  return this._props.allChildDocs().some(doc => doc._layout_isFlashcard) ? StrCast(this._props.layoutDoc.practiceMode) : ''; } // prettier-ignore
 
     btnHeight = () => NumCast(this.filterDoc?.height) * Math.min(1, this._props.ScreenToLocalBoxXf().Scale);
     btnWidth = () => (!this.filterDoc ? 1 : (this.btnHeight() * NumCast(this.filterDoc._width)) / NumCast(this.filterDoc._height));
@@ -62,12 +61,12 @@ export class FlashcardPracticeUI extends ObservableReactComponent<PracticeUIProp
      */
     setPracticeMode = (mode: practiceMode | undefined) => {
         this._props.layoutDoc.practiceMode = mode;
-        this._props.carouselItems().map(doc => (doc[this.practiceField] = undefined));
+        this._props.allChildDocs().map(doc => (doc[this.practiceField] = undefined));
     };
 
     @computed get emptyMessage() {
-        const cardCount = this._props.carouselItems().length;
-        const practiceMessage = this.practiceMode && !Doc.hasDocFilter(this._props.layoutDoc, 'tags', Doc.FilterAny) && !this._props.carouselItems().length ? 'Finished! Click here to view all flashcards.' : '';
+        const cardCount = this._props.filteredChildDocs().length;
+        const practiceMessage = this.practiceMode && !Doc.hasDocFilter(this._props.layoutDoc, 'tags', Doc.FilterAny) && !cardCount ? 'Finished! Click here to view all flashcards.' : '';
         const filterMessage = practiceMessage
             ? ''
             : Doc.hasDocFilter(this._props.layoutDoc, 'tags', Doc.FilterAny) && !cardCount
@@ -78,7 +77,7 @@ export class FlashcardPracticeUI extends ObservableReactComponent<PracticeUIProp
         return !practiceMessage && !filterMessage ? null : (
             <p
                 className="FlashcardPracticeUI-message"
-                style={{ transform: `scale(${this._props.uiBtnScaleTransform})` }}
+                style={{ transform: `scale(${this._props.uiBtnScaling})` }}
                 onClick={() => {
                     if (filterMessage || practiceMessage) {
                         this.setPracticeMode(undefined);
@@ -102,7 +101,7 @@ export class FlashcardPracticeUI extends ObservableReactComponent<PracticeUIProp
         };
 
         return this.practiceMode == practiceMode.PRACTICE && this._props.curDoc() ? (
-            <div className="FlashcardPracticeUI-practice" style={{ transform: `scale(${this._props.uiBtnScaleTransform})`, bottom: `${this._props.practiceBtnOffset ?? this._props.sideBtnWidth}px`, height: `${this._props.sideBtnWidth}px` }}>
+            <div className="FlashcardPracticeUI-practice" style={{ transform: `scale(${this._props.uiBtnScaling})`, bottom: `${this._props.practiceBtnOffset ?? this._props.sideBtnWidth}px`, height: `${this._props.sideBtnWidth}px` }}>
                 <Tooltip title="Incorrect. View again later.">
                     <div key="remove" className="FlashcardPracticeUI-remove" onClick={e => setPracticeVal(e, practiceVal.MISSED)}>
                         <FontAwesomeIcon icon="xmark" color="red" size="1x" />
@@ -120,12 +119,12 @@ export class FlashcardPracticeUI extends ObservableReactComponent<PracticeUIProp
         const setColor = (mode: practiceMode) => (StrCast(this.practiceMode) === mode ? 'white' : 'light gray');
         const togglePracticeMode = (mode: practiceMode) => this.setPracticeMode(mode === this.practiceMode ? undefined : mode);
 
-        return !this._props.childDocs.some(doc => doc._layout_isFlashcard) ? null : (
+        return !this._props.allChildDocs().some(doc => doc._layout_isFlashcard) ? null : (
             <div
                 className="FlashcardPracticeUI-practiceModes"
                 style={{
                     transformOrigin: `0px ${-this.btnHeight()}px`,
-                    transform: `scale(${Math.max(1, 1 / this._props.ScreenToLocalBoxXf().Scale / this._props.maxWidgetScale)})`,
+                    transform: `scale(${Math.max(1, 1 / this._props.ScreenToLocalBoxXf().Scale)})`,
                 }}>
                 <Tooltip title="Practice flashcards using GPT">
                     <div key="back" className="FlashcardPracticeUI-quiz" style={{ width: this.btnWidth(), height: this.btnHeight() }} onClick={() => togglePracticeMode(practiceMode.QUIZ)}>
@@ -146,7 +145,7 @@ export class FlashcardPracticeUI extends ObservableReactComponent<PracticeUIProp
             <>
                 {this.emptyMessage}
                 {this.practiceButtons}
-                <div className="FlashcardPracticeUI-menu" style={{ height: this.btnHeight(), width: this.btnHeight(), transform: `scale(${this._props.uiBtnScaleTransform})` }}>
+                <div className="FlashcardPracticeUI-menu" style={{ height: this.btnHeight(), width: this.btnHeight(), transform: `scale(${this._props.uiBtnScaling})` }}>
                     {!this.filterDoc || this._props.layoutDoc._chromeHidden ? null : (
                         <DocumentView
                             {...this._props.docViewProps()}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index d8678eebc..0cc63d632 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -138,7 +138,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         return this._props.layoutEngine?.() || StrCast(this.layoutDoc._layoutEngine);
     }
     @computed get childPointerEvents() {
-        return falseSnappingManager.IsResizing
+        return SnappingManager.IsResizing
             ? 'none'
             : (this._props.childPointerEvents?.() ??
                   (this._props.viewDefDivClick || //
diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx
index 81e223028..a8c91aae8 100644
--- a/src/client/views/nodes/ComparisonBox.tsx
+++ b/src/client/views/nodes/ComparisonBox.tsx
@@ -87,34 +87,26 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
         );
     }
 
-    _sideBtnWidth = 30;
+    _sideBtnWidth = 35;
     /**
      * How much the content of the view is being scaled based on its nesting and its fit-to-width settings
      */
-    @computed get contentScaling() {
-        return this.ScreenToLocalBoxXf().Scale * (this._props.NativeDimScaling?.() ?? 1);
-    }
+    @computed get viewScaling() { return this.ScreenToLocalBoxXf().Scale; } // prettier-ignore
     /**
      * The maximum size a UI widget can be scaled so that it won't be bigger in screen pixels than its normal 35 pixel size.
      */
-    @computed get maxWidgetScale() {
-        const maxWidgetSize = Math.min(this._sideBtnWidth * this.contentScaling, 0.25 * Math.min(NumCast(this.Document.width), NumCast(this.Document.height)));
-        return Math.max(maxWidgetSize / this._sideBtnWidth, 1);
-    }
+    @computed get maxWidgetSize() { return  Math.min(this._sideBtnWidth * this.viewScaling, 0.25 * Math.min(NumCast(this.Document.width), NumCast(this.Document.height))); } // prettier-ignore
     /**
      * How much to reactively scale a UI element so that it is as big as it can be (up to its normal 35pixel size) without being too big for the Doc content
      */
-    @computed get uiBtnScaleTransform() {
-        return this.maxWidgetScale * Math.min(1, this.contentScaling);
-    }
+    @computed get uiBtnScaling() { return Math.max(this.maxWidgetSize / this._sideBtnWidth, 1) * Math.min(1, this.viewScaling)* (this._props.NativeDimScaling?.() ?? 1); } // prettier-ignore
 
     @computed get flashcardMenu() {
         return (
-            <div className="comparisonBox-bottomMenu" style={{ transform: `scale(${this.uiBtnScaleTransform})` }}>
+            <div className="comparisonBox-bottomMenu" style={{ transform: `scale(${this.uiBtnScaling})` }}>
                 {this.overlayAlternateIcon}
                 {!this._props.isContentActive() ? null : (
                     <>
-                        {' '}
                         {!this._frontSide ? null : (
                             <Tooltip
                                 title={
-- 
cgit v1.2.3-70-g09d2


From a760773be31c274af45a7d781160585d58d88c07 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 9 Oct 2024 23:43:59 -0400
Subject: changed flashcard revealOp to be set on collection, not individual
 flashcards

---
 .../views/collections/CollectionCardDeckView.tsx   | 18 ++++----
 .../views/collections/FlashcardPracticeUI.tsx      | 53 +++++++++++++++++-----
 src/client/views/nodes/ComparisonBox.tsx           | 17 +------
 3 files changed, 52 insertions(+), 36 deletions(-)

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

diff --git a/src/client/views/collections/CollectionCardDeckView.tsx b/src/client/views/collections/CollectionCardDeckView.tsx
index 8f351d8a7..8807462c3 100644
--- a/src/client/views/collections/CollectionCardDeckView.tsx
+++ b/src/client/views/collections/CollectionCardDeckView.tsx
@@ -332,14 +332,16 @@ export class CollectionCardView extends CollectionSubView() {
         return docs;
     };
 
-    isChildContentActive = () =>
-        this._props.isContentActive?.() === false
-            ? false
-            : this._props.isDocumentActive?.() && (this._props.childDocumentsActive?.() || BoolCast(this.Document.childDocumentsActive))
-              ? true
-              : this._props.childDocumentsActive?.() === false || this.Document.childDocumentsActive === false
+    isChildContentActive = computedFn(
+        (doc: Doc) => () =>
+            this._props.isContentActive?.() === false
                 ? false
-                : undefined;
+                : this._props.isDocumentActive?.() && this.curDoc() === doc
+                  ? true
+                  : this._props.childDocumentsActive?.() === false || this.Document.childDocumentsActive === false
+                    ? false
+                    : undefined
+    ); // prettier-ignore
 
     displayDoc = (doc: Doc, screenToLocalTransform: () => Transform) => (
         <DocumentView
@@ -362,7 +364,7 @@ export class CollectionCardView extends CollectionSubView() {
             dragAction={(this.Document.childDragAction ?? this._props.childDragAction) as dropActionType}
             showTags={BoolCast(this.layoutDoc.showChildTags)}
             whenChildContentsActiveChanged={this._props.whenChildContentsActiveChanged}
-            isContentActive={this.isChildContentActive}
+            isContentActive={this.isChildContentActive(doc)}
             dontHideOnDrag
         />
     );
diff --git a/src/client/views/collections/FlashcardPracticeUI.tsx b/src/client/views/collections/FlashcardPracticeUI.tsx
index 7bf4d86d1..efa94d845 100644
--- a/src/client/views/collections/FlashcardPracticeUI.tsx
+++ b/src/client/views/collections/FlashcardPracticeUI.tsx
@@ -3,13 +3,17 @@ import { Tooltip } from '@mui/material';
 import { computed, makeObservable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
-import { returnZero } from '../../../ClientUtils';
+import { returnFalse, returnZero, setupMoveUpEvents } from '../../../ClientUtils';
 import { Doc, DocListCast } from '../../../fields/Doc';
 import { BoolCast, NumCast, StrCast } from '../../../fields/Types';
 import { Transform } from '../../util/Transform';
 import { ObservableReactComponent } from '../ObservableReactComponent';
 import { DocumentView, DocumentViewProps } from '../nodes/DocumentView';
 import './FlashcardPracticeUI.scss';
+import { IconButton, MultiToggle, Type } from 'browndash-components';
+import { SnappingManager } from '../../util/SnappingManager';
+import { IconProp } from '@fortawesome/fontawesome-svg-core';
+import { emptyFunction } from '../../../Utils';
 
 enum practiceMode {
     PRACTICE = 'practice',
@@ -116,7 +120,7 @@ export class FlashcardPracticeUI extends ObservableReactComponent<PracticeUIProp
         ) : null;
     }
     @computed get practiceModesMenu() {
-        const setColor = (mode: practiceMode) => (StrCast(this.practiceMode) === mode ? 'white' : 'light gray');
+        const setColor = (mode: practiceMode) => (StrCast(this.practiceMode) === mode ? 'white' : 'lightgray');
         const togglePracticeMode = (mode: practiceMode) => this.setPracticeMode(mode === this.practiceMode ? undefined : mode);
 
         return !this._props.allChildDocs().some(doc => doc._layout_isFlashcard) ? null : (
@@ -126,16 +130,41 @@ export class FlashcardPracticeUI extends ObservableReactComponent<PracticeUIProp
                     transformOrigin: `0px ${-this.btnHeight()}px`,
                     transform: `scale(${Math.max(1, 1 / this._props.ScreenToLocalBoxXf().Scale)})`,
                 }}>
-                <Tooltip title="Practice flashcards using GPT">
-                    <div key="back" className="FlashcardPracticeUI-quiz" style={{ width: this.btnWidth(), height: this.btnHeight() }} onClick={() => togglePracticeMode(practiceMode.QUIZ)}>
-                        <FontAwesomeIcon icon="file-pen" color={setColor(practiceMode.QUIZ)} size="sm" />
-                    </div>
-                </Tooltip>
-                <Tooltip title={this.practiceMode === practiceMode.PRACTICE ? 'Exit practice mode' : 'Practice flashcards manually'}>
-                    <div key="back" className="FlashcardPracticeUI-practice" style={{ width: this.btnWidth(), height: this.btnHeight() }} onClick={() => togglePracticeMode(practiceMode.PRACTICE)}>
-                        <FontAwesomeIcon icon="check" color={setColor(practiceMode.PRACTICE)} size="sm" />
-                    </div>
-                </Tooltip>
+                <MultiToggle
+                    tooltip="Practice flashcards one at a time"
+                    type={Type.PRIM}
+                    color={SnappingManager.userColor}
+                    background={SnappingManager.userVariantColor}
+                    multiSelect={false}
+                    isToggle={false}
+                    toggleStatus={!!this.practiceMode}
+                    label="Practice"
+                    items={[
+                        [practiceMode.QUIZ, 'file-pen', 'Practice flashcards using GPT'],
+                        [practiceMode.PRACTICE, 'check', this.practiceMode === practiceMode.PRACTICE ? 'Exit practice mode' : 'Practice flashcards manually'],
+                    ].map(([item, icon, tooltip]) => ({
+                        icon: <FontAwesomeIcon className={`FlashcardPracticeUI-${item}`} color={setColor(item as practiceMode)} icon={icon as IconProp} size="sm" />,
+                        tooltip: tooltip,
+                        val: item,
+                    }))}
+                    selectedItems={this.practiceMode}
+                    onSelectionChange={(val: (string | number) | (string | number)[]) => togglePracticeMode(val as practiceMode)}
+                />
+                <IconButton
+                    tooltip="hover over card to reveal answer"
+                    type={Type.TERT}
+                    text={StrCast(this._props.layoutDoc.revealOp)}
+                    color={SnappingManager.userColor}
+                    background={SnappingManager.userVariantColor}
+                    icon={<FontAwesomeIcon color={SnappingManager.userColor} icon={this._props.layoutDoc.revealOp === 'hover' ? 'hand-point-up' : 'question'} size="sm" />}
+                    label={StrCast(this._props.layoutDoc.revealOp)}
+                    onPointerDown={e =>
+                        setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => {
+                            this._props.layoutDoc.revealOp = this._props.layoutDoc.revealOp === 'hover' ? 'flip' : 'hover';
+                            this._props.layoutDoc.childDocumentsActive = this._props.layoutDoc.revealOp === 'hover' ? true : undefined;
+                        })
+                    }
+                />
             </div>
         );
     }
diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx
index b86d61fdd..aed81709b 100644
--- a/src/client/views/nodes/ComparisonBox.tsx
+++ b/src/client/views/nodes/ComparisonBox.tsx
@@ -62,7 +62,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
     @computed get clipWidthKey()  { return `_${this._props.fieldKey}_clipWidth`;  } // prettier-ignore
     @computed get clipWidth()     { return NumCast(this.layoutDoc[this.clipWidthKey], 50); } // prettier-ignore
     @computed get clipHeight()    { return NumCast(this.layoutDoc[this.clipHeightKey], 200);  } // prettier-ignore
-    @computed get revealOp()      { return StrCast(this.layoutDoc[this.revealOpKey]); } // prettier-ignore
+    @computed get revealOp()      { return StrCast(this.layoutDoc[this.revealOpKey], StrCast(this._props.docViewPath().slice(-2)[0]?.Document.revealOp)); } // prettier-ignore
     set revealOp(value:string)    { this.layoutDoc[this.revealOpKey] = value; } // prettier-ignore
 
     @computed get overlayAlternateIcon() {
@@ -135,27 +135,12 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
                                 </Tooltip>
                             </>
                         )}
-                        <Tooltip title={<div className="dash-tooltip">Hover to reveal</div>}>
-                            <div className="comparisonBox-button" onClick={this.handleHover}>
-                                <FontAwesomeIcon color={this.revealOp === 'hover' ? 'blue' : 'black'} icon="hand-point-up" size="xl" />
-                            </div>
-                        </Tooltip>
                     </>
                 )}
             </div>
         );
     }
 
-    @action handleHover = () => {
-        if (this.revealOp === 'hover') {
-            this.revealOp = 'flip';
-            this.Document.forceActive = false;
-        } else {
-            this.revealOp = 'hover';
-            this.Document.forceActive = true;
-        }
-    };
-
     @action handleInputChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
         this._inputValue = e.target.value;
     };
-- 
cgit v1.2.3-70-g09d2


From f0f1d04df56eb95a6d28dcbeab5071bd681baa15 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 10 Oct 2024 10:03:03 -0400
Subject: tweaks to cardDeck view so that cards are activated when clicked, but
 not selected.  This is consistent with carousel behavior and allows
 flashcards to behave the same way.  Also added advance to nexst card for card
 view

---
 .../views/collections/CollectionCardDeckView.tsx   | 56 +++++++++++++++-------
 .../views/collections/FlashcardPracticeUI.tsx      |  4 +-
 src/client/views/collections/TreeView.tsx          |  4 +-
 3 files changed, 41 insertions(+), 23 deletions(-)

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

diff --git a/src/client/views/collections/CollectionCardDeckView.tsx b/src/client/views/collections/CollectionCardDeckView.tsx
index 8807462c3..810b7b33c 100644
--- a/src/client/views/collections/CollectionCardDeckView.tsx
+++ b/src/client/views/collections/CollectionCardDeckView.tsx
@@ -2,7 +2,7 @@ import { IReactionDisposer, ObservableMap, action, computed, makeObservable, obs
 import { observer } from 'mobx-react';
 import { computedFn } from 'mobx-utils';
 import * as React from 'react';
-import { ClientUtils, DashColor, imageUrlToBase64, returnFalse, returnZero } from '../../../ClientUtils';
+import { ClientUtils, DashColor, imageUrlToBase64, returnFalse, returnNever, returnZero } from '../../../ClientUtils';
 import { Doc } from '../../../fields/Doc';
 import { DocData } from '../../../fields/DocSymbols';
 import { Id } from '../../../fields/FieldSymbols';
@@ -18,10 +18,12 @@ import { Transform } from '../../util/Transform';
 import { undoable } from '../../util/UndoManager';
 import { StyleProp } from '../StyleProp';
 import { TagItem } from '../TagsView';
-import { DocumentView } from '../nodes/DocumentView';
+import { DocumentView, DocumentViewProps } from '../nodes/DocumentView';
 import { GPTPopup, GPTPopupMode } from '../pdf/GPTPopup/GPTPopup';
 import './CollectionCardDeckView.scss';
 import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
+import { emptyFunction } from '../../../Utils';
+import { ScriptField } from '../../../fields/ScriptField';
 
 enum cardSortings {
     Time = 'time',
@@ -45,12 +47,14 @@ export class CollectionCardView extends CollectionSubView() {
     private _disposers: { [key: string]: IReactionDisposer } = {};
     private _textToDoc = new Map<string, Doc>();
     private _dropped = false; // set when a card doc has just moved and the drop method has been called - prevents the pointerUp method from hiding doc decorations (which needs to be done when clicking on a card to animate it to front/center)
+    private _clickScript = () => ScriptField.MakeScript('scriptContext._curDoc=this', { scriptContext: 'any' })!;
 
     @observable _forceChildXf = 0;
     @observable _hoveredNodeIndex = -1;
     @observable _docRefs = new ObservableMap<Doc, DocumentView>();
     @observable _maxRowCount = 10;
     @observable _docDraggedIndex: number = -1;
+    @observable _curDoc: Doc | undefined = undefined;
 
     constructor(props: SubCollectionViewProps) {
         super(props);
@@ -131,10 +135,10 @@ export class CollectionCardView extends CollectionSubView() {
     /**
      * When in quiz mode, randomly selects a document
      */
-    quizMode = () => {
+    quizMode = action(() => {
         const randomIndex = Math.floor(Math.random() * this.childDocs.length);
-        DocumentView.getDocumentView(this.childDocs[randomIndex])?.select(false);
-    };
+        this._curDoc = this.childDocs[randomIndex];
+    });
 
     /**
      * Number of rows of cards to be rendered
@@ -336,7 +340,7 @@ export class CollectionCardView extends CollectionSubView() {
         (doc: Doc) => () =>
             this._props.isContentActive?.() === false
                 ? false
-                : this._props.isDocumentActive?.() && this.curDoc() === doc
+                : this._props.isDocumentActive?.() && this._curDoc === doc
                   ? true
                   : this._props.childDocumentsActive?.() === false || this.Document.childDocumentsActive === false
                     ? false
@@ -360,6 +364,9 @@ export class CollectionCardView extends CollectionSubView() {
             isDocumentActive={this._props.childDocumentsActive?.() || this.Document._childDocumentsActive ? this._props.isDocumentActive : this.isContentActive}
             PanelWidth={this.childPanelWidth}
             PanelHeight={this.childPanelHeight}
+            waitForDoubleClickToClick={returnNever}
+            scriptContext={this}
+            onClickScript={this._curDoc === doc ? undefined : this._clickScript}
             dontCenter="y" // Don't center it vertically, because the grid it's in is already doing that and we don't want to do it twice.
             dragAction={(this.Document.childDragAction ?? this._props.childDragAction) as dropActionType}
             showTags={BoolCast(this.layoutDoc.showChildTags)}
@@ -553,7 +560,7 @@ export class CollectionCardView extends CollectionSubView() {
 
     cardPointerUp = action((doc: Doc) => {
         // if a card doc has just moved, or a card is selected and in front, then ignore this event
-        if (this.isSelected(doc) || this._dropped) {
+        if (this._curDoc === doc || this._dropped) {
             this._dropped = false;
         } else {
             // otherwise, turn off documentDecorations becase we're in a selection transition and want to avoid artifacts.
@@ -581,9 +588,8 @@ export class CollectionCardView extends CollectionSubView() {
         return this.sortedDocs.map((doc, index) => {
             const calcRowIndex = this.overflowIndexCalc(index);
             const amCards = this.overflowAmCardsCalc(index);
-            const view = DocumentView.getDocumentView(doc, this.DocumentView?.());
 
-            const childScreenToLocal = this.childScreenToLocal(doc, index, calcRowIndex, !!view?.IsContentActive, amCards);
+            const childScreenToLocal = this.childScreenToLocal(doc, index, calcRowIndex, doc === this._curDoc, amCards);
 
             const translateIfSelected = () => {
                 const indexInRow = index % this._maxRowCount;
@@ -598,15 +604,15 @@ export class CollectionCardView extends CollectionSubView() {
             return (
                 <div
                     key={doc[Id]}
-                    className={`card-item${view?.IsContentActive ? '-active' : this.isAnyChildContentActive() ? '-inactive' : ''}`}
+                    className={`card-item${doc === this._curDoc ? '-active' : this.isAnyChildContentActive() ? '-inactive' : ''}`}
                     onPointerUp={() => this.cardPointerUp(doc)}
                     style={{
                         width: this.childPanelWidth(),
                         height: 'max-content',
-                        transform: `translateY(${this.calculateTranslateY(!!view?.IsContentActive, index, amCards, calcRowIndex)}px)
-                                    translateX(calc(${view?.IsContentActive ? translateIfSelected() : 0}% + ${this.translateOverflowX(index, amCards)}px))
-                                    rotate(${!view?.IsContentActive ? this.rotate(amCards, calcRowIndex) : 0}deg)
-                                    scale(${view?.IsContentActive ? `${Math.min(hscale, vscale) * 100}%` : this._hoveredNodeIndex === index ? 1.1 : 1})`,
+                        transform: `translateY(${this.calculateTranslateY(doc === this._curDoc, index, amCards, calcRowIndex)}px)
+                                    translateX(calc(${doc === this._curDoc ? translateIfSelected() : 0}% + ${this.translateOverflowX(index, amCards)}px))
+                                    rotate(${doc !== this._curDoc ? this.rotate(amCards, calcRowIndex) : 0}deg)
+                                    scale(${doc === this._curDoc ? `${Math.min(hscale, vscale) * 100}%` : this._hoveredNodeIndex === index ? 1.1 : 1})`,
                     }} // prettier-ignore
                     onPointerEnter={() => this.setHoveredNodeIndex(index)}
                     onPointerLeave={() => this.setHoveredNodeIndex(-1)}>
@@ -617,13 +623,16 @@ export class CollectionCardView extends CollectionSubView() {
     }
 
     contentScreenToLocalXf = () => this._props.ScreenToLocalTransform().scale(this._props.NativeDimScaling?.() || 1);
-    curDoc = () => this.childCards.find(card => DocumentView.getDocumentView(card.layout, this.DocumentView?.())?.IsSelected)?.layout;
-    docViewProps = () => ({
+    docViewProps = (): DocumentViewProps => ({
         ...this._props, //
         isDocumentActive: this._props.childDocumentsActive?.() ? this._props.isDocumentActive : this._props.isContentActive,
-        isContentActive: this.isChildContentActive,
+        isContentActive: emptyFunction,
         ScreenToLocalTransform: this.contentScreenToLocalXf,
     });
+    answered = action(() => {
+        this._curDoc = this.curDoc ? this.filteredChildDocs()[(this.filteredChildDocs().findIndex(this.curDoc) + 1) % (this.filteredChildDocs().length || 1)] : undefined;
+    });
+    curDoc = () => this._curDoc;
 
     render() {
         const isEmpty = this.childCards.length === 0;
@@ -631,6 +640,17 @@ export class CollectionCardView extends CollectionSubView() {
             <div
                 className="collectionCardView-outer"
                 ref={(ele: HTMLDivElement | null) => this.createDashEventsTarget(ele)}
+                onPointerDown={action(() => {
+                    this._curDoc = undefined;
+                    SnappingManager.SetHideDecorations(true);
+                    setTimeout(
+                        action(() => {
+                            SnappingManager.SetHideDecorations(false);
+                            this._forceChildXf++;
+                        }),
+                        1000
+                    );
+                })}
                 onPointerLeave={action(() => (this._docDraggedIndex = -1))}
                 onPointerMove={e => this.onPointerMove(...this._props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY))}
                 onDrop={this.onExternalDrop.bind(this)}
@@ -648,7 +668,7 @@ export class CollectionCardView extends CollectionSubView() {
                     }}>
                     {this.renderCards}
                 </div>
-                {this.flashCardUI(this.curDoc, this.docViewProps)}
+                {this.flashCardUI(this.curDoc, this.docViewProps, this.answered)}
             </div>
         );
     }
diff --git a/src/client/views/collections/FlashcardPracticeUI.tsx b/src/client/views/collections/FlashcardPracticeUI.tsx
index efa94d845..272fb81dc 100644
--- a/src/client/views/collections/FlashcardPracticeUI.tsx
+++ b/src/client/views/collections/FlashcardPracticeUI.tsx
@@ -107,12 +107,12 @@ export class FlashcardPracticeUI extends ObservableReactComponent<PracticeUIProp
         return this.practiceMode == practiceMode.PRACTICE && this._props.curDoc() ? (
             <div className="FlashcardPracticeUI-practice" style={{ transform: `scale(${this._props.uiBtnScaling})`, bottom: `${this._props.practiceBtnOffset ?? this._props.sideBtnWidth}px`, height: `${this._props.sideBtnWidth}px` }}>
                 <Tooltip title="Incorrect. View again later.">
-                    <div key="remove" className="FlashcardPracticeUI-remove" onClick={e => setPracticeVal(e, practiceVal.MISSED)}>
+                    <div key="remove" className="FlashcardPracticeUI-remove" onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => setPracticeVal(e, practiceVal.MISSED))}>
                         <FontAwesomeIcon icon="xmark" color="red" size="1x" />
                     </div>
                 </Tooltip>
                 <Tooltip title="Correct">
-                    <div key="check" className="FlashcardPracticeUI-check" onClick={e => setPracticeVal(e, practiceVal.CORRECT)}>
+                    <div key="check" className="FlashcardPracticeUI-check" onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => setPracticeVal(e, practiceVal.CORRECT))}>
                         <FontAwesomeIcon icon="check" color="green" size="1x" />
                     </div>
                 </Tooltip>
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 5444a7a57..9284a36a2 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -38,7 +38,7 @@ import { CollectionView } from './CollectionView';
 import { TreeSort } from './TreeSort';
 import './TreeView.scss';
 
-// eslint-disable-next-line @typescript-eslint/no-var-requires
+// eslint-disable-next-line @typescript-eslint/no-require-imports
 const { TREE_BULLET_WIDTH } = require('../global/globalCssVariables.module.scss'); // prettier-ignore
 
 export interface TreeViewProps {
@@ -500,9 +500,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
                 !(key in ids) && doc[key] !== ComputedField.undefined && (ids[key] = key);
             });
 
-        // eslint-disable-next-line no-restricted-syntax
         for (const key of Object.keys(ids).slice().sort()) {
-            // eslint-disable-next-line no-continue
             if (this._props.skipFields?.includes(key) || key === 'title' || key === 'treeView_Open') continue;
             const contents = doc[key];
             let contentElement: (JSX.Element | null)[] | JSX.Element = [];
-- 
cgit v1.2.3-70-g09d2


From 040a1c5fd3e80606793e65be3ae821104460511b Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 10 Oct 2024 20:03:56 -0400
Subject: minor lint fixes.

---
 src/client/views/EditableView.tsx                  | 19 +++---
 .../views/collections/CollectionCardDeckView.tsx   |  1 -
 src/client/views/collections/TreeView.tsx          |  4 +-
 .../components/EditableText/EditableText.tsx       | 73 +++++++---------------
 4 files changed, 32 insertions(+), 65 deletions(-)

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

diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx
index 898a98c98..e2490cec8 100644
--- a/src/client/views/EditableView.tsx
+++ b/src/client/views/EditableView.tsx
@@ -7,7 +7,6 @@ import { DocumentIconContainer } from './nodes/DocumentIcon';
 import { FieldView, FieldViewProps } from './nodes/FieldView';
 import { ObservableReactComponent } from './ObservableReactComponent';
 import { OverlayView } from './OverlayView';
-import { SchemaFieldType } from './collections/collectionSchema/SchemaColumnHeader';
 
 export interface EditableProps {
     /**
@@ -289,14 +288,16 @@ export class EditableView extends ObservableReactComponent<EditableProps> {
     staticDisplay = () => {
         let toDisplay;
         const gval = this._props.GetValue()?.replace(/\n/g, '\\r\\n');
-        if (this._props.inputString){
-            toDisplay = <input className="editableView-input"
-            value={gval}
-            placeholder={this._props.inputStringPlaceholder}
-            readOnly
-            style={{ display: this._props.display, overflow: 'auto', pointerEvents: 'none', fontSize: this._props.fontSize, width: '100%', margin: 0, background: this._props.background}}
-            // eslint-disable-next-line jsx-a11y/no-autofocus
-        />
+        if (this._props.inputString) {
+            toDisplay = (
+                <input
+                    className="editableView-input"
+                    value={gval}
+                    placeholder={this._props.inputStringPlaceholder}
+                    readOnly
+                    style={{ display: this._props.display, overflow: 'auto', pointerEvents: 'none', fontSize: this._props.fontSize, width: '100%', margin: 0, background: this._props.background }}
+                />
+            );
         } else {
             toDisplay = (
                 <span
diff --git a/src/client/views/collections/CollectionCardDeckView.tsx b/src/client/views/collections/CollectionCardDeckView.tsx
index 92c69c3cf..3122aa587 100644
--- a/src/client/views/collections/CollectionCardDeckView.tsx
+++ b/src/client/views/collections/CollectionCardDeckView.tsx
@@ -22,7 +22,6 @@ import { GPTPopup, GPTPopupMode } from '../pdf/GPTPopup/GPTPopup';
 import './CollectionCardDeckView.scss';
 import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
 import { computedFn } from 'mobx-utils';
-import { DocumentDecorations } from '../DocumentDecorations';
 
 enum cardSortings {
     Time = 'time',
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 5444a7a57..2f604aaa5 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -38,7 +38,7 @@ import { CollectionView } from './CollectionView';
 import { TreeSort } from './TreeSort';
 import './TreeView.scss';
 
-// eslint-disable-next-line @typescript-eslint/no-var-requires
+// eslint-disable-next-line  @typescript-eslint/no-require-imports
 const { TREE_BULLET_WIDTH } = require('../global/globalCssVariables.module.scss'); // prettier-ignore
 
 export interface TreeViewProps {
@@ -500,9 +500,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
                 !(key in ids) && doc[key] !== ComputedField.undefined && (ids[key] = key);
             });
 
-        // eslint-disable-next-line no-restricted-syntax
         for (const key of Object.keys(ids).slice().sort()) {
-            // eslint-disable-next-line no-continue
             if (this._props.skipFields?.includes(key) || key === 'title' || key === 'treeView_Open') continue;
             const contents = doc[key];
             let contentElement: (JSX.Element | null)[] | JSX.Element = [];
diff --git a/src/client/views/newlightbox/components/EditableText/EditableText.tsx b/src/client/views/newlightbox/components/EditableText/EditableText.tsx
index e9e7ca264..6273e1859 100644
--- a/src/client/views/newlightbox/components/EditableText/EditableText.tsx
+++ b/src/client/views/newlightbox/components/EditableText/EditableText.tsx
@@ -1,16 +1,16 @@
-import * as React from 'react'
-import './EditableText.scss'
-import { Size } from 'browndash-components'
+import * as React from 'react';
+import './EditableText.scss';
+import { Size } from 'browndash-components';
 
 export interface IEditableTextProps {
-  text: string
-  placeholder?: string
-  editing: boolean
-  onEdit: (newText: string) => void
-  setEditing: (editing: boolean) => void
-  backgroundColor?: string
-  size?: Size
-  height?: number
+    text: string;
+    placeholder?: string;
+    editing: boolean;
+    onEdit: (newText: string) => void;
+    setEditing: (editing: boolean) => void;
+    backgroundColor?: string;
+    size?: Size;
+    height?: number;
 }
 
 /**
@@ -20,46 +20,15 @@ export interface IEditableTextProps {
  * @returns
  */
 export const EditableText = (props: IEditableTextProps) => {
-  const {
-    editing,
-    height,
-    size,
-    text,
-    onEdit,
-    setEditing,
-    backgroundColor,
-    placeholder,
-  } = props
+    const { editing, height, text, onEdit, setEditing, backgroundColor, placeholder } = props;
 
-  const handleOnChange = (event: React.ChangeEvent<HTMLInputElement>) => {
-    onEdit(event.target.value)
-  }
+    const handleOnChange = (event: React.ChangeEvent<HTMLInputElement>) => {
+        onEdit(event.target.value);
+    };
 
-  return editing ? (
-    <input
-      style={{ background: backgroundColor, height: height }}
-      placeholder={placeholder}
-      size={1}
-      className="lb-editableText"
-      autoFocus
-      onChange={handleOnChange}
-      onBlur={() => setEditing(false)}
-      defaultValue={text}
-    ></input>
-  ) : (
-    <input
-      style={{ background: backgroundColor, height: height }}
-      placeholder={placeholder}
-      size={1}
-      className="lb-editableText"
-      autoFocus
-      onChange={handleOnChange}
-      onBlur={() => setEditing(false)}
-      defaultValue={text}
-    ></input>
-    // <div className="lb-displayText" onClick={(e) => {
-    //     e.stopPropagation()
-    //     setEditing(true)
-    // }}>{text}</div>
-  )
-}
+    return editing ? (
+        <input style={{ background: backgroundColor, height: height }} placeholder={placeholder} size={1} className="lb-editableText" autoFocus onChange={handleOnChange} onBlur={() => setEditing(false)} defaultValue={text}></input>
+    ) : (
+        <input style={{ background: backgroundColor, height: height }} placeholder={placeholder} size={1} className="lb-editableText" autoFocus onChange={handleOnChange} onBlur={() => setEditing(false)} defaultValue={text}></input>
+    );
+};
-- 
cgit v1.2.3-70-g09d2


From 76abb174684f2cd231a0dd9f6b71484c16e0498a Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 11 Oct 2024 15:55:19 -0400
Subject: fixes for quiz mode - comparisonbox renderSide fixes. scrolling
 doesn't propagate out of carousel or card views.   fix for text with image
 Doc - now gets saved to UPDATE_CACHE working  set.

---
 .../views/collections/CollectionCardDeckView.tsx   |   6 ++
 .../views/collections/CollectionCarousel3DView.tsx |   6 ++
 .../views/collections/CollectionCarouselView.tsx   |   6 ++
 src/client/views/nodes/ComparisonBox.tsx           | 114 +++++++++++----------
 .../nodes/formattedText/DashDocCommentView.tsx     |   4 +-
 src/fields/Doc.ts                                  |  13 +++
 src/server/GarbageCollector.ts                     |   3 -
 7 files changed, 95 insertions(+), 57 deletions(-)

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

diff --git a/src/client/views/collections/CollectionCardDeckView.tsx b/src/client/views/collections/CollectionCardDeckView.tsx
index 286df30aa..14ce9d2af 100644
--- a/src/client/views/collections/CollectionCardDeckView.tsx
+++ b/src/client/views/collections/CollectionCardDeckView.tsx
@@ -46,6 +46,7 @@ export class CollectionCardView extends CollectionSubView() {
     private _dropDisposer?: DragManager.DragDropDisposer;
     private _disposers: { [key: string]: IReactionDisposer } = {};
     private _textToDoc = new Map<string, Doc>();
+    private _oldWheel: HTMLElement | null = null;
     private _dropped = false; // set when a card doc has just moved and the drop method has been called - prevents the pointerUp method from hiding doc decorations (which needs to be done when clicking on a card to animate it to front/center)
     private _clickScript = () => ScriptField.MakeScript('scriptContext._curDoc=this', { scriptContext: 'any' })!;
 
@@ -66,6 +67,10 @@ export class CollectionCardView extends CollectionSubView() {
         if (ele) {
             this._dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.layoutDoc);
         }
+        this._oldWheel?.removeEventListener('wheel', this.onPassiveWheel);
+        this._oldWheel = ele;
+        // prevent wheel events from passively propagating up through containers and prevents containers from preventDefault which would block scrolling
+        ele?.addEventListener('wheel', this.onPassiveWheel, { passive: false });
     };
     /**
      * Callback to ensure gpt's text versions of the child docs are updated
@@ -621,6 +626,7 @@ export class CollectionCardView extends CollectionSubView() {
             );
         });
     }
+    onPassiveWheel = (e: WheelEvent) => e.stopPropagation();
 
     contentScreenToLocalXf = () => this._props.ScreenToLocalTransform().scale(this._props.NativeDimScaling?.() || 1);
     docViewProps = (): DocumentViewProps => ({
diff --git a/src/client/views/collections/CollectionCarousel3DView.tsx b/src/client/views/collections/CollectionCarousel3DView.tsx
index f2ba90c78..05be376ca 100644
--- a/src/client/views/collections/CollectionCarousel3DView.tsx
+++ b/src/client/views/collections/CollectionCarousel3DView.tsx
@@ -22,6 +22,7 @@ const { CAROUSEL3D_CENTER_SCALE, CAROUSEL3D_SIDE_SCALE, CAROUSEL3D_TOP } = requi
 @observer
 export class CollectionCarousel3DView extends CollectionSubView() {
     private _dropDisposer?: DragManager.DragDropDisposer;
+    private _oldWheel: HTMLElement | null = null;
 
     constructor(props: SubCollectionViewProps) {
         super(props);
@@ -37,6 +38,10 @@ export class CollectionCarousel3DView extends CollectionSubView() {
         if (ele) {
             this._dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.layoutDoc);
         }
+        this._oldWheel?.removeEventListener('wheel', this.onPassiveWheel);
+        this._oldWheel = ele;
+        // prevent wheel events from passively propagating up through containers and prevents containers from preventDefault which would block scrolling
+        ele?.addEventListener('wheel', this.onPassiveWheel, { passive: false });
     };
 
     @computed get scrollSpeed() {
@@ -194,6 +199,7 @@ export class CollectionCarousel3DView extends CollectionSubView() {
         return this.panelWidth() * (1 - index);
     }
 
+    onPassiveWheel = (e: WheelEvent) => e.stopPropagation();
     curDoc = () => this.carouselItems[NumCast(this.layoutDoc._carousel_index)]?.layout;
     answered = (correct: boolean) => (!correct || !this.curDoc()) && this.changeSlide(1);
     docViewProps = () => ({
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index aa447c7bf..ef66a2c83 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -17,6 +17,7 @@ import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
 export class CollectionCarouselView extends CollectionSubView() {
     private _dropDisposer?: DragManager.DragDropDisposer;
 
+    _oldWheel: HTMLElement | null = null;
     _fadeTimer: NodeJS.Timeout | undefined;
     @observable _last_index = this.carouselIndex;
     @observable _last_opacity = 1;
@@ -35,6 +36,10 @@ export class CollectionCarouselView extends CollectionSubView() {
         if (ele) {
             this._dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.layoutDoc);
         }
+        this._oldWheel?.removeEventListener('wheel', this.onPassiveWheel);
+        this._oldWheel = ele;
+        // prevent wheel events from passively propagating up through containers and prevents containers from preventDefault which would block scrolling
+        ele?.addEventListener('wheel', this.onPassiveWheel, { passive: false });
     };
 
     @computed get captionMarginX(){ return NumCast(this.layoutDoc.caption_xMargin, 50); } // prettier-ignore
@@ -91,6 +96,7 @@ export class CollectionCarouselView extends CollectionSubView() {
               : this._props.childDocumentsActive?.() === false || this.Document.childDocumentsActive === false
                 ? false
                 : undefined;
+    onPassiveWheel = (e: WheelEvent) => e.stopPropagation();
     renderDoc = (doc: Doc, showCaptions: boolean, overlayFunc?: (r: DocumentView | null) => void) => {
         return (
             <DocumentView
diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx
index a57090e99..111fabca3 100644
--- a/src/client/views/nodes/ComparisonBox.tsx
+++ b/src/client/views/nodes/ComparisonBox.tsx
@@ -39,6 +39,9 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
     public static LayoutString(fieldKey: string) {
         return FieldView.LayoutString(ComparisonBox, fieldKey);
     }
+    static qtoken = 'Question: ';
+    static ktoken = 'Keyword: ';
+    static atoken = 'Answer: ';
     private SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
     private _closeRef = React.createRef<HTMLDivElement>();
     private _disposers: { [key: string]: DragManager.DragDropDisposer | undefined } = {};
@@ -55,11 +58,11 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
     @observable private _childActive = false;
     @observable private _animating = '';
     @observable private _listening = false;
-    @observable private _renderSide = this.fieldKey;
+    @observable private _renderSide = this.frontKey;
     @observable private _recognition = new this.SpeechRecognition();
 
     @computed get isFlashcard()   { return BoolCast(this.Document.layout_isFlashcard); } // prettier-ignore
-    @computed get frontKey()      { return this._props.fieldKey; } // prettier-ignore
+    @computed get frontKey()      { return this._props.fieldKey + '_front'; } // prettier-ignore
     @computed get backKey()       { return this._props.fieldKey + '_back'; } // prettier-ignore
     @computed get revealOpKey()   { return `_${this._props.fieldKey}_revealOp`; } // prettier-ignore
     @computed get clipHeightKey() { return `_${this._props.fieldKey}_clipHeight`; } // prettier-ignore
@@ -124,7 +127,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
                                 </div>
                             </Tooltip>
                         )}
-                        {DocCast(this.Document.embedContainer)?.type_collection !== CollectionViewType.Freeform ? null : (
+                        {DocCast(this.Document.embedContainer)?.type_collection !== CollectionViewType.Freeform || this._renderSide === this.backKey ? null : (
                             <Tooltip title={<div className="dash-tooltip">Create new flashcard stack based on text</div>}>
                                 <div className="comparisonBox-button" onClick={this.gptFlashcardPile}>
                                     <FontAwesomeIcon icon="layer-group" size="xl" />
@@ -150,9 +153,9 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
         if (phonTrans) {
             this._inputValue = StrCast(phonTrans);
             this.askGPTPhonemes(this._inputValue);
+            this._renderSide = this.backKey;
+            this._outputValue = '';
         } else if (this._inputValue) this.askGPT(GPTCallType.QUIZ);
-        this._renderSide = this.backKey;
-        this._outputValue = '';
     };
 
     onPointerMove = ({ movementX }: PointerEvent) => {
@@ -444,38 +447,41 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
             }),
             text
         );
-    /**
-     * Transfers the content of flashcards into a flashcard pile.
-     */
-    gptFlashcardPile = async () => {
-        this.askGPT(GPTCallType.STACK).then(text => {
-            const [qtoken, ktoken, atoken] = ['Question: ', 'Keyword: ', 'Answer: '];
-            const collectionArr: Doc[] = [];
-            const promises = text
-                .split(qtoken)
+
+    createFlashcard = (tuple: string, useDoc?: Doc) => {
+        const [ktoken, atoken] = [ComparisonBox.ktoken, ComparisonBox.atoken];
+        const newDoc = useDoc ?? Docs.Create.ComparisonDocument('', { _layout_isFlashcard: true, _width: 300, _height: 300 });
+        const question = (tuple.includes(ktoken) ? tuple.split(ktoken)[0] : tuple).split(atoken)[0];
+        const rest = tuple.replace(question, '');
+        // prettier-ignore
+        const answer = rest.startsWith(ktoken) ?     // if keyword comes first, 
+                            tuple.includes(atoken) ? tuple.split(atoken)[1] : "" : //if tuple includes answer, split at answer and take what's left, otherwise there's no answer
+                        rest.includes(ktoken) ?      // otherwise if keyword is present it must come after answer, 
+                            rest.split(ktoken)[0].split(atoken)[1] :  // split at keyword and take what comes first and split that at answer and take what's left
+                            rest.replace(atoken,""); // finally if there's no keyword, just get rid of answer token and take what's left
+        const keyword = rest.replace(atoken, '').replace(answer, '').replace(ktoken, '').trim();
+        const fillInFlashcard = (img?: Doc) => {
+            newDoc[DocData][this.frontKey] = this.textCreator('question', question, img);
+            newDoc[DocData][this.backKey] = this.textCreator('answer', answer);
+            return newDoc;
+        };
+        return keyword && keyword !== 'none' ? this.fetchImages(keyword).then(img => fillInFlashcard(img)) : fillInFlashcard();
+    };
+
+    createFlashcardDeck = (text: string) => {
+        Promise.all(
+            text
+                .split(ComparisonBox.qtoken)
                 .filter(t => t)
-                .map(tuple => {
-                    const newDoc = Docs.Create.ComparisonDocument('', { _layout_isFlashcard: true, _width: 300, _height: 300 });
-                    const question = (tuple.includes(ktoken) ? tuple.split(ktoken)[0] : tuple).split(atoken)[0];
-                    const rest = tuple.replace(question, '');
-                    // prettier-ignore
-                    const answer = rest.startsWith(ktoken) ?     // if keyword comes first, 
-                                        tuple.includes(atoken) ? tuple.split(atoken)[1] : "" : //if tuple includes answer, split at answer and take what's left, otherwise there's no answer
-                                    rest.includes(ktoken) ?      // otherwise if keyword is present it must come after answer, 
-                                        rest.split(ktoken)[0].split(atoken)[1] :  // split at keyword and take what comes first and split that at answer and take what's left
-                                        rest.replace(atoken,""); // finally if there's no keyword, just get rid of answer token and take what's left
-                    const keyword = rest.replace(atoken, '').replace(answer, '').replace(ktoken, '').trim();
-                    const fillInFlashcard = (img?: Doc) => {
-                        newDoc[DocData][this.frontKey] = this.textCreator('question', question, img);
-                        newDoc[DocData][this.backKey] = this.textCreator('answer', answer);
-                        collectionArr.push(newDoc);
-                    };
-                    return keyword && keyword !== 'none' ? this.fetchImages(keyword).then(img => fillInFlashcard(img)) : fillInFlashcard();
-                });
-            Promise.all(promises).then(() => this.createFlashcardPile(collectionArr, true));
-        });
+                .map(tuple => this.createFlashcard(tuple))
+        ).then(docs => this.createFlashcardPile(docs, true));
     };
 
+    /**
+     * queries GPT about a topic and then creates a flashcard deck from the results.
+     */
+    gptFlashcardPile = () => this.askGPT(GPTCallType.STACK).then(this.createFlashcardDeck);
+
     /**
      * Calls GPT for each flashcard type.
      */
@@ -498,7 +504,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
                             break;
                         case GPTCallType.QUIZ:
                             runInAction(() => {
-                                this._renderSide = this.frontKey;
+                                this._renderSide = this.backKey;
                                 this._outputValue = res.replace(/UserAnswer/g, "user's answer").replace(/Rubric/g, 'rubric');
                             });
                             break;
@@ -728,20 +734,24 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
         );
 
         if (this.isFlashcard) {
-            const dataSplit = StrCast(this.dataDoc.data).includes('Keyword: ') ? StrCast(this.dataDoc.data).split('Keyword: ') : StrCast(this.dataDoc.data).split('Answer: ');
-
-            // add text box to each side when comparison box is first created
-            if (!this.dataDoc[this.backKey] && !this._isEmpty) {
-                this.dataDoc[this.backKey] = this.textCreator('answer', dataSplit[1]);
+            if (this.dataDoc.data) {
+                if (!this.dataDoc[this.backKey] || !this.dataDoc[this.frontKey]) this.createFlashcard(StrCast(this.dataDoc.data), this.Document);
+            } else {
+                // add text box to each side when comparison box is first created
+                if (!this.dataDoc[this.backKey] && !this._isEmpty) {
+                    const answer = this.textCreator('answer', 'answer here');
+                    this.dataDoc[this.backKey] = answer;
+                    answer[DocData].text_placeholder = true;
+                }
+
+                if (!this.dataDoc[this.frontKey] && !this._isEmpty) {
+                    const question = this.textCreator('question', 'hint: Enter a topic, select this document and click the stack button to have GPT create a deck of cards');
+                    this.dataDoc[this.frontKey] = question;
+                    question[DocData].text_placeholder = true;
+                }
             }
 
-            if (!this.dataDoc[this.frontKey] && !this._isEmpty) {
-                const question = this.textCreator('question', dataSplit[0] || 'hint: Enter a topic, select this document and click the stack button to have GPT create a deck of cards');
-                this.dataDoc[this.frontKey] = question;
-                !dataSplit[0] && (question[DocData].text_placeholder = true);
-            }
-
-            if (DocCast(this.Document.embedContainer).practiceMode === practiceMode.QUIZ) {
+            if (DocCast(this.Document.embedContainer)?.practiceMode === practiceMode.QUIZ) {
                 const text = StrCast(RTFCast(DocCast(this.dataDoc[this.frontKey]).text)?.Text);
                 return (
                     <div className={`comparisonBox${this._props.isContentActive() ? '-interactive' : ''}`}>
@@ -749,15 +759,15 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
                         <p style={{ display: text === '' ? 'flex' : 'none', color: 'white', marginLeft: '10px' }}>Return to all flashcards and add text to both sides. </p>
                         <div className="input-box">
                             <textarea
-                                value={this._renderSide ? this._outputValue : this._inputValue}
+                                value={this._renderSide === this.backKey ? this._outputValue : this._inputValue}
                                 onChange={this.handleInputChange}
                                 onScroll={e => {
                                     e.stopPropagation();
                                     e.preventDefault();
                                 }}
                                 placeholder={!this.layoutDoc[`_${this._props.fieldKey}_usePath`] ? 'Enter a response for GPT to evaluate.' : ''}
-                                readOnly={this._renderSide === this.frontKey}></textarea>
-
+                                readOnly={this._renderSide === this.backKey}
+                            />
                             {!this.loading ? null : (
                                 <div className="loading-spinner">
                                     <ReactLoading type="spin" height={30} width={30} color={'blue'} />
@@ -778,8 +788,8 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
                                 <button className="submit-buttonpronunciation" onClick={this.evaluatePronunciation}>
                                     Evaluate Pronunciation
                                 </button>
-                                <button className="submit-buttonsubmit" type="button" onClick={this._renderSide === this.frontKey ? this.flipFlashcard : this.handleRenderGPTClick}>
-                                    {this._renderSide === this.frontKey ? 'Redo the Question' : 'Submit'}
+                                <button className="submit-buttonsubmit" type="button" onClick={this._renderSide === this.backKey ? this.flipFlashcard : this.handleRenderGPTClick}>
+                                    {this._renderSide === this.backKey ? 'Redo the Question' : 'Submit'}
                                 </button>
                             </div>
                         </div>
diff --git a/src/client/views/nodes/formattedText/DashDocCommentView.tsx b/src/client/views/nodes/formattedText/DashDocCommentView.tsx
index 0304ddc86..967f4aa5b 100644
--- a/src/client/views/nodes/formattedText/DashDocCommentView.tsx
+++ b/src/client/views/nodes/formattedText/DashDocCommentView.tsx
@@ -68,7 +68,7 @@ export class DashDocCommentViewInternal extends React.Component<IDashDocCommentV
                 expand && this._dashDoc.then(async dashDoc => dashDoc instanceof Doc && Doc.linkFollowHighlight(dashDoc));
                 try {
                     this.props.view.dispatch(this.props.view.state.tr.setSelection(TextSelection.create(this.props.view.state.tr.doc, (this.props.getPos() ?? 0) + (expand ? 2 : 1))));
-                } catch (err) {
+                } catch {
                     /* empty */
                 }
             }, 0);
@@ -95,7 +95,7 @@ export class DashDocCommentViewInternal extends React.Component<IDashDocCommentV
         setTimeout(() => {
             try {
                 this.props.view.dispatch(state.tr.setSelection(TextSelection.create(state.tr.doc, this.props.getPos() + 2)));
-            } catch (err) {
+            } catch {
                 /* empty */
             }
         }, 0);
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 81241f9fe..45dfe233f 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -960,6 +960,19 @@ export namespace Doc {
                         }
                     } else if (field instanceof PrefetchProxy) {
                         Doc.FindReferences(field.value, references, system);
+                    } else if (field instanceof RichTextField) {
+                        const re = /"docId"\s*:\s*"(.*?)"/g;
+                        let match: string[] | null;
+                        while ((match = re.exec(field.Data)) !== null) {
+                            const urlString = match[1];
+                            if (urlString) {
+                                const rdoc = DocServer.GetCachedRefField(urlString);
+                                if (rdoc) {
+                                    references.add(rdoc);
+                                    Doc.FindReferences(rdoc, references, system);
+                                }
+                            }
+                        }
                     }
                 } else if (field instanceof Promise) {
                     // eslint-disable-next-line no-debugger
diff --git a/src/server/GarbageCollector.ts b/src/server/GarbageCollector.ts
index 041f65592..74e8c288a 100644
--- a/src/server/GarbageCollector.ts
+++ b/src/server/GarbageCollector.ts
@@ -1,7 +1,4 @@
 /* eslint-disable no-await-in-loop */
-/* eslint-disable no-continue */
-/* eslint-disable no-cond-assign */
-/* eslint-disable no-restricted-syntax */
 import * as fs from 'fs';
 import * as path from 'path';
 import { Database } from './database';
-- 
cgit v1.2.3-70-g09d2


From 9779182e74e427d3a8a2004d0efd01fac8387d55 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 16 Oct 2024 17:31:12 -0400
Subject: major fixes to cardDeck view to simplify code and to make arch follow
 a true circle arc and to fix doc sizing when fitwidth/lightbox/etc.  fixes to
 flashcard UI for advancing to next Doc in cardView and carousel3D.

---
 src/client/documents/Documents.ts                  |   4 +-
 src/client/views/DocumentDecorations.tsx           |   4 +-
 .../views/collections/CollectionCardDeckView.scss  |  24 +-
 .../views/collections/CollectionCardDeckView.tsx   | 336 +++++++++++----------
 .../views/collections/CollectionCarousel3DView.tsx |   2 +-
 .../views/collections/CollectionCarouselView.tsx   |   2 +-
 .../views/collections/FlashcardPracticeUI.scss     |   6 +
 .../views/collections/FlashcardPracticeUI.tsx      |   6 +-
 src/client/views/global/globalScripts.ts           |  26 +-
 src/client/views/nodes/ComparisonBox.tsx           |  25 +-
 src/client/views/nodes/DocumentContentsView.tsx    |   1 -
 11 files changed, 230 insertions(+), 206 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 99af1f1a9..e539e3c65 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -508,8 +508,8 @@ export class DocumentOptions {
     userBackgroundColor?: STRt = new StrInfo('background color associated with a Dash user (seen in header fields of shared documents)');
     userColor?: STRt = new StrInfo('color associated with a Dash user (seen in header fields of shared documents)');
 
-    cardSort?: STRt = new StrInfo('way cards are sorted in deck view');
-    cardSort_isDesc?: BOOLt = new BoolInfo('whether the cards are sorted ascending or descending');
+    card_sort?: STRt = new StrInfo('way cards are sorted in deck view');
+    card_sort_isDesc?: BOOLt = new BoolInfo('whether the cards are sorted ascending or descending');
 }
 
 export const DocOptions = new DocumentOptions();
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 62f2de776..5a48b6c62 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -10,7 +10,7 @@ import { lightOrDark, returnFalse, setupMoveUpEvents } from '../../ClientUtils';
 import { Utils, emptyFunction, numberValue } from '../../Utils';
 import { DateField } from '../../fields/DateField';
 import { Doc, DocListCast, Field, FieldType, HierarchyMapping, ReverseHierarchyMap } from '../../fields/Doc';
-import { AclAdmin, AclAugment, AclEdit, DocData } from '../../fields/DocSymbols';
+import { AclAdmin, AclAugment, AclEdit, Animation, DocData } from '../../fields/DocSymbols';
 import { Id } from '../../fields/FieldSymbols';
 import { InkField } from '../../fields/InkField';
 import { ScriptField } from '../../fields/ScriptField';
@@ -650,7 +650,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
         this._forceRender;
         const { b, r, x, y } = this.Bounds;
         const seldocview = DocumentView.Selected().lastElement();
-        if (SnappingManager.IsDragging || r - x < 1 || x === Number.MAX_VALUE || !seldocview || SnappingManager.HideDecorations || isNaN(r) || isNaN(b) || isNaN(x) || isNaN(y)) {
+        if (SnappingManager.IsDragging || r - x < 1 || x === Number.MAX_VALUE || !seldocview || seldocview?.Document[Animation] || SnappingManager.HideDecorations || isNaN(r) || isNaN(b) || isNaN(x) || isNaN(y)) {
             setTimeout(
                 action(() => {
                     this._editingTitle = false;
diff --git a/src/client/views/collections/CollectionCardDeckView.scss b/src/client/views/collections/CollectionCardDeckView.scss
index 0637cd4e9..5283601bf 100644
--- a/src/client/views/collections/CollectionCardDeckView.scss
+++ b/src/client/views/collections/CollectionCardDeckView.scss
@@ -7,23 +7,31 @@
     background-color: white;
     overflow: hidden;
     display: flex;
+    .collectionCardView-inner {
+        display: flex;
+        transform-origin: top left;
+        align-items: center;
+    }
 
     button {
         border-radius: 50%;
     }
 }
 
-.card-wrapper {
-    display: grid;
-    grid-template-columns: repeat(10, 1fr);
+.collectionCardView-flashcardUI {
+    top: 0;
+    position: absolute;
+    width: 100%;
+    height: 100%;
     transform-origin: top left;
+}
 
-    position: absolute;
+.collectionCardView-cardwrapper {
+    display: grid;
+    grid-template-columns: repeat(10, 1fr);
+    transform-origin: left 50%;
     align-items: center;
-    justify-items: center;
-    justify-content: center;
-
-    transition: transform 0.3s cubic-bezier(0.455, 0.03, 0.515, 0.955);
+    z-index: 0; // so that setting z-index of active card doesn't make it land on top of things outside of the card-wrapper
 }
 
 .no-card-span {
diff --git a/src/client/views/collections/CollectionCardDeckView.tsx b/src/client/views/collections/CollectionCardDeckView.tsx
index 14ce9d2af..5faabacf4 100644
--- a/src/client/views/collections/CollectionCardDeckView.tsx
+++ b/src/client/views/collections/CollectionCardDeckView.tsx
@@ -5,7 +5,7 @@ import * as React from 'react';
 import { ClientUtils, DashColor, imageUrlToBase64, returnFalse, returnNever, returnZero } from '../../../ClientUtils';
 import { emptyFunction } from '../../../Utils';
 import { Doc } from '../../../fields/Doc';
-import { DocData } from '../../../fields/DocSymbols';
+import { Animation, DocData } from '../../../fields/DocSymbols';
 import { Id } from '../../../fields/FieldSymbols';
 import { List } from '../../../fields/List';
 import { ScriptField } from '../../../fields/ScriptField';
@@ -96,7 +96,7 @@ export class CollectionCardView extends CollectionSubView() {
                 if (isVis) {
                     this.openChatPopup();
                 } else {
-                    this.Document.cardSort = this.cardSort === cardSortings.Chat ? '' : this.Document.cardSort;
+                    this.Document.card_sort = this.cardSort === cardSortings.Chat ? '' : this.Document.card_sort;
                 }
             }
         );
@@ -119,7 +119,25 @@ export class CollectionCardView extends CollectionSubView() {
     }
 
     @computed get cardSort() {
-        return StrCast(this.Document.cardSort) as cardSortings;
+        return StrCast(this.Document.card_sort) as cardSortings;
+    }
+    /**
+     * Number of rows of cards to be rendered
+     */
+    @computed get numRows() {
+        return Math.ceil(this.sortedDocs.length / this._maxRowCount);
+    }
+    /**
+     * Circle arc size, in radians, to layout cards
+     */
+    @computed get archAngle() {
+        return NumCast(this.layoutDoc.card_arch, 90) * (Math.PI / 180) * (this.childCards.length < this._maxRowCount ? this.childCards.length / this._maxRowCount : 1);
+    }
+    /**
+     * Spacing card rows as a percent of Doc size.   100 means rows spread out to fill 100% of the Doc vertically.  Default is 60%
+     */
+    @computed get cardSpacing() {
+        return NumCast(this.layoutDoc.card_spacing, 60);
     }
 
     /**
@@ -137,6 +155,10 @@ export class CollectionCardView extends CollectionSubView() {
         return (this.childPanelWidth() * length) / this._props.PanelWidth();
     }
 
+    @computed get nativeScaling() {
+        return this._props.NativeDimScaling?.() || 1;
+    }
+
     /**
      * When in quiz mode, randomly selects a document
      */
@@ -145,70 +167,17 @@ export class CollectionCardView extends CollectionSubView() {
         this._curDoc = this.childDocs[randomIndex];
     });
 
-    /**
-     * Number of rows of cards to be rendered
-     */
-    @computed get numRows() {
-        return Math.ceil(this.sortedDocs.length / this._maxRowCount);
-    }
-
-    @action
-    setHoveredNodeIndex = (index: number) => {
+    setHoveredNodeIndex = action((index: number) => {
         if (!SnappingManager.IsDragging) this._hoveredNodeIndex = index;
-    };
+    });
 
     isSelected = (doc: Doc) => this._docRefs.get(doc)?.IsSelected;
-    childPanelWidth = () => NumCast(this.layoutDoc.childPanelWidth, this._props.PanelWidth() / 2);
+    childPanelWidth = () => NumCast(this.layoutDoc.childPanelWidth, Math.max(150, this._props.PanelWidth() / (this.childCards.length > this._maxRowCount ? this._maxRowCount : this.childCards.length) / this.nativeScaling));
     childPanelHeight = () => this._props.PanelHeight() * this.fitContentScale;
     onChildDoubleClick = () => ScriptCast(this.layoutDoc.onChildDoubleClick);
     isContentActive = () => this._props.isSelected() || this._props.isContentActive() || this.isAnyChildContentActive();
     isAnyChildContentActive = this._props.isAnyChildContentActive;
 
-    /**
-     * Returns the degree to rotate a card dependind on the amount of cards in their row and their index in said row
-     * @param amCards
-     * @param index
-     * @returns
-     */
-    rotate = (amCards: number, index: number) => {
-        if (amCards == 1) return 0;
-
-        const possRotate = -30 + index * (30 / ((amCards - (amCards % 2)) / 2));
-        if (amCards % 2 === 0) {
-            if (possRotate === 0) {
-                return possRotate + Math.abs(-30 + (index - 1) * (30 / (amCards / 2)));
-            }
-            if (index > (amCards + 1) / 2) {
-                const stepMag = Math.abs(-30 + (amCards / 2 - 1) * (30 / ((amCards - (amCards % 2)) / 2)));
-                return possRotate + stepMag;
-            }
-        }
-
-        return possRotate;
-    };
-    /**
-     * Returns the degree to which a card should be translated in the y direction for the arch effect
-     */
-    translateY = (amCards: number, index: number, realIndex: number) => {
-        const evenOdd = amCards % 2;
-        const apex = (amCards - evenOdd) / 2;
-        const Magnitude = this.childPanelWidth() / 2; // 400
-        const stepMag = Magnitude / 2 / ((amCards - evenOdd) / 2) + Math.abs((apex - index) * 25);
-
-        let rowOffset = 0;
-        if (realIndex > this._maxRowCount - 1) {
-            rowOffset = Magnitude * ((realIndex - (realIndex % this._maxRowCount)) / this._maxRowCount);
-        }
-        if (evenOdd === 1 || index < apex - 1) {
-            return Math.abs(stepMag * (apex - index)) - rowOffset;
-        }
-        if (index === apex || index === apex - 1) {
-            return 0 - rowOffset;
-        }
-
-        return Math.abs(stepMag * (apex - index - 1)) - rowOffset;
-    };
-
     /**
      * When dragging a card, determines the index the card should be set to if dropped
      * @param mouseX mouse's x location
@@ -216,28 +185,27 @@ export class CollectionCardView extends CollectionSubView() {
      * @returns the card's new index
      */
     findCardDropIndex = (mouseX: number, mouseY: number) => {
-        const amCardsTotal = this.sortedDocs.length;
+        const cardCount = this.sortedDocs.length;
         let index = 0;
-        const cardWidth = amCardsTotal < this._maxRowCount ? this._props.PanelWidth() / amCardsTotal : this._props.PanelWidth() / this._maxRowCount;
+        const cardWidth = cardCount < this._maxRowCount ? this._props.PanelWidth() / cardCount : this._props.PanelWidth() / this._maxRowCount;
 
         // Calculate the adjusted X position accounting for the initial offset
         let adjustedX = mouseX;
 
-        const amRows = Math.ceil(amCardsTotal / this._maxRowCount);
-        const rowHeight = this._props.PanelHeight() / amRows;
+        const rowHeight = this._props.PanelHeight() / this.numRows;
         const currRow = Math.floor((mouseY - 100) / rowHeight); //rows start at 0
 
         if (adjustedX < 0) {
             return 0; // Before the first column
         }
 
-        if (amCardsTotal < this._maxRowCount) {
+        if (cardCount < this._maxRowCount) {
             index = Math.floor(adjustedX / cardWidth);
-        } else if (currRow != amRows - 1) {
+        } else if (currRow != this.numRows - 1) {
             index = Math.floor(adjustedX / cardWidth) + currRow * this._maxRowCount;
         } else {
-            const rowAmCards = amCardsTotal - currRow * this._maxRowCount;
-            const offset = ((this._maxRowCount - rowAmCards) / 2) * cardWidth;
+            const cardsInRow = cardCount - currRow * this._maxRowCount;
+            const offset = ((this._maxRowCount - cardsInRow) / 2) * cardWidth;
             adjustedX = mouseX - offset;
 
             index = Math.floor(adjustedX / cardWidth) + currRow * this._maxRowCount;
@@ -246,11 +214,14 @@ export class CollectionCardView extends CollectionSubView() {
     };
 
     /**
-     * Checks to see if a card is being dragged and calls the appropriate methods if so
+     * if pointer moves over cardDeck while dragging a Doc that is in the Deck or that can be dropped in the deck,
+     * then this sets the card index where the dragged card would be added.
      */
     @action
     onPointerMove = (x: number, y: number) => {
-        this._docDraggedIndex = DragManager.docsBeingDragged.length ? this.findCardDropIndex(x, y) : -1;
+        if (DragManager.docsBeingDragged.some(doc => this.sortedDocs.includes(doc)) || SnappingManager.CanEmbed) {
+            this._docDraggedIndex = this.findCardDropIndex(x, y);
+        }
     };
 
     /**
@@ -269,7 +240,7 @@ export class CollectionCardView extends CollectionSubView() {
                     const sorted = this.sortedDocs;
                     const originalIndex = sorted.findIndex(doc => doc === draggedDoc);
 
-                    this.Document.cardSort = '';
+                    this.Document.card_sort = '';
                     originalIndex !== -1 && sorted.splice(originalIndex, 1);
                     sorted.splice(dragIndex, 0, draggedDoc);
                     if (de.complete.docDragData.removeDocument?.(draggedDoc)) {
@@ -285,15 +256,6 @@ export class CollectionCardView extends CollectionSubView() {
         ''
     );
 
-    @computed get sortedDocs() {
-        return this.sort(
-            this.childCards.map(card => card.layout),
-            this.cardSort,
-            BoolCast(this.Document.cardSort_isDesc),
-            this._docDraggedIndex
-        );
-    }
-
     /**
      * Used to determine how to sort cards based on tags. The leftmost tags are given lower values while cards to the right are
      * given higher values. Decimals are used to determine placement for cards with multiple tags
@@ -341,6 +303,15 @@ export class CollectionCardView extends CollectionSubView() {
         return docs;
     };
 
+    @computed get sortedDocs() {
+        return this.sort(
+            this.childCards.map(card => card.layout),
+            this.cardSort,
+            BoolCast(this.Document.card_sort_isDesc),
+            this._docDraggedIndex
+        );
+    }
+
     isChildContentActive = computedFn(
         (doc: Doc) => () =>
             this._props.isContentActive?.() === false
@@ -359,74 +330,99 @@ export class CollectionCardView extends CollectionSubView() {
             Document={doc}
             NativeWidth={returnZero}
             NativeHeight={returnZero}
-            fitWidth={returnFalse}
-            onDoubleClickScript={this.onChildDoubleClick}
+            PanelWidth={this.childPanelWidth}
+            PanelHeight={this.childPanelHeight}
             renderDepth={this._props.renderDepth + 1}
             LayoutTemplate={this._props.childLayoutTemplate}
             LayoutTemplateString={this._props.childLayoutString}
             containerViewPath={this.childContainerViewPath}
             ScreenToLocalTransform={screenToLocalTransform} // makes sure the box wrapper thing is in the right spot
             isDocumentActive={this._props.childDocumentsActive?.() || this.Document._childDocumentsActive ? this._props.isDocumentActive : this.isContentActive}
-            PanelWidth={this.childPanelWidth}
-            PanelHeight={this.childPanelHeight}
+            isContentActive={this.isChildContentActive(doc)}
+            fitWidth={returnFalse}
             waitForDoubleClickToClick={returnNever}
             scriptContext={this}
+            onDoubleClickScript={this.onChildDoubleClick}
             onClickScript={this._curDoc === doc ? undefined : this._clickScript}
             dontCenter="y" // Don't center it vertically, because the grid it's in is already doing that and we don't want to do it twice.
             dragAction={(this.Document.childDragAction ?? this._props.childDragAction) as dropActionType}
             showTags={BoolCast(this.layoutDoc.showChildTags)}
             whenChildContentsActiveChanged={this._props.whenChildContentsActiveChanged}
-            isContentActive={this.isChildContentActive(doc)}
             dontHideOnDrag
         />
     );
 
     /**
      * Determines how many cards are in the row of a card at a specific index
-     * @param index
-     * @returns
+     * @param index numerical index of card in total list of all cards
+     * @returns number of cards in row that contains index
      */
-    overflowAmCardsCalc = (index: number) => {
-        if (this.sortedDocs.length < this._maxRowCount) {
-            return this.sortedDocs.length;
+    cardsInRowThatIncludesCardIndex = (index: number) => {
+        if (this.childCards.length < this._maxRowCount) {
+            return this.childCards.length;
         }
-        const totalCards = this.sortedDocs.length;
-        // if 9 or less
+        const totalCards = this.childCards.length;
         if (index < totalCards - (totalCards % this._maxRowCount)) {
             return this._maxRowCount;
         }
         return totalCards % this._maxRowCount;
     };
     /**
-     * Determines the index a card is in in a row
-     * @param realIndex
-     * @returns
+     * Determines the index a card is in in a row.  If the row is not full, then the cards
+     * are centered within the row (as if unrendered cards had been added to the start and end
+     * of the row) and the retuned index is the index the card in this virtual full row.
+     * @param index numerical index of card in total list of all cards
+     * @returns index of card in its row, normalized to a full size row
      */
-    overflowIndexCalc = (realIndex: number) => realIndex % this._maxRowCount;
+    centeredIndexOfCardInRow = (index: number) => {
+        const cardsInRow = this.cardsInRowThatIncludesCardIndex(index);
+        const lineIndex = index % this._maxRowCount;
+        if (cardsInRow === this._maxRowCount) return lineIndex;
+        return lineIndex + (this._maxRowCount - cardsInRow) / 2;
+    };
     /**
-     * Translates the cards in the second rows and beyond over to the right
-     * @param realIndex
-     * @param calcIndex
-     * @param calcRowCards
-     * @returns
+     * Returns the rotation of a card in radians based on its horizontal location (and thus m apping to a circle arc).
+     * The amount of rotation is goverend by the Doc's card_arch field which specifies, in degrees, the range of a circle
+     * arc that cards should cover -- by default, -45 to 45 degrees.
+     * @param index numerical index of card in total list of all cards
+     * @returns angle of rotation in radians
+     */
+    rotate = (index: number) => {
+        const cardsInRow = this.cardsInRowThatIncludesCardIndex(index);
+        const centeredIndexInRow = (cardsInRow < this._maxRowCount ? index + (this._maxRowCount - cardsInRow) / 2 : index) % this._maxRowCount;
+        const rowIndexMax = this._maxRowCount - 1;
+        return ((this.archAngle / 2) * (centeredIndexInRow - rowIndexMax / 2)) / (rowIndexMax / 2);
+    };
+    /**
+     * Provides a vertical adjustment to a card's grid position so that it will lie along an arch.
+     * @param index numerical index of card in total list of all cards
+     */
+    translateY = (index: number) => {
+        const Magnitude = ((this._props.PanelHeight() * this.fitContentScale) / 2) * Math.sqrt(((this.archAngle * (180 / Math.PI)) / 60) * 4);
+        return Magnitude * (1 - Math.sin(this.rotate(index) + Math.PI / 2) - (1 - Math.sin(this.archAngle / 2 + Math.PI / 2)) / 2);
+    };
+    /**
+     * When the card index is for a row (not the first row) that is not full, this returns a horizontal adjustment that centers the row
+     * @param index index of card from start of deck
+     * @param cardsInRow number of cards in the row containing the indexed card
+     * @returns horizontal pixel translation
      */
-    translateOverflowX = (realIndex: number, calcRowCards: number) => (realIndex < this._maxRowCount ? 0 : (this._maxRowCount - calcRowCards) * (this.childPanelWidth() / 2));
+    horizontalAdjustmentForPartialRows = (index: number, cardsInRow: number) => (index < this._maxRowCount ? 0 : (this._maxRowCount - cardsInRow) * (this.childPanelWidth() / 2));
 
     /**
-     * Determines how far to translate a card in the y direction depending on its index and if it's selected
+     * Adjusts the vertical placement of the card from its grid position so that it will either line on a
+     * circular arc if the card isn't active, or so that it will be centered otherwise.
      * @param isActive whether the card is focused for interaction
-     * @param realIndex index of card from start of deck
-     * @param amCards ??
-     * @param calcRowIndex index of card from start of row
-     * @returns Y translation of card
+     * @param index index of card from start of deck
+     * @returns vertical pixel translation
      */
-    calculateTranslateY = (isActive: boolean, realIndex: number, amCards: number, calcRowIndex: number) => {
-        const rowHeight = (this._props.PanelHeight() * this.fitContentScale) / this.numRows;
-        const rowIndex = Math.trunc(realIndex / this._maxRowCount);
+    adjustCardYtoFitArch = (isActive: boolean, index: number) => {
+        const rowHeight = this._props.PanelHeight() / this.numRows;
+        const rowIndex = Math.floor(index / this._maxRowCount);
         const rowToCenterShift = this.numRows / 2 - rowIndex;
-        if (isActive) return rowToCenterShift * rowHeight - rowHeight / 2;
-        if (amCards == 1) return 50 * this.fitContentScale;
-        return this.translateY(amCards, calcRowIndex, realIndex);
+        return isActive
+            ? (rowToCenterShift * rowHeight - rowHeight / 2) * ((this.cardSpacing * this.fitContentScale) / 100) //
+            : this.translateY(index);
     };
 
     /**
@@ -493,7 +489,7 @@ export class CollectionCardView extends CollectionSubView() {
         }
 
         if (questionType === '6') {
-            this.Document.cardSort = 'chat';
+            this.Document.card_sort = 'chat';
         }
 
         listItems.forEach((item, index) => {
@@ -549,7 +545,7 @@ export class CollectionCardView extends CollectionSubView() {
         await this.childPairStringListAndUpdateSortDesc();
     };
 
-    childScreenToLocal = computedFn((doc: Doc, index: number, calcRowIndex: number, isSelected: boolean, amCards: number) => () => {
+    childScreenToLocal = computedFn((doc: Doc, index: number, isSelected: boolean) => () => {
         // need to explicitly trigger an invalidation since we're reading everything from the Dom
         this._forceChildXf;
         this._props.ScreenToLocalTransform();
@@ -560,24 +556,40 @@ export class CollectionCardView extends CollectionSubView() {
 
         return new Transform(-translateX + (dref?.centeringX || 0) * scale, 
                              -translateY + (dref?.centeringY || 0) * scale, 1)
-                  .scale(1 / scale).rotate(!isSelected ? -this.rotate(amCards, calcRowIndex) : 0); // prettier-ignore
+                  .scale(1 / scale).rotate(!isSelected ? -this.rotate(this.centeredIndexOfCardInRow(index)) : 0); // prettier-ignore
     });
 
+    /**
+     * Releases the currently focused Doc by deselecting it and returning it to its location on the arch, and selecting the
+     * cardDeck itself.
+     * This will also force the Doc to recompute its layout transform when the animation completes.
+     * In addition, this sets an animating flag on the Doc so that it will receive no poiner events when animating, such as hover
+     * events that would trigger a flashcard to flip.
+     * @param doc doc that will be animated away from center focus
+     */
+    releaseCurDoc = action(() => {
+        const selDoc = this._curDoc;
+        this._curDoc = undefined;
+        const cardDocView = DocumentView.getDocumentView(selDoc, this.DocumentView?.());
+        if (cardDocView && selDoc) {
+            DocumentView.DeselectView(cardDocView);
+            this._props.select(false);
+            selDoc[Animation] = selDoc; // turns off pointer events & doc decorations while animating - useful for flashcards that reveal back on hover
+            setTimeout(action(() => {
+                selDoc[Animation] = undefined;
+                this._forceChildXf++;
+            }), 350); // prettier-ignore
+        }
+    });
+
+    /**
+     * turns off the _dropped flag at the end of a drag/drop, or releases the focused Doc if a different Doc is clicked
+     */
     cardPointerUp = action((doc: Doc) => {
-        // if a card doc has just moved, or a card is selected and in front, then ignore this event
         if (this._curDoc === doc || this._dropped) {
             this._dropped = false;
         } else {
-            // otherwise, turn off documentDecorations becase we're in a selection transition and want to avoid artifacts.
-            // Turn them back on when the animation has completed and the render and backend structures are in synch
-            SnappingManager.SetHideDecorations(true);
-            setTimeout(
-                action(() => {
-                    SnappingManager.SetHideDecorations(false);
-                    this._forceChildXf++;
-                }),
-                1000
-            );
+            this.releaseCurDoc(); // NOTE: the onClick script for the card will select the new card (ie, 'doc')
         }
     });
 
@@ -585,25 +597,17 @@ export class CollectionCardView extends CollectionSubView() {
      * Actually renders all the cards
      */
     @computed get renderCards() {
-        if (!this.childCards.length) {
-            return null;
-        }
-
+        console.log('CHILDPw = ' + this.childPanelWidth());
         // Map sorted documents to their rendered components
         return this.sortedDocs.map((doc, index) => {
-            const calcRowIndex = this.overflowIndexCalc(index);
-            const amCards = this.overflowAmCardsCalc(index);
+            const cardsInRow = this.cardsInRowThatIncludesCardIndex(index);
+
+            const childScreenToLocal = this.childScreenToLocal(doc, index, doc === this._curDoc);
 
-            const childScreenToLocal = this.childScreenToLocal(doc, index, calcRowIndex, doc === this._curDoc, amCards);
+            const translateToCenterIfActive = () => (doc === this._curDoc ? (cardsInRow / 2 - (index % this._maxRowCount)) * 100 - 50 : 0);
 
-            const translateIfSelected = () => {
-                const indexInRow = index % this._maxRowCount;
-                const rowIndex = Math.trunc(index / this._maxRowCount);
-                const rowCenterIndex = Math.min(this._maxRowCount, this.sortedDocs.length - rowIndex * this._maxRowCount) / 2;
-                return (rowCenterIndex - indexInRow) * 100 - 50;
-            };
             const aspect = NumCast(doc.height) / NumCast(doc.width, 1);
-            const vscale = Math.max(1,Math.min((this._props.PanelHeight() * 0.95 * this.fitContentScale) / (aspect * this.childPanelWidth()), 
+            const vscale = Math.max(1,Math.min((this._props.PanelHeight() * 0.95 * this.fitContentScale * this.nativeScaling) / (aspect * this.childPanelWidth()), 
                                     (this._props.PanelHeight() - 80) / (aspect * (this._props.PanelWidth() / 10)))); // prettier-ignore
             const hscale = Math.min(this.sortedDocs.length, this._maxRowCount) / 2; // bcz: hack - the grid is divided evenly into maxRowCount cells, so the max scaling would be maxRowCount -- but making things that wide is ugly, so cap it off at half the window size
             return (
@@ -614,9 +618,9 @@ export class CollectionCardView extends CollectionSubView() {
                     style={{
                         width: this.childPanelWidth(),
                         height: 'max-content',
-                        transform: `translateY(${this.calculateTranslateY(doc === this._curDoc, index, amCards, calcRowIndex)}px)
-                                    translateX(calc(${doc === this._curDoc ? translateIfSelected() : 0}% + ${this.translateOverflowX(index, amCards)}px))
-                                    rotate(${doc !== this._curDoc ? this.rotate(amCards, calcRowIndex) : 0}deg)
+                        transform: `translateY(${this.adjustCardYtoFitArch(doc === this._curDoc, index)}px)
+                                    translateX(calc(${translateToCenterIfActive()}% + ${this.horizontalAdjustmentForPartialRows(index, cardsInRow)}px))
+                                    rotate(${doc !== this._curDoc ? this.rotate(index) : 0}rad)
                                     scale(${doc === this._curDoc ? `${Math.min(hscale, vscale) * 100}%` : this._hoveredNodeIndex === index ? 1.1 : 1})`,
                     }} // prettier-ignore
                     onPointerEnter={() => this.setHoveredNodeIndex(index)}
@@ -636,26 +640,19 @@ export class CollectionCardView extends CollectionSubView() {
         ScreenToLocalTransform: this.contentScreenToLocalXf,
     });
     answered = action(() => {
-        this._curDoc = this.curDoc ? this.filteredChildDocs()[(this.filteredChildDocs().findIndex(this.curDoc) + 1) % (this.filteredChildDocs().length || 1)] : undefined;
+        this._curDoc = this.curDoc ? this.filteredChildDocs()[(this.filteredChildDocs().findIndex(d => d === this.curDoc()) + 1) % (this.filteredChildDocs().length || 1)] : undefined;
     });
     curDoc = () => this._curDoc;
 
     render() {
-        const isEmpty = this.childCards.length === 0;
+        const fitContentScale = this.childCards.length === 0 ? 1 : this.fitContentScale;
         return (
             <div
                 className="collectionCardView-outer"
                 ref={(ele: HTMLDivElement | null) => this.createDashEventsTarget(ele)}
-                onPointerDown={action(() => {
-                    this._curDoc = undefined;
-                    SnappingManager.SetHideDecorations(true);
-                    setTimeout(
-                        action(() => {
-                            SnappingManager.SetHideDecorations(false);
-                            this._forceChildXf++;
-                        }),
-                        1000
-                    );
+                onPointerDown={action(e => {
+                    if (e.button === 2 || e.ctrlKey) return;
+                    this.releaseCurDoc();
                 })}
                 onPointerLeave={action(() => (this._docDraggedIndex = -1))}
                 onPointerMove={e => this.onPointerMove(...this._props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY))}
@@ -665,16 +662,31 @@ export class CollectionCardView extends CollectionSubView() {
                     color: this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Color) as string,
                 }}>
                 <div
-                    className="card-wrapper"
+                    className="collectionCardView-inner"
                     style={{
-                        ...(!isEmpty && { transform: `scale(${1 / this.fitContentScale})` }),
-                        ...{ height: `${100 * (isEmpty ? 1 : this.fitContentScale)}%` },
-                        ...{ width: `${100 * (isEmpty ? 1 : this.fitContentScale)}%` },
-                        gridAutoRows: `${100 / this.numRows}%`,
+                        transform: `scale(${1 / fitContentScale})`,
+                        height: `${100 * fitContentScale}%`,
+                        width: `${100 * fitContentScale}%`,
                     }}>
-                    {this.renderCards}
+                    <div
+                        className="collectionCardView-cardwrapper"
+                        style={{
+                            gridAutoRows: `${100 / this.numRows}%`,
+                            height: `${this.cardSpacing}%`,
+                        }}>
+                        {this.renderCards}
+                    </div>
+                    <div
+                        className="collectionCardView-flashcardUI"
+                        style={{
+                            pointerEvents: this.childCards.length === 0 ? undefined : 'none',
+                            height: `${100 / this.nativeScaling / fitContentScale}%`,
+                            width: `${100 / this.nativeScaling / fitContentScale}%`,
+                            transform: `scale(${this.nativeScaling * fitContentScale})`,
+                        }}>
+                        {this.flashCardUI(this.curDoc, this.docViewProps, this.answered)}
+                    </div>
                 </div>
-                {this.flashCardUI(this.curDoc, this.docViewProps, this.answered)}
             </div>
         );
     }
diff --git a/src/client/views/collections/CollectionCarousel3DView.tsx b/src/client/views/collections/CollectionCarousel3DView.tsx
index e9ace733e..a71cc43ba 100644
--- a/src/client/views/collections/CollectionCarousel3DView.tsx
+++ b/src/client/views/collections/CollectionCarousel3DView.tsx
@@ -205,7 +205,7 @@ export class CollectionCarousel3DView extends CollectionSubView() {
 
     onPassiveWheel = (e: WheelEvent) => e.stopPropagation();
     curDoc = () => this.carouselItems[NumCast(this.layoutDoc._carousel_index)]?.layout;
-    answered = (correct: boolean) => (!correct || !this.curDoc()) && this.changeSlide(1);
+    answered = (correct: boolean) => (!correct || !this.curDoc() || NumCast(this.layoutDoc._carousel_index) === this.carouselItems.length - 1) && this.changeSlide(1);
     docViewProps = () => ({
         ...this._props, //
         isDocumentActive: this._props.childDocumentsActive?.() ? this._props.isDocumentActive : this._props.isContentActive,
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index ff587b199..1f2bc908f 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -203,7 +203,7 @@ export class CollectionCarouselView extends CollectionSubView() {
         isContentActive: this.isChildContentActive,
         ScreenToLocalTransform: this.contentScreenToLocalXf,
     });
-    answered = () => this.advance();
+    answered = (correct: boolean) => (!correct || !this.curDoc() || NumCast(this.layoutDoc._carousel_index) === this.carouselItems.length - 1) && this.advance();
 
     render() {
         return (
diff --git a/src/client/views/collections/FlashcardPracticeUI.scss b/src/client/views/collections/FlashcardPracticeUI.scss
index 4ed27793d..210c6798f 100644
--- a/src/client/views/collections/FlashcardPracticeUI.scss
+++ b/src/client/views/collections/FlashcardPracticeUI.scss
@@ -1,3 +1,9 @@
+.FlashcardPracticeUI {
+    width: 100%;
+    height: 100%;
+    display: flex;
+    align-items: center;
+}
 .FlashcardPracticeUI-remove,
 .FlashcardPracticeUI-check {
     position: absolute;
diff --git a/src/client/views/collections/FlashcardPracticeUI.tsx b/src/client/views/collections/FlashcardPracticeUI.tsx
index 79eb7f107..ec892ee3d 100644
--- a/src/client/views/collections/FlashcardPracticeUI.tsx
+++ b/src/client/views/collections/FlashcardPracticeUI.tsx
@@ -104,8 +104,8 @@ export class FlashcardPracticeUI extends ObservableReactComponent<PracticeUIProp
         const setPracticeVal = (e: React.MouseEvent, val: string) => {
             e.stopPropagation();
             const curDoc = this._props.curDoc();
-            curDoc && (curDoc[this.practiceField] = val);
             this._props.advance?.(val === practiceVal.CORRECT);
+            curDoc && (curDoc[this.practiceField] = val);
         };
 
         return this.practiceMode == practiceMode.PRACTICE && this._props.curDoc() ? (
@@ -182,7 +182,7 @@ export class FlashcardPracticeUI extends ObservableReactComponent<PracticeUIProp
     tryFilterOut = (doc: Doc) => (this.practiceMode && BoolCast(doc?._layout_isFlashcard) && doc[this.practiceField] === practiceVal.CORRECT ? true : false); // show only cards that aren't marked as correct
     render() {
         return (
-            <>
+            <div className="FlashcardPracticeUI">
                 {this.emptyMessage}
                 {this.practiceButtons}
                 {this._props.layoutDoc._chromeHidden ? null : (
@@ -208,7 +208,7 @@ export class FlashcardPracticeUI extends ObservableReactComponent<PracticeUIProp
                         {this.practiceModesMenu}
                     </div>
                 )}
-            </>
+            </div>
         );
     }
 }
diff --git a/src/client/views/global/globalScripts.ts b/src/client/views/global/globalScripts.ts
index 903e04ad7..954c79f7d 100644
--- a/src/client/views/global/globalScripts.ts
+++ b/src/client/views/global/globalScripts.ts
@@ -189,38 +189,38 @@ ScriptingGlobals.add(function showFreeform(
             setDoc: (doc: Doc, dv: DocumentView) => { Doc.UserDoc().defaultToFlashcards = !Doc.UserDoc().defaultToFlashcards}, // prettier-ignore
         }],
         ['time', {
-            checkResult: (doc: Doc) => StrCast(doc?.cardSort) === "time",
-            setDoc: (doc: Doc, dv: DocumentView) => { doc.cardSort === "time" ? doc.cardSort = '' : doc.cardSort = 'time'}, // prettier-ignore
+            checkResult: (doc: Doc) => StrCast(doc?.card_sort) === "time",
+            setDoc: (doc: Doc, dv: DocumentView) => { doc.card_sort === "time" ? doc.card_sort = '' : doc.card_sort = 'time'}, // prettier-ignore
         }],
         ['docType', {
-            checkResult: (doc: Doc) => StrCast(doc?.cardSort) === "type",
-            setDoc: (doc: Doc, dv: DocumentView) => { doc.cardSort === "type" ? doc.cardSort = '' : doc.cardSort = 'type'}, // prettier-ignore
+            checkResult: (doc: Doc) => StrCast(doc?.card_sort) === "type",
+            setDoc: (doc: Doc, dv: DocumentView) => { doc.card_sort === "type" ? doc.card_sort = '' : doc.card_sort = 'type'}, // prettier-ignore
         }],
         ['color', {
-            checkResult: (doc: Doc) => StrCast(doc?.cardSort) === "color",
-            setDoc: (doc: Doc, dv: DocumentView) => { doc.cardSort === "color" ? doc.cardSort = '' : doc.cardSort = 'color'}, // prettier-ignore
+            checkResult: (doc: Doc) => StrCast(doc?.card_sort) === "color",
+            setDoc: (doc: Doc, dv: DocumentView) => { doc.card_sort === "color" ? doc.card_sort = '' : doc.card_sort = 'color'}, // prettier-ignore
         }],
         ['tag', {
-            checkResult: (doc: Doc) => StrCast(doc?.cardSort) === "tag",
-            setDoc: (doc: Doc, dv: DocumentView) => { doc.cardSort === "tag" ? doc.cardSort = '' : doc.cardSort = 'tag'}, // prettier-ignore
+            checkResult: (doc: Doc) => StrCast(doc?.card_sort) === "tag",
+            setDoc: (doc: Doc, dv: DocumentView) => { doc.card_sort === "tag" ? doc.card_sort = '' : doc.card_sort = 'tag'}, // prettier-ignore
         }],
         ['up', {
-            checkResult: (doc: Doc) => BoolCast(!doc?.cardSort_isDesc),
+            checkResult: (doc: Doc) => BoolCast(!doc?.card_sort_isDesc),
             setDoc: (doc: Doc, dv: DocumentView) => {
-                doc.cardSort_isDesc = false;
+                doc.card_sort_isDesc = false;
             },
         }],
         ['down', {
-            checkResult: (doc: Doc) => BoolCast(doc?.cardSort_isDesc),
+            checkResult: (doc: Doc) => BoolCast(doc?.card_sort_isDesc),
             setDoc: (doc: Doc, dv: DocumentView) => {
-                doc.cardSort_isDesc = true;
+                doc.card_sort_isDesc = true;
             },
         }],
         ['toggle-chat', {
             checkResult: (doc: Doc) => GPTPopup.Instance.visible,
             setDoc: (doc: Doc, dv: DocumentView) => {
                 if (GPTPopup.Instance.visible){
-                    doc.cardSort = ''
+                    doc.card_sort = ''
                     GPTPopup.Instance.setVisible(false);
    
                 } else {
diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx
index 38ce5f2f7..3c126ea4a 100644
--- a/src/client/views/nodes/ComparisonBox.tsx
+++ b/src/client/views/nodes/ComparisonBox.tsx
@@ -8,7 +8,7 @@ import ReactLoading from 'react-loading';
 import { imageUrlToBase64, returnFalse, returnNone, returnTrue, returnZero, setupMoveUpEvents } from '../../../ClientUtils';
 import { emptyFunction } from '../../../Utils';
 import { Doc, Opt } from '../../../fields/Doc';
-import { DocData } from '../../../fields/DocSymbols';
+import { Animation, DocData } from '../../../fields/DocSymbols';
 import { RichTextField } from '../../../fields/RichTextField';
 import { BoolCast, DocCast, NumCast, RTFCast, StrCast, toList } from '../../../fields/Types';
 import { nullAudio } from '../../../fields/URLField';
@@ -18,7 +18,6 @@ import { DocumentType } from '../../documents/DocumentTypes';
 import { Docs } from '../../documents/Documents';
 import { DragManager } from '../../util/DragManager';
 import { dropActionType } from '../../util/DropActionTypes';
-import { SnappingManager } from '../../util/SnappingManager';
 import { undoable } from '../../util/UndoManager';
 import { ContextMenu } from '../ContextMenu';
 import { ViewBoxAnnotatableComponent } from '../DocComponent';
@@ -235,7 +234,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
     @computed get uiBtnScaling() { return Math.max(this.maxWidgetSize / this._sideBtnWidth, 1) * Math.min(1, this.viewScaling)* (this._props.NativeDimScaling?.() ?? 1); } // prettier-ignore
 
     @computed get flashcardMenu() {
-        return SnappingManager.HideDecorations ? null : (
+        return (
             <div className="comparisonBox-bottomMenu" style={{ transform: `scale(${this.uiBtnScaling})` }}>
                 {this.revealOpHover || !this._props.isSelected() ? null : this.overlayAlternateIcon}
                 {!this._props.isSelected() || this._renderSide === this.frontKey ? null : (
@@ -666,20 +665,20 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
             <>
                 <DocumentView
                     {...this._props}
-                    showTags={undefined}
-                    fitWidth={undefined} // set to returnTrue to make images fill the comparisonBox-- should be a user option
-                    ignoreUsePath={layoutString ? true : undefined}
-                    renderDepth={this.props.renderDepth + 1}
-                    LayoutTemplateString={layoutString}
                     Document={layoutString ? this.Document : targetDoc}
-                    containerViewPath={this._props.docViewPath}
-                    moveDocument={whichSlot === this.frontKey ? this.moveDocFront : this.moveDocBack}
-                    removeDocument={whichSlot === this.frontKey ? this.remDocFront : this.remDocBack}
                     NativeWidth={returnZero}
                     NativeHeight={returnZero}
+                    renderDepth={this.props.renderDepth + 1}
+                    LayoutTemplateString={layoutString}
+                    containerViewPath={this._props.docViewPath}
                     ScreenToLocalTransform={this.contentScreenToLocalXf}
-                    isContentActive={this.childActiveFunc}
                     isDocumentActive={returnFalse}
+                    isContentActive={this.childActiveFunc}
+                    showTags={undefined}
+                    fitWidth={undefined} // set to returnTrue to make images fill the comparisonBox-- should be a user option
+                    ignoreUsePath={layoutString ? true : undefined}
+                    moveDocument={whichSlot === this.frontKey ? this.moveDocFront : this.moveDocBack}
+                    removeDocument={whichSlot === this.frontKey ? this.remDocFront : this.remDocBack}
                     dontSelect={returnTrue}
                     whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
                     styleProvider={this._childActive ? this._props.styleProvider : this.docStyleProvider}
@@ -795,7 +794,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
             [flashcardRevealOp.SLIDE, this.renderAsBeforeAfter]]); // prettier-ignore
         if (this.isQuizMode) this.renderAsQuiz(this.frontText);
         return (
-            <div className="comparisonBox" style={{ pointerEvents: this._props.isContentActive() ? 'unset' : undefined }} onContextMenu={this.flashcardContextMenu}>
+            <div className="comparisonBox" style={{ pointerEvents: this._props.isContentActive() && !this.Document[Animation] ? 'unset' : undefined }} onContextMenu={this.flashcardContextMenu}>
                 {renderMode.get(this.revealOp)?.() ?? null}
                 {this.loading ? (
                     <div className="loading-spinner">
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 9aa000ba7..aab8a183a 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -1,4 +1,3 @@
-/* eslint-disable react/require-default-props */
 import { computed, makeObservable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
-- 
cgit v1.2.3-70-g09d2


From dd93f5175064850c6c0e47f025cd7bbba1f23106 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 17 Oct 2024 17:19:11 -0400
Subject: fixed quizMode to actually render and to get pointer events.

---
 src/client/views/collections/CollectionCardDeckView.tsx | 14 +++++++++++++-
 src/client/views/nodes/ComparisonBox.scss               |  3 +++
 src/client/views/nodes/ComparisonBox.tsx                |  5 +++--
 3 files changed, 19 insertions(+), 3 deletions(-)

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

diff --git a/src/client/views/collections/CollectionCardDeckView.tsx b/src/client/views/collections/CollectionCardDeckView.tsx
index 5faabacf4..b86dad9d7 100644
--- a/src/client/views/collections/CollectionCardDeckView.tsx
+++ b/src/client/views/collections/CollectionCardDeckView.tsx
@@ -4,7 +4,7 @@ import { computedFn } from 'mobx-utils';
 import * as React from 'react';
 import { ClientUtils, DashColor, imageUrlToBase64, returnFalse, returnNever, returnZero } from '../../../ClientUtils';
 import { emptyFunction } from '../../../Utils';
-import { Doc } from '../../../fields/Doc';
+import { Doc, DocListCast, Opt } from '../../../fields/Doc';
 import { Animation, DocData } from '../../../fields/DocSymbols';
 import { Id } from '../../../fields/FieldSymbols';
 import { List } from '../../../fields/List';
@@ -24,6 +24,7 @@ import { DocumentView, DocumentViewProps } from '../nodes/DocumentView';
 import { GPTPopup, GPTPopupMode } from '../pdf/GPTPopup/GPTPopup';
 import './CollectionCardDeckView.scss';
 import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
+import { FocusViewOptions } from '../nodes/FocusViewOptions';
 
 enum cardSortings {
     Time = 'time',
@@ -342,6 +343,7 @@ export class CollectionCardView extends CollectionSubView() {
             fitWidth={returnFalse}
             waitForDoubleClickToClick={returnNever}
             scriptContext={this}
+            focus={this.focus}
             onDoubleClickScript={this.onChildDoubleClick}
             onClickScript={this._curDoc === doc ? undefined : this._clickScript}
             dontCenter="y" // Don't center it vertically, because the grid it's in is already doing that and we don't want to do it twice.
@@ -593,6 +595,16 @@ export class CollectionCardView extends CollectionSubView() {
         }
     });
 
+    focus = action((anchor: Doc, options: FocusViewOptions): Opt<number> => {
+        const docs = DocListCast(this.Document[this.fieldKey ?? Doc.LayoutFieldKey(this.Document)]);
+        if (anchor.type !== DocumentType.CONFIG && !docs.includes(anchor)) return undefined;
+        options.didMove = true;
+        const target = DocCast(anchor.annotationOn) ?? anchor;
+        const index = docs.indexOf(target);
+        index !== -1 && (this._curDoc = target);
+        return undefined;
+    });
+
     /**
      * Actually renders all the cards
      */
diff --git a/src/client/views/nodes/ComparisonBox.scss b/src/client/views/nodes/ComparisonBox.scss
index d1cc48051..d2ba9796b 100644
--- a/src/client/views/nodes/ComparisonBox.scss
+++ b/src/client/views/nodes/ComparisonBox.scss
@@ -236,6 +236,9 @@
         }
     }
 }
+.comparisonBox-interactive {
+    pointer-events: all;
+}
 
 .comparisonBox-explain {
     position: absolute;
diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx
index 3c126ea4a..f6c33d6ba 100644
--- a/src/client/views/nodes/ComparisonBox.tsx
+++ b/src/client/views/nodes/ComparisonBox.tsx
@@ -792,8 +792,9 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
         const renderMode = new Map<flashcardRevealOp, () => JSX.Element>([
             [flashcardRevealOp.FLIP, this.renderAsFlip],
             [flashcardRevealOp.SLIDE, this.renderAsBeforeAfter]]); // prettier-ignore
-        if (this.isQuizMode) this.renderAsQuiz(this.frontText);
-        return (
+        return this.isQuizMode ? (
+            this.renderAsQuiz(this.frontText)
+        ) : (
             <div className="comparisonBox" style={{ pointerEvents: this._props.isContentActive() && !this.Document[Animation] ? 'unset' : undefined }} onContextMenu={this.flashcardContextMenu}>
                 {renderMode.get(this.revealOp)?.() ?? null}
                 {this.loading ? (
-- 
cgit v1.2.3-70-g09d2


From 4b231bbaf21939144ed8639d35f022834a406e59 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 17 Oct 2024 22:02:19 -0400
Subject: changed layout_isFlashcard to layout_flashcardType with value of
 'flashcard' to make it easy to search for flashcards. made carousel views
 able to focus() on a Doc.  added ability for carousel and card views to have
 anchors so they can be pinned and linked.  fixed pinning collections to save
 filters

---
 src/client/apis/gpt/GPT.ts                         |  9 +++-
 src/client/documents/Documents.ts                  |  4 +-
 src/client/util/LinkFollower.ts                    |  2 -
 src/client/views/PinFuncs.ts                       | 13 ++++--
 .../views/collections/CollectionCardDeckView.tsx   | 32 +++++++++++++-
 .../views/collections/CollectionCarousel3DView.tsx | 32 +++++++++++++-
 .../views/collections/CollectionCarouselView.tsx   | 51 +++++++++++++++++++++-
 .../collections/CollectionStackedTimeline.tsx      |  5 +--
 .../views/collections/CollectionTimeView.tsx       |  2 -
 .../views/collections/FlashcardPracticeUI.tsx      |  6 +--
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  2 +-
 src/client/views/nodes/ComparisonBox.tsx           | 21 +++++----
 src/client/views/nodes/DataVizBox/DataVizBox.tsx   |  2 +-
 src/client/views/nodes/MapBox/MapBox.tsx           |  2 +-
 .../views/nodes/MapboxMapBox/MapboxContainer.tsx   |  5 +--
 src/client/views/nodes/PDFBox.tsx                  |  2 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |  2 +-
 17 files changed, 151 insertions(+), 41 deletions(-)

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

diff --git a/src/client/apis/gpt/GPT.ts b/src/client/apis/gpt/GPT.ts
index 8a2c91269..03380e4d6 100644
--- a/src/client/apis/gpt/GPT.ts
+++ b/src/client/apis/gpt/GPT.ts
@@ -42,7 +42,7 @@ const callTypeMap: { [type: string]: GPTCallOpts } = {
         model: 'gpt-4o',
         maxTokens: 2048,
         temp: 0.7,
-        prompt: 'Create a stack of flashcards out of this text with each question and answer labeled as question and answer. For some questions, ask "what is this image of" but tailored to stacks theme and the image and write a keyword that represents the image and label it "keyword". Otherwise, write none. Do not label each flashcard and do not include asterisks.',
+        prompt: 'Create a stack of flashcards out of this text with each question and answer labeled as question and answer. Create a title that represents the question in just a few words and label it "title". For some questions, ask "what is this image of" but tailored to stacks theme and the image and write a keyword that represents the image and label it "keyword". Otherwise, write none. Do not label each flashcard and do not include asterisks.',
     },
 
     completion: { model: 'gpt-4-turbo', maxTokens: 256, temp: 0.5, prompt: "You are a helpful assistant. Answer the user's prompt." },
@@ -65,7 +65,12 @@ const callTypeMap: { [type: string]: GPTCallOpts } = {
         prompt: "The user is going to give you a list of descriptions. Each one is separated by `======` on either side. Descriptions will vary in length, so make sure to only separate when you see `======`. Sort them by the user's specifications. Make sure each description is only in the list once. Each item should be separated by `======`. Immediately afterward, surrounded by `------` on BOTH SIDES, provide some insight into your reasoning for the way you sorted (and mention nothing about the formatting details given in this description). It is VERY important that you format it exactly as described, ensuring the proper number of `=` and `-` (6 of each) and NO commas",
     },
     describe: { model: 'gpt-4-vision-preview', maxTokens: 2048, temp: 0, prompt: 'Describe these images in 3-5 words' },
-    flashcard: { model: 'gpt-4-turbo', maxTokens: 512, temp: 0.5, prompt: 'Make flashcards out of this text with each question and answer labeled as question and answer. Do not label each flashcard and do not include asterisks: ' },
+    flashcard: {
+        model: 'gpt-4-turbo',
+        maxTokens: 512,
+        temp: 0.5,
+        prompt: 'Make flashcards out of this text with each question and answer labeled as question and answer. Create a title for each question and asnwer that is labeled as "title".  Do not label each flashcard and do not include asterisks: ',
+    },
     chatcard: { model: 'gpt-4-turbo', maxTokens: 512, temp: 0.5, prompt: 'Answer the following question as a short flashcard response. Do not include a label.' },
     quiz: {
         model: 'gpt-4-turbo',
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index e539e3c65..f2e85cf7a 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -281,7 +281,7 @@ export class DocumentOptions {
     _layout_fitWidth?: BOOLt = new BoolInfo('whether document should scale its contents to fit its rendered width or not (e.g., for PDFviews)');
     _layout_fieldKey?: STRt = new StrInfo('the field key containing the current layout definition', false);
     _layout_enableAltContentUI?: BOOLt = new BoolInfo('whether to show alternate content button');
-    _layout_isFlashcard?: BOOLt = new BoolInfo('whether comparison node should be displayed as a flashcard');
+    _layout_flashcardType?: STRt = new StrInfo('flashcard style to render in ComparisonBox.  currently just "flashcard".');
     _layout_showTitle?: string; // field name to display in header (:hover is an optional suffix)
     _layout_showSidebar?: BOOLt = new BoolInfo('whether an annotationsidebar should be displayed for text docuemnts');
     _layout_showCaption?: string; // which field to display in the caption area.  leave empty to have no caption
@@ -821,7 +821,7 @@ export namespace Docs {
                 data_front: front ?? CenteredTextCreator('question', 'hint: Enter a topic, select this document and click the stack button to have GPT create a deck of cards', { text_placeholder: true, cloneOnCopy: true }, undefined),
                 data_back: back ?? CenteredTextCreator('answer', 'answer here', { text_placeholder: true, cloneOnCopy: true }, undefined),
                 _layout_fitWidth: true,
-                _layout_isFlashcard: true,
+                _layout_flashcardType: 'flashcard',
                 title,
                 ...options,
             });
diff --git a/src/client/util/LinkFollower.ts b/src/client/util/LinkFollower.ts
index 0a3a0ba49..0e67dcfaa 100644
--- a/src/client/util/LinkFollower.ts
+++ b/src/client/util/LinkFollower.ts
@@ -113,12 +113,10 @@ export class LinkFollower {
                 }
                 const moveTo = [NumCast(sourceDoc.x) + NumCast(sourceDoc.followLinkXoffset), NumCast(sourceDoc.y) + NumCast(sourceDoc.followLinkYoffset)];
                 if (srcAnchor.followLinkXoffset !== undefined && moveTo[0] !== target.x) {
-                    // eslint-disable-next-line prefer-destructuring
                     target.x = moveTo[0];
                     movedTarget = true;
                 }
                 if (srcAnchor.followLinkYoffset !== undefined && moveTo[1] !== target.y) {
-                    // eslint-disable-next-line prefer-destructuring
                     target.y = moveTo[1];
                     movedTarget = true;
                 }
diff --git a/src/client/views/PinFuncs.ts b/src/client/views/PinFuncs.ts
index 430455644..5b57cd624 100644
--- a/src/client/views/PinFuncs.ts
+++ b/src/client/views/PinFuncs.ts
@@ -39,9 +39,14 @@ export interface PinProps {
     pinData?: pinDataTypes;
 }
 
-/// copies values from the targetDoc (which is the prototype of the pinDoc) to
-/// reserved fields on the pinDoc so that those values can be restored to the
-/// target doc when navigating to it.
+/**
+ * copies values from the targetDoc (which is the prototype of the pinDoc) to
+ * reserved fields on the pinDoc so that those values can be restored to the
+ * target doc when navigating to it.
+ * @param pinDoc Doc that will store pinned metadata
+ * @param pinProps description of props to pin
+ * @param targetDoc Doc that is being pinned
+ */
 export function PinDocView(pinDocIn: Doc, pinProps: PinProps, targetDoc: Doc) {
     const pinDoc = pinDocIn;
     pinDoc.presentation = true;
@@ -114,7 +119,7 @@ export function PinDocView(pinDocIn: Doc, pinProps: PinProps, targetDoc: Doc) {
                 )
             );
         if (pinProps.pinData.type_collection) pinDoc.config_viewType = targetDoc._type_collection;
-        if (pinProps.pinData.filters) pinDoc.config_docFilters = ObjectField.MakeCopy(targetDoc.childFilters as ObjectField);
+        if (pinProps.pinData.filters) pinDoc.config_docFilters = ObjectField.MakeCopy(targetDoc.childFilters as ObjectField) ?? new List<string>();
         if (pinProps.pinData.pivot) pinDoc.config_pivotField = targetDoc._pivotField;
         if (pinProps.pinData.pannable) {
             pinDoc.config_panX = NumCast(targetDoc._freeform_panX);
diff --git a/src/client/views/collections/CollectionCardDeckView.tsx b/src/client/views/collections/CollectionCardDeckView.tsx
index b86dad9d7..60305244c 100644
--- a/src/client/views/collections/CollectionCardDeckView.tsx
+++ b/src/client/views/collections/CollectionCardDeckView.tsx
@@ -9,7 +9,7 @@ import { Animation, DocData } from '../../../fields/DocSymbols';
 import { Id } from '../../../fields/FieldSymbols';
 import { List } from '../../../fields/List';
 import { ScriptField } from '../../../fields/ScriptField';
-import { BoolCast, DateCast, DocCast, NumCast, RTFCast, ScriptCast, StrCast } from '../../../fields/Types';
+import { BoolCast, DateCast, DocCast, NumCast, RTFCast, ScriptCast, StrCast, toList } from '../../../fields/Types';
 import { URLField } from '../../../fields/URLField';
 import { gptImageLabel } from '../../apis/gpt/GPT';
 import { DocumentType } from '../../documents/DocumentTypes';
@@ -25,6 +25,9 @@ import { GPTPopup, GPTPopupMode } from '../pdf/GPTPopup/GPTPopup';
 import './CollectionCardDeckView.scss';
 import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
 import { FocusViewOptions } from '../nodes/FocusViewOptions';
+import { Docs } from '../../documents/Documents';
+import { PinDocView } from '../PinFuncs';
+import { OpenWhere, OpenWhereMod } from '../nodes/OpenWhere';
 
 enum cardSortings {
     Time = 'time',
@@ -604,6 +607,33 @@ export class CollectionCardView extends CollectionSubView() {
         index !== -1 && (this._curDoc = target);
         return undefined;
     });
+    getAnchor = (addAsAnnotation: boolean) => {
+        const anchor = Docs.Create.ConfigDocument({ annotationOn: this.Document });
+        anchor.config_curDoc = this.curDoc();
+        PinDocView(anchor, { pinData: { type_collection: true, filters: true } }, this.Document);
+        if (addAsAnnotation) {
+            // when added as an annotation, links to anchors can be found as links to the document even if the anchors are not rendered
+            Doc.AddDocToList(this.dataDoc, this._props.fieldKey + '_annotations', anchor);
+        }
+        return anchor;
+    };
+    // pinned / linked anchor doc includes selected rows, graph titles, and graph colors
+    restoreView = (viewData: Doc) => {
+        if (viewData.config_curDoc !== undefined && this.curDoc() !== viewData.config_curDoc) {
+            this._curDoc = DocCast(viewData.config_curDoc);
+            return true;
+        }
+        return false;
+    };
+    addDocTab = (docsIn: Doc | Doc[], location: OpenWhere) => {
+        const doc = toList(docsIn).lastElement();
+        const where = location.split(':')[0];
+        if (where === OpenWhere.lightbox && (this.childDocList?.includes(doc) || this.childLayoutPairs.map(pair => pair.layout)?.includes(doc))) {
+            if (doc.hidden) doc.hidden = false;
+            if (!location.includes(OpenWhereMod.always)) return true;
+        }
+        return this._props.addDocTab(docsIn, location);
+    };
 
     /**
      * Actually renders all the cards
diff --git a/src/client/views/collections/CollectionCarousel3DView.tsx b/src/client/views/collections/CollectionCarousel3DView.tsx
index a71cc43ba..2be3ef48f 100644
--- a/src/client/views/collections/CollectionCarousel3DView.tsx
+++ b/src/client/views/collections/CollectionCarousel3DView.tsx
@@ -6,7 +6,7 @@ import { returnZero } from '../../../ClientUtils';
 import { Utils } from '../../../Utils';
 import { Doc, DocListCast, Opt } from '../../../fields/Doc';
 import { Id } from '../../../fields/FieldSymbols';
-import { BoolCast, DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
+import { BoolCast, DocCast, NumCast, ScriptCast, StrCast, toList } from '../../../fields/Types';
 import { DocumentType } from '../../documents/DocumentTypes';
 import { DragManager } from '../../util/DragManager';
 import { Transform } from '../../util/Transform';
@@ -16,6 +16,9 @@ import { FocusViewOptions } from '../nodes/FocusViewOptions';
 import './CollectionCarousel3DView.scss';
 import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
 import { computedFn } from 'mobx-utils';
+import { Docs } from '../../documents/Documents';
+import { PinDocView } from '../PinFuncs';
+import { OpenWhere, OpenWhereMod } from '../nodes/OpenWhere';
 
 // eslint-disable-next-line @typescript-eslint/no-require-imports
 const { CAROUSEL3D_CENTER_SCALE, CAROUSEL3D_SIDE_SCALE, CAROUSEL3D_TOP } = require('../global/globalCssVariables.module.scss');
@@ -96,6 +99,33 @@ export class CollectionCarousel3DView extends CollectionSubView() {
         index !== -1 && (this.layoutDoc._carousel_index = index);
         return undefined;
     };
+    getAnchor = (addAsAnnotation: boolean) => {
+        const anchor = Docs.Create.ConfigDocument({ annotationOn: this.Document });
+        anchor.config_carousel_index = this.layoutDoc._carousel_index;
+        PinDocView(anchor, { pinData: { type_collection: true, filters: true } }, this.Document);
+        if (addAsAnnotation) {
+            // when added as an annotation, links to anchors can be found as links to the document even if the anchors are not rendered
+            Doc.AddDocToList(this.dataDoc, this._props.fieldKey + '_annotations', anchor);
+        }
+        return anchor;
+    };
+    // pinned / linked anchor doc includes selected rows, graph titles, and graph colors
+    restoreView = (viewData: Doc) => {
+        if (viewData.config_carousel_index !== undefined && this.layoutDoc._carousel_index !== viewData.config_carousel_index) {
+            this.layoutDoc._carousel_index = viewData.config_carousel_index;
+            return true;
+        }
+        return false;
+    };
+    addDocTab = (docsIn: Doc | Doc[], location: OpenWhere) => {
+        const doc = toList(docsIn).lastElement();
+        const where = location.split(':')[0];
+        if (where === OpenWhere.lightbox && (this.childDocList?.includes(doc) || this.childLayoutPairs.map(pair => pair.layout)?.includes(doc))) {
+            if (doc.hidden) doc.hidden = false;
+            if (!location.includes(OpenWhereMod.always)) return true;
+        }
+        return this._props.addDocTab(docsIn, location);
+    };
 
     @computed get content() {
         const currentIndex = NumCast(this.layoutDoc._carousel_index);
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index 1f2bc908f..b043706fd 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -3,12 +3,17 @@ import { action, computed, makeObservable, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { StopEvent, returnOne, returnZero } from '../../../ClientUtils';
-import { Doc, Opt } from '../../../fields/Doc';
-import { BoolCast, DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
+import { Doc, DocListCast, Opt } from '../../../fields/Doc';
+import { BoolCast, DocCast, NumCast, ScriptCast, StrCast, toList } from '../../../fields/Types';
+import { DocumentType } from '../../documents/DocumentTypes';
+import { Docs } from '../../documents/Documents';
 import { DragManager } from '../../util/DragManager';
+import { PinDocView } from '../PinFuncs';
 import { StyleProp } from '../StyleProp';
 import { DocumentView } from '../nodes/DocumentView';
 import { FieldViewProps } from '../nodes/FieldView';
+import { FocusViewOptions } from '../nodes/FocusViewOptions';
+import { OpenWhere, OpenWhereMod } from '../nodes/OpenWhere';
 import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
 import './CollectionCarouselView.scss';
 import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
@@ -27,6 +32,9 @@ export class CollectionCarouselView extends CollectionSubView() {
         makeObservable(this);
     }
 
+    componentDidMount(): void {
+        this._props.setContentViewBox?.(this);
+    }
     componentWillUnmount() {
         this._dropDisposer?.();
     }
@@ -67,6 +75,44 @@ export class CollectionCarouselView extends CollectionSubView() {
 
     curDoc = () => this.carouselItems[this.carouselIndex]?.layout;
 
+    focus = (anchor: Doc, options: FocusViewOptions): Opt<number> => {
+        const docs = DocListCast(this.Document[this.fieldKey ?? Doc.LayoutFieldKey(this.Document)]);
+        if (anchor.type !== DocumentType.CONFIG && !docs.includes(anchor)) return undefined;
+        options.didMove = true;
+        const target = DocCast(anchor.annotationOn) ?? anchor;
+        const index = docs.indexOf(target);
+        index !== -1 && (this.layoutDoc._carousel_index = index);
+        return undefined;
+    };
+
+    getAnchor = (addAsAnnotation: boolean) => {
+        const anchor = Docs.Create.ConfigDocument({ annotationOn: this.Document });
+        anchor.config_carousel_index = this.carouselIndex;
+        PinDocView(anchor, { pinData: { type_collection: true, filters: true } }, this.Document);
+        if (addAsAnnotation) {
+            // when added as an annotation, links to anchors can be found as links to the document even if the anchors are not rendered
+            Doc.AddDocToList(this.dataDoc, this._props.fieldKey + '_annotations', anchor);
+        }
+        return anchor;
+    };
+    // pinned / linked anchor doc includes selected rows, graph titles, and graph colors
+    restoreView = (viewData: Doc) => {
+        if (viewData.config_carousel_index !== undefined && this.layoutDoc._carousel_index !== viewData.config_carousel_index) {
+            this.layoutDoc._carousel_index = viewData.config_carousel_index;
+            return true;
+        }
+        return false;
+    };
+    addDocTab = (docsIn: Doc | Doc[], location: OpenWhere) => {
+        const doc = toList(docsIn).lastElement();
+        const where = location.split(':')[0];
+        if (where === OpenWhere.lightbox && (this.childDocList?.includes(doc) || this.childLayoutPairs.map(pair => pair.layout)?.includes(doc))) {
+            if (doc.hidden) doc.hidden = false;
+            if (!location.includes(OpenWhereMod.always)) return true;
+        }
+        return this._props.addDocTab(docsIn, location);
+    };
+
     captionStyleProvider = (doc: Doc | undefined, captionProps: Opt<FieldViewProps>, property: string) => {
         // first look for properties on the document in the carousel, then fallback to properties on the container
         const childValue = doc?.['caption_' + property] ? this._props.styleProvider?.(doc, captionProps, property) : undefined;
@@ -113,6 +159,7 @@ export class CollectionCarouselView extends CollectionSubView() {
                 LayoutTemplateString={this._props.childLayoutString}
                 TemplateDataDocument={DocCast(Doc.Layout(doc).resolvedDataDoc)}
                 childFilters={this.childDocFilters}
+                focus={this.focus}
                 hideDecorations={BoolCast(this.layoutDoc.layout_hideDecorations)}
                 addDocument={this._props.addDocument}
                 ScreenToLocalTransform={this.contentScreenToLocalXf}
diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx
index 486c826b6..c3047e5fb 100644
--- a/src/client/views/collections/CollectionStackedTimeline.tsx
+++ b/src/client/views/collections/CollectionStackedTimeline.tsx
@@ -1,4 +1,3 @@
-/* eslint-disable react/jsx-props-no-spreading */
 /* eslint-disable no-use-before-define */
 import { action, computed, IReactionDisposer, makeObservable, observable, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
@@ -59,7 +58,6 @@ export enum TrimScope {
 
 @observer
 export class CollectionStackedTimeline extends CollectionSubView<CollectionStackedTimelineProps>() {
-    // eslint-disable-next-line no-use-before-define
     public static SelectingRegions: Set<CollectionStackedTimeline> = new Set();
     public static StopSelecting() {
         this.SelectingRegions.forEach(
@@ -171,7 +169,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
 
     makeDocUnfiltered = (doc: Doc) => this.childDocList?.some(item => item === doc);
 
-    getView = async (doc: Doc, options: FocusViewOptions): Promise<Opt<DocumentView>> =>
+    getView = (doc: Doc, options: FocusViewOptions): Promise<Opt<DocumentView>> =>
         new Promise<Opt<DocumentView>>(res => {
             if (doc.hidden) options.didMove = !(doc.hidden = false);
             const findDoc = (finish: (dv: DocumentView) => void) => DocumentView.addViewRenderedCb(doc, dv => finish(dv));
@@ -600,7 +598,6 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
                                         pointerEvents: 'none',
                                     }}>
                                     <StackedTimelineAnchor
-                                        // eslint-disable-next-line react/jsx-props-no-spreading
                                         {...this._props}
                                         mark={d.anchor}
                                         containerViewPath={this._props.containerViewPath}
diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index 8a24db330..5738e7ce3 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -147,7 +147,6 @@ export class CollectionTimeView extends CollectionSubView() {
         return (
             <div className="collectionTimeView-innards" key="timeline" style={{ pointerEvents: this._props.isContentActive() ? undefined : 'none' }} onClick={this.contentsDown}>
                 <CollectionFreeFormView
-                    // eslint-disable-next-line react/jsx-props-no-spreading
                     {...this._props}
                     engineProps={{ pivotField: this.pivotField, childFilters: this.childDocFilters, childFiltersByRanges: this.childDocRangeFilters }}
                     fitContentsToBox={returnTrue}
@@ -257,7 +256,6 @@ ScriptingGlobals.add(function pivotColumnClick(pivotDoc: Doc, bounds: ViewDefBou
             const pivotView = DocumentView.getDocumentView(pivotDoc);
             if (pivotDoc && pivotView?.ComponentView instanceof CollectionTimeView && filterVals.length === 1) {
                 if (pivotView?.ComponentView.childDocs.length && pivotView.ComponentView.childDocs[0][filterVals[0]]) {
-                    // eslint-disable-next-line prefer-destructuring
                     pivotDoc._pivotField = filterVals[0];
                 }
             }
diff --git a/src/client/views/collections/FlashcardPracticeUI.tsx b/src/client/views/collections/FlashcardPracticeUI.tsx
index 9e9318c0a..c298bff22 100644
--- a/src/client/views/collections/FlashcardPracticeUI.tsx
+++ b/src/client/views/collections/FlashcardPracticeUI.tsx
@@ -58,7 +58,7 @@ export class FlashcardPracticeUI extends ObservableReactComponent<PracticeUIProp
     get practiceField() { return this._props.fieldKey + "_practice"; } // prettier-ignore
 
     @computed get filterDoc() { return DocListCast(Doc.MyContextMenuBtns.data).find(doc => doc.title === 'Filter'); } // prettier-ignore
-    @computed get practiceMode() {  return this._props.allChildDocs().some(doc => doc._layout_isFlashcard) ? StrCast(this._props.layoutDoc.practiceMode) : ''; } // prettier-ignore
+    @computed get practiceMode() {  return this._props.allChildDocs().some(doc => doc._flashcardType) ? StrCast(this._props.layoutDoc.practiceMode) : ''; } // prettier-ignore
 
     btnHeight = () => NumCast(this.filterDoc?.height) * Math.min(1, this._props.ScreenToLocalBoxXf().Scale);
     btnWidth = () => (!this.filterDoc ? 1 : (this.btnHeight() * NumCast(this.filterDoc._width)) / NumCast(this.filterDoc._height));
@@ -127,7 +127,7 @@ export class FlashcardPracticeUI extends ObservableReactComponent<PracticeUIProp
         const setColor = (mode: practiceMode) => (StrCast(this.practiceMode) === mode ? 'white' : 'lightgray');
         const togglePracticeMode = (mode: practiceMode) => this.setPracticeMode(mode === this.practiceMode ? undefined : mode);
 
-        return !this._props.allChildDocs().some(doc => doc._layout_isFlashcard) ? null : (
+        return !this._props.allChildDocs().some(doc => doc._layout_flashcardType) ? null : (
             <div
                 className="FlashcardPracticeUI-practiceModes"
                 style={{
@@ -179,7 +179,7 @@ export class FlashcardPracticeUI extends ObservableReactComponent<PracticeUIProp
             </div>
         );
     }
-    tryFilterOut = (doc: Doc) => (this.practiceMode && BoolCast(doc?._layout_isFlashcard) && doc[this.practiceField] === practiceVal.CORRECT ? true : false); // show only cards that aren't marked as correct
+    tryFilterOut = (doc: Doc) => (this.practiceMode && BoolCast(doc?._flashcardType) && doc[this.practiceField] === practiceVal.CORRECT ? true : false); // show only cards that aren't marked as correct
     render() {
         return (
             <div className="FlashcardPracticeUI">
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index d2bc8f2c2..afd662b22 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -389,7 +389,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         return undefined;
     };
 
-    getView = async (doc: Doc, options: FocusViewOptions): Promise<Opt<DocumentView>> =>
+    getView = (doc: Doc, options: FocusViewOptions): Promise<Opt<DocumentView>> =>
         new Promise<Opt<DocumentView>>(res => {
             if (doc.hidden && this._lightboxDoc !== doc) options.didMove = !(doc.hidden = false);
             if (doc === this.Document) {
diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx
index f6c33d6ba..672c189ba 100644
--- a/src/client/views/nodes/ComparisonBox.tsx
+++ b/src/client/views/nodes/ComparisonBox.tsx
@@ -63,8 +63,9 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
      * @param useDoc doc to fill in instead of creating a Doc
      * @returns the resulting flashcard Doc
      */
-    public static createFlashcard(tuple: string, frontKey: string, backKey: string, useDoc?: Doc) {
-        const [ktoken, atoken] = [ComparisonBox.ktoken, ComparisonBox.atoken];
+    public static createFlashcard(tuple3: string, frontKey: string, backKey: string, useDoc?: Doc) {
+        const [qtoken, ktoken, atoken] = [ComparisonBox.qtoken, ComparisonBox.ktoken, ComparisonBox.atoken];
+        const [title, tuple] = tuple3.split(qtoken);
         const question = (tuple.includes(ktoken) ? tuple.split(ktoken)[0] : tuple).split(atoken)[0];
         const rest = tuple.replace(question, '');
         // prettier-ignore
@@ -82,7 +83,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
                 useDoc[DocData][backKey] = back;
                 return useDoc;
             }
-            return Docs.Create.FlashcardDocument('flashcard', front, back, { _width: 300, _height: 300 });
+            return Docs.Create.FlashcardDocument(title, front, back, { _width: 300, _height: 300 });
         };
         return keyword && keyword.toLowerCase() !== 'none' ? ComparisonBox.fetchImages(keyword).then(img => fillInFlashcard(img)) : fillInFlashcard();
     }
@@ -95,12 +96,13 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
     public static createFlashcardDeck(text: string, width: number, height: number, front: string, back: string) {
         return Promise.all(
             text
-                .split(ComparisonBox.qtoken)
+                .toLowerCase()
+                .split(ComparisonBox.ttoken)
                 .filter(t => t)
                 .map(tuple => ComparisonBox.createFlashcard(tuple, front, back))
         ).then(docs => {
             return Docs.Create.CarouselDocument(docs, {
-                title: 'flashcard deck',
+                title: text,
                 _width: width,
                 _height: height,
                 _layout_fitWidth: false,
@@ -112,9 +114,10 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
     }
     private SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
 
-    static qtoken = 'Question: ';
-    static ktoken = 'Keyword: ';
-    static atoken = 'Answer: ';
+    static qtoken = 'question: ';
+    static ktoken = 'keyword: ';
+    static atoken = 'answer: ';
+    static ttoken = 'title: ';
     private _slideTiming = 200;
     private _sideBtnWidth = 35;
     private _closeRef = React.createRef<HTMLDivElement>();
@@ -183,7 +186,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
 
     @computed get containerDoc()  { return this._props.docViewPath().slice(-2)[0]?.Document; } // prettier-ignore
     @computed get isQuizMode()    { return this.containerDoc?.practiceMode === practiceMode.QUIZ; } // prettier-ignore
-    @computed get isFlashcard()   { return BoolCast(this.Document.layout_isFlashcard); } // prettier-ignore
+    @computed get isFlashcard()   { return StrCast(this.Document.layout_flashcardType); } // prettier-ignore
     @computed get frontKey()      { return this._props.fieldKey + '_front'; } // prettier-ignore
     @computed get backKey()       { return this._props.fieldKey + '_back'; } // prettier-ignore
     @computed get frontText()     { return RTFCast(DocCast(this.dataDoc[this.frontKey]).text)?.Text; } // prettier-ignore
diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx
index 896048ab3..d79a37181 100644
--- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx
+++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx
@@ -323,7 +323,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
             () => UndoManager.RunInBatch(this.toggleSidebar, 'toggle sidebar')
         );
     };
-    getView = async (doc: Doc, options: FocusViewOptions) => {
+    getView = (doc: Doc, options: FocusViewOptions) => {
         if (this._sidebarRef?.current?.makeDocUnfiltered(doc) && !this.SidebarShown) {
             options.didMove = true;
             this.toggleSidebar();
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index c66f7c726..4a436319b 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -428,7 +428,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
         }
     };
 
-    getView = async (doc: Doc, options: FocusViewOptions) => {
+    getView = (doc: Doc, options: FocusViewOptions) => {
         if (this._sidebarRef?.current?.makeDocUnfiltered(doc) && !this.SidebarShown) {
             this.toggleSidebar();
             options.didMove = true;
diff --git a/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx b/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx
index a4557196e..d5d8f8afa 100644
--- a/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx
+++ b/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx
@@ -383,7 +383,7 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<FieldViewProps>
         }
     };
 
-    getView = async (doc: Doc, options: FocusViewOptions) => {
+    getView = (doc: Doc, options: FocusViewOptions) => {
         if (this._sidebarRef?.current?.makeDocUnfiltered(doc) && !this.SidebarShown) {
             this.toggleSidebar();
             options.didMove = true;
@@ -732,7 +732,6 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<FieldViewProps>
                         MapBoxContainer._rerenderDelay = 0;
                     }
                     this._rerenderTimeout = undefined;
-                    // eslint-disable-next-line operator-assignment
                     this.Document[DocCss] = this.Document[DocCss] + 1;
                 }), MapBoxContainer._rerenderDelay);
             return null;
@@ -792,7 +791,6 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<FieldViewProps>
                                   .map(pushpin => (
                                       <DocumentView
                                           key={pushpin[Id]}
-                                          // eslint-disable-next-line react/jsx-props-no-spreading
                                           {...this._props}
                                           renderDepth={this._props.renderDepth + 1}
                                           Document={pushpin}
@@ -830,7 +828,6 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<FieldViewProps>
                 <div className="mapBox-sidebar" style={{ width: `${this.sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
                     <SidebarAnnos
                         ref={this._sidebarRef}
-                        // eslint-disable-next-line react/jsx-props-no-spreading
                         {...this._props}
                         fieldKey={this.fieldKey}
                         Document={this.Document}
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 816d4a3b0..2e4c87d38 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -233,7 +233,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
         return this._pdfViewer?.scrollFocus(anchor, NumCast(anchor.y, NumCast(anchor.config_scrollTop)), options);
     };
 
-    getView = async (doc: Doc, options: FocusViewOptions) => {
+    getView = (doc: Doc, options: FocusViewOptions) => {
         if (this._sidebarRef?.current?.makeDocUnfiltered(doc) && !this.SidebarShown) {
             options.didMove = true;
             this.toggleSidebar(false);
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 29be8d285..2aee7b6b3 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1156,7 +1156,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
         return anchorDoc ?? this.Document;
     }
 
-    getView = async (doc: Doc, options: FocusViewOptions) => {
+    getView = (doc: Doc, options: FocusViewOptions) => {
         if (DocListCast(this.dataDoc[this.sidebarKey]).find(anno => Doc.AreProtosEqual(doc.layout_unrendered ? DocCast(doc.annotationOn) : doc, anno))) {
             if (!this.SidebarShown) {
                 this.toggleSidebar(false);
-- 
cgit v1.2.3-70-g09d2


From 0728e918e6d075c0eda738b7d2fdbf6095c714ae Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 18 Oct 2024 00:28:55 -0400
Subject: fixed following links/show document of items in tabs/carousel/card
 view to set options.didMove correctly so that toggleTarget works.

---
 src/client/documents/Documents.ts                  |  2 ++
 .../views/collections/CollectionCardDeckView.tsx   | 42 +++++++++-------------
 .../views/collections/CollectionCarousel3DView.tsx | 40 +++++++--------------
 .../views/collections/CollectionCarouselView.tsx   | 33 ++++++-----------
 src/client/views/collections/CollectionSubView.tsx | 14 +++++++-
 src/client/views/collections/TabDocView.tsx        |  1 +
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 19 ++++------
 src/client/views/nodes/trails/PresBox.tsx          |  3 +-
 src/extensions/ExtensionsTypings.ts                |  8 +++++
 src/extensions/Extensions_Array.ts                 | 10 ++++--
 10 files changed, 78 insertions(+), 94 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index f2e85cf7a..be3b9c3bb 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -372,6 +372,8 @@ export class DocumentOptions {
     config_panX?: NUMt = new NumInfo('panX saved as a view spec', false);
     config_panY?: NUMt = new NumInfo('panY saved as a view spec', false);
     config_zoom?: NUMt = new NumInfo('zoom saved as a view spec', false);
+    config_carousel_index?: NUMt = new NumInfo('saved carousel index', false);
+    config_curDoc?: DOCt = new DocInfo('current doc in a collection view, e.g., cardView');
     config_viewScale?: NUMt = new NumInfo('viewScale saved as a view Spec', false);
     presentation_transition?: NUMt = new NumInfo('the time taken for the transition TO a document', false);
     presentation_duration?: NUMt = new NumInfo('the duration of the slide in presentation view', false);
diff --git a/src/client/views/collections/CollectionCardDeckView.tsx b/src/client/views/collections/CollectionCardDeckView.tsx
index 60305244c..729a1585a 100644
--- a/src/client/views/collections/CollectionCardDeckView.tsx
+++ b/src/client/views/collections/CollectionCardDeckView.tsx
@@ -9,25 +9,24 @@ import { Animation, DocData } from '../../../fields/DocSymbols';
 import { Id } from '../../../fields/FieldSymbols';
 import { List } from '../../../fields/List';
 import { ScriptField } from '../../../fields/ScriptField';
-import { BoolCast, DateCast, DocCast, NumCast, RTFCast, ScriptCast, StrCast, toList } from '../../../fields/Types';
+import { BoolCast, DateCast, DocCast, NumCast, RTFCast, ScriptCast, StrCast } from '../../../fields/Types';
 import { URLField } from '../../../fields/URLField';
 import { gptImageLabel } from '../../apis/gpt/GPT';
 import { DocumentType } from '../../documents/DocumentTypes';
+import { Docs } from '../../documents/Documents';
 import { DragManager } from '../../util/DragManager';
 import { dropActionType } from '../../util/DropActionTypes';
 import { SnappingManager } from '../../util/SnappingManager';
 import { Transform } from '../../util/Transform';
 import { undoable } from '../../util/UndoManager';
+import { PinDocView } from '../PinFuncs';
 import { StyleProp } from '../StyleProp';
 import { TagItem } from '../TagsView';
 import { DocumentView, DocumentViewProps } from '../nodes/DocumentView';
+import { FocusViewOptions } from '../nodes/FocusViewOptions';
 import { GPTPopup, GPTPopupMode } from '../pdf/GPTPopup/GPTPopup';
 import './CollectionCardDeckView.scss';
 import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
-import { FocusViewOptions } from '../nodes/FocusViewOptions';
-import { Docs } from '../../documents/Documents';
-import { PinDocView } from '../PinFuncs';
-import { OpenWhere, OpenWhereMod } from '../nodes/OpenWhere';
 
 enum cardSortings {
     Time = 'time',
@@ -599,22 +598,21 @@ export class CollectionCardView extends CollectionSubView() {
     });
 
     focus = action((anchor: Doc, options: FocusViewOptions): Opt<number> => {
-        const docs = DocListCast(this.Document[this.fieldKey ?? Doc.LayoutFieldKey(this.Document)]);
-        if (anchor.type !== DocumentType.CONFIG && !docs.includes(anchor)) return undefined;
-        options.didMove = true;
-        const target = DocCast(anchor.annotationOn) ?? anchor;
-        const index = docs.indexOf(target);
-        index !== -1 && (this._curDoc = target);
+        const docs = DocListCast(this.Document[this.fieldKey]);
+        if (anchor.type === DocumentType.CONFIG || docs.includes(anchor)) {
+            const foundDoc = DocCast(
+                anchor.config_curDoc,
+                docs.find(doc => doc === DocCast(anchor.annotationOn, anchor))
+            );
+            options.didMove = foundDoc !== this.curDoc() ? true : false;
+            options.didMove && (this._curDoc = foundDoc);
+        }
         return undefined;
     });
     getAnchor = (addAsAnnotation: boolean) => {
-        const anchor = Docs.Create.ConfigDocument({ annotationOn: this.Document });
-        anchor.config_curDoc = this.curDoc();
+        const anchor = Docs.Create.ConfigDocument({ annotationOn: this.Document, config_curDoc: this.curDoc() });
         PinDocView(anchor, { pinData: { type_collection: true, filters: true } }, this.Document);
-        if (addAsAnnotation) {
-            // when added as an annotation, links to anchors can be found as links to the document even if the anchors are not rendered
-            Doc.AddDocToList(this.dataDoc, this._props.fieldKey + '_annotations', anchor);
-        }
+        addAsAnnotation && Doc.AddDocToList(this.dataDoc, this.fieldKey + '_annotations', anchor); // when added as an annotation, links to anchors can be found as links to the document even if the anchors are not rendered
         return anchor;
     };
     // pinned / linked anchor doc includes selected rows, graph titles, and graph colors
@@ -625,15 +623,7 @@ export class CollectionCardView extends CollectionSubView() {
         }
         return false;
     };
-    addDocTab = (docsIn: Doc | Doc[], location: OpenWhere) => {
-        const doc = toList(docsIn).lastElement();
-        const where = location.split(':')[0];
-        if (where === OpenWhere.lightbox && (this.childDocList?.includes(doc) || this.childLayoutPairs.map(pair => pair.layout)?.includes(doc))) {
-            if (doc.hidden) doc.hidden = false;
-            if (!location.includes(OpenWhereMod.always)) return true;
-        }
-        return this._props.addDocTab(docsIn, location);
-    };
+    addDocTab = this.addLinkedDocTab;
 
     /**
      * Actually renders all the cards
diff --git a/src/client/views/collections/CollectionCarousel3DView.tsx b/src/client/views/collections/CollectionCarousel3DView.tsx
index 81e921724..081e2fe5a 100644
--- a/src/client/views/collections/CollectionCarousel3DView.tsx
+++ b/src/client/views/collections/CollectionCarousel3DView.tsx
@@ -1,24 +1,23 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { computed, makeObservable } from 'mobx';
 import { observer } from 'mobx-react';
+import { computedFn } from 'mobx-utils';
 import * as React from 'react';
 import { returnZero } from '../../../ClientUtils';
 import { Utils } from '../../../Utils';
 import { Doc, DocListCast, Opt } from '../../../fields/Doc';
 import { Id } from '../../../fields/FieldSymbols';
-import { BoolCast, DocCast, NumCast, ScriptCast, StrCast, toList } from '../../../fields/Types';
+import { BoolCast, DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
 import { DocumentType } from '../../documents/DocumentTypes';
+import { Docs } from '../../documents/Documents';
 import { DragManager } from '../../util/DragManager';
 import { Transform } from '../../util/Transform';
+import { PinDocView } from '../PinFuncs';
 import { StyleProp } from '../StyleProp';
 import { DocumentView } from '../nodes/DocumentView';
 import { FocusViewOptions } from '../nodes/FocusViewOptions';
 import './CollectionCarousel3DView.scss';
 import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
-import { computedFn } from 'mobx-utils';
-import { Docs } from '../../documents/Documents';
-import { PinDocView } from '../PinFuncs';
-import { OpenWhere, OpenWhereMod } from '../nodes/OpenWhere';
 
 // eslint-disable-next-line @typescript-eslint/no-require-imports
 const { CAROUSEL3D_CENTER_SCALE, CAROUSEL3D_SIDE_SCALE, CAROUSEL3D_TOP } = require('../global/globalCssVariables.module.scss');
@@ -94,22 +93,18 @@ export class CollectionCarousel3DView extends CollectionSubView() {
             .scale(1 / this.centerScale);
 
     focus = (anchor: Doc, options: FocusViewOptions): Opt<number> => {
-        const docs = DocListCast(this.Document[this.fieldKey ?? Doc.LayoutFieldKey(this.Document)]);
-        if (anchor.type !== DocumentType.CONFIG && !docs.includes(anchor)) return undefined;
-        options.didMove = true;
-        const target = DocCast(anchor.annotationOn) ?? anchor;
-        const index = docs.indexOf(target);
-        index !== -1 && (this.layoutDoc._carousel_index = index);
+        const docs = DocListCast(this.Document[this.fieldKey]);
+        if (anchor.type === DocumentType.CONFIG || docs.includes(anchor)) {
+            const newIndex = anchor.config_carousel_index ?? docs.getIndex(DocCast(anchor.annotationOn, anchor));
+            options.didMove = newIndex !== this.layoutDoc._carousel_index;
+            options.didMove && (this.layoutDoc._carousel_index = newIndex);
+        }
         return undefined;
     };
     getAnchor = (addAsAnnotation: boolean) => {
-        const anchor = Docs.Create.ConfigDocument({ annotationOn: this.Document });
-        anchor.config_carousel_index = this.layoutDoc._carousel_index;
+        const anchor = Docs.Create.ConfigDocument({ annotationOn: this.Document, config_carousel_index: this.layoutDoc._carousel_index as number });
         PinDocView(anchor, { pinData: { type_collection: true, filters: true } }, this.Document);
-        if (addAsAnnotation) {
-            // when added as an annotation, links to anchors can be found as links to the document even if the anchors are not rendered
-            Doc.AddDocToList(this.dataDoc, this._props.fieldKey + '_annotations', anchor);
-        }
+        addAsAnnotation && Doc.AddDocToList(this.dataDoc, this.fieldKey + '_annotations', anchor); // when added as an annotation, links to anchors can be found as links to the document even if the anchors are not rendered
         return anchor;
     };
     // pinned / linked anchor doc includes selected rows, graph titles, and graph colors
@@ -120,16 +115,7 @@ export class CollectionCarousel3DView extends CollectionSubView() {
         }
         return false;
     };
-    addDocTab = (docsIn: Doc | Doc[], location: OpenWhere) => {
-        const doc = toList(docsIn).lastElement();
-        const where = location.split(':')[0];
-        if (where === OpenWhere.lightbox && (this.childDocList?.includes(doc) || this.childLayoutPairs.map(pair => pair.layout)?.includes(doc))) {
-            if (doc.hidden) doc.hidden = false;
-            if (!location.includes(OpenWhereMod.always)) return true;
-        }
-        return this._props.addDocTab(docsIn, location);
-    };
-
+    addDocTab = this.addLinkedDocTab;
     @computed get content() {
         const currentIndex = NumCast(this.layoutDoc._carousel_index);
         const displayDoc = (child: Doc, dxf: () => Transform) => (
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index b043706fd..bfb58733d 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -4,7 +4,7 @@ import { observer } from 'mobx-react';
 import * as React from 'react';
 import { StopEvent, returnOne, returnZero } from '../../../ClientUtils';
 import { Doc, DocListCast, Opt } from '../../../fields/Doc';
-import { BoolCast, DocCast, NumCast, ScriptCast, StrCast, toList } from '../../../fields/Types';
+import { BoolCast, DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
 import { DocumentType } from '../../documents/DocumentTypes';
 import { Docs } from '../../documents/Documents';
 import { DragManager } from '../../util/DragManager';
@@ -13,7 +13,6 @@ import { StyleProp } from '../StyleProp';
 import { DocumentView } from '../nodes/DocumentView';
 import { FieldViewProps } from '../nodes/FieldView';
 import { FocusViewOptions } from '../nodes/FocusViewOptions';
-import { OpenWhere, OpenWhereMod } from '../nodes/OpenWhere';
 import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
 import './CollectionCarouselView.scss';
 import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
@@ -76,23 +75,19 @@ export class CollectionCarouselView extends CollectionSubView() {
     curDoc = () => this.carouselItems[this.carouselIndex]?.layout;
 
     focus = (anchor: Doc, options: FocusViewOptions): Opt<number> => {
-        const docs = DocListCast(this.Document[this.fieldKey ?? Doc.LayoutFieldKey(this.Document)]);
-        if (anchor.type !== DocumentType.CONFIG && !docs.includes(anchor)) return undefined;
-        options.didMove = true;
-        const target = DocCast(anchor.annotationOn) ?? anchor;
-        const index = docs.indexOf(target);
-        index !== -1 && (this.layoutDoc._carousel_index = index);
+        const docs = DocListCast(this.Document[this.fieldKey]);
+        if (anchor.type === DocumentType.CONFIG || docs.includes(anchor)) {
+            const newIndex = anchor.config_carousel_index ?? docs.getIndex(DocCast(anchor.annotationOn, anchor));
+            options.didMove = newIndex !== this.layoutDoc._carousel_index;
+            options.didMove && (this.layoutDoc._carousel_index = newIndex);
+        }
         return undefined;
     };
 
     getAnchor = (addAsAnnotation: boolean) => {
-        const anchor = Docs.Create.ConfigDocument({ annotationOn: this.Document });
-        anchor.config_carousel_index = this.carouselIndex;
+        const anchor = Docs.Create.ConfigDocument({ annotationOn: this.Document, config_carousel_index: this.carouselIndex });
         PinDocView(anchor, { pinData: { type_collection: true, filters: true } }, this.Document);
-        if (addAsAnnotation) {
-            // when added as an annotation, links to anchors can be found as links to the document even if the anchors are not rendered
-            Doc.AddDocToList(this.dataDoc, this._props.fieldKey + '_annotations', anchor);
-        }
+        addAsAnnotation && Doc.AddDocToList(this.dataDoc, this.fieldKey + '_annotations', anchor); // when added as an annotation, links to anchors can be found as links to the document even if the anchors are not rendered
         return anchor;
     };
     // pinned / linked anchor doc includes selected rows, graph titles, and graph colors
@@ -103,15 +98,7 @@ export class CollectionCarouselView extends CollectionSubView() {
         }
         return false;
     };
-    addDocTab = (docsIn: Doc | Doc[], location: OpenWhere) => {
-        const doc = toList(docsIn).lastElement();
-        const where = location.split(':')[0];
-        if (where === OpenWhere.lightbox && (this.childDocList?.includes(doc) || this.childLayoutPairs.map(pair => pair.layout)?.includes(doc))) {
-            if (doc.hidden) doc.hidden = false;
-            if (!location.includes(OpenWhereMod.always)) return true;
-        }
-        return this._props.addDocTab(docsIn, location);
-    };
+    addDocTab = this.addLinkedDocTab;
 
     captionStyleProvider = (doc: Doc | undefined, captionProps: Opt<FieldViewProps>, property: string) => {
         // first look for properties on the document in the carousel, then fallback to properties on the container
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 48aac3a68..9952863d7 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -9,7 +9,7 @@ import { Id } from '../../../fields/FieldSymbols';
 import { List } from '../../../fields/List';
 import { listSpec } from '../../../fields/Schema';
 import { ScriptField } from '../../../fields/ScriptField';
-import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
+import { BoolCast, Cast, NumCast, ScriptCast, StrCast, toList } from '../../../fields/Types';
 import { WebField } from '../../../fields/URLField';
 import { GetEffectiveAcl, TraceMobx } from '../../../fields/util';
 import { GestureUtils } from '../../../pen-gestures/GestureUtils';
@@ -27,6 +27,7 @@ import { ViewBoxBaseComponent } from '../DocComponent';
 import { FieldViewProps } from '../nodes/FieldView';
 import { DocumentView, DocumentViewProps } from '../nodes/DocumentView';
 import { FlashcardPracticeUI } from './FlashcardPracticeUI';
+import { OpenWhere, OpenWhereMod } from '../nodes/OpenWhere';
 
 export interface CollectionViewProps extends React.PropsWithChildren<FieldViewProps> {
     isAnnotationOverlay?: boolean; // is the collection an annotation overlay (eg an overlay on an image/video/etc)
@@ -130,6 +131,17 @@ export function CollectionSubView<X>() {
         @computed get childDocList() {
             return Cast(this.dataField, listSpec(Doc));
         }
+
+        addLinkedDocTab = (docsIn: Doc | Doc[], location: OpenWhere) => {
+            const doc = toList(docsIn).lastElement();
+            const where = location.split(':')[0];
+            if (where === OpenWhere.lightbox && (this.childDocList?.includes(doc) || this.childLayoutPairs.map(pair => pair.layout)?.includes(doc))) {
+                if (doc.hidden) doc.hidden = false;
+                if (!location.includes(OpenWhereMod.always)) return true;
+            }
+            return this._props.addDocTab(docsIn, location);
+        };
+
         collectionFilters = () => this._focusFilters ?? StrListCast(this.Document._childFilters);
         collectionRangeDocFilters = () => this._focusRangeFilters ?? Cast(this.Document._childFiltersByRanges, listSpec('string'), []);
         // child filters apply to the descendants of the documents in this collection
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 5bfdee1f5..1c5722217 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -285,6 +285,7 @@ export class TabDocView extends ObservableReactComponent<TabDocViewProps> {
 
     static Activate = (tabDoc: Doc) => {
         const tab = Array.from(CollectionDockingView.Instance?.tabMap ?? []).find(findTab => findTab.DashDoc === tabDoc && !findTab.contentItem.config.props.keyValue);
+        if (tab.header.parent._activeContentItem === tab.contentItem) return false;
         tab?.header.parent.setActiveContentItem(tab.contentItem); // glr: Panning does not work when this is set - (this line is for trying to make a tab that is not topmost become topmost)
         return tab !== undefined;
     };
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index afd662b22..0241fc2dd 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -40,7 +40,7 @@ import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveEraserWidth, Active
 import { FieldViewProps } from '../../nodes/FieldView';
 import { FocusViewOptions } from '../../nodes/FocusViewOptions';
 import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox';
-import { OpenWhere, OpenWhereMod } from '../../nodes/OpenWhere';
+import { OpenWhere } from '../../nodes/OpenWhere';
 import { PinDocView, PinProps } from '../../PinFuncs';
 import { AnnotationPalette } from '../../smartdraw/AnnotationPalette';
 import { DrawingOptions, SmartDrawHandler } from '../../smartdraw/SmartDrawHandler';
@@ -1596,21 +1596,14 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             }
             case undefined:
             case OpenWhere.lightbox:
-                {
-                    const firstDoc = docs[0];
-                    if (this.layoutDoc._isLightbox) {
-                        this._lightboxDoc = firstDoc;
-                        return true;
-                    }
-                    if (firstDoc === this.Document || this.childDocList?.includes(firstDoc) || this.childLayoutPairs.map(pair => pair.layout)?.includes(firstDoc)) {
-                        if (firstDoc.hidden) firstDoc.hidden = false;
-                        if (!location.includes(OpenWhereMod.always)) return true;
-                    }
+                if (this.layoutDoc._isLightbox) {
+                    this._lightboxDoc = docs[0];
+                    return true;
                 }
-                break;
+                return this.addLinkedDocTab(docsIn, location);
             default:
         }
-        return this._props.addDocTab(docs, location);
+        return this._props.addDocTab(docsIn, location);
     });
     getCalculatedPositions(pair: { layout: Doc; data?: Doc }): PoolData {
         const random = (min: number, max: number, x: number, y: number) => /* min should not be equal to max */ min + (((Math.abs(x * y) * 9301 + 49297) % 233280) / 233280) * (max - min);
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 7448fa898..53d72e946 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -48,6 +48,7 @@ import './PresBox.scss';
 import { PresEffect, PresEffectDirection, PresMovement, PresStatus } from './PresEnums';
 import SlideEffect from './SlideEffect';
 import { AnimationSettings, SpringSettings, SpringType, easeItems, effectItems, effectTimings, movementItems, presEffectDefaultTimings, springMappings, springPreviewColors } from './SpringUtils';
+import _ from 'lodash';
 
 @observer
 export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
@@ -708,7 +709,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         }
 
         if ((pinDataTypes?.filters && activeItem.config_docFilters !== undefined) || (!pinDataTypes && activeItem.config_docFilters !== undefined)) {
-            if (bestTarget.childFilters !== activeItem.config_docFilters) {
+            if (!_.isEqual(Array.from(StrListCast(bestTarget.childFilters)), Array.from(StrListCast(activeItem.config_docFilters)))) {
                 bestTarget.childFilters = ObjectField.MakeCopy(activeItem.config_docFilters as ObjectField) || new List<string>([]);
                 changed = true;
             }
diff --git a/src/extensions/ExtensionsTypings.ts b/src/extensions/ExtensionsTypings.ts
index d6ffd3be3..fa8851bb3 100644
--- a/src/extensions/ExtensionsTypings.ts
+++ b/src/extensions/ExtensionsTypings.ts
@@ -1,6 +1,14 @@
 /* eslint-disable @typescript-eslint/no-unused-vars */
 interface Array<T> {
+    /**
+     * returns the last element of the array or undefined
+     */
     lastElement(): T;
+    /**
+     * if val is in the list, it returns its index, otherwise undefined;
+     * @param val
+     */
+    getIndex(val: T): number | undefined;
 }
 
 interface String {
diff --git a/src/extensions/Extensions_Array.ts b/src/extensions/Extensions_Array.ts
index a50fb330f..d61585e28 100644
--- a/src/extensions/Extensions_Array.ts
+++ b/src/extensions/Extensions_Array.ts
@@ -1,14 +1,13 @@
 export default class ArrayExtension {
     private readonly property: string;
-    private readonly body: <T>(this: Array<T>) => any;
+    private readonly body: <T>(this: Array<T>, args: unknown) => unknown;
 
-    constructor(property: string, body: <T>(this: Array<T>) => any) {
+    constructor(property: string, body: <T>(this: Array<T>, args: unknown) => unknown) {
         this.property = property;
         this.body = body;
     }
 
     assign() {
-        // eslint-disable-next-line no-extend-native
         Object.defineProperty(Array.prototype, this.property, {
             value: this.body,
             enumerable: false,
@@ -28,6 +27,11 @@ const extensions = [
         }
         return this[this.length - 1];
     }),
+    // eslint-disable-next-line @typescript-eslint/no-explicit-any
+    new ArrayExtension('getIndex', function (val: any) {
+        const index = this.indexOf(val);
+        return index === -1 ? undefined : index;
+    }),
 ];
 
 function Assign() {
-- 
cgit v1.2.3-70-g09d2


From 6d5b80adf8c0d1f93dce30401e1f90d11b838de3 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 18 Oct 2024 10:00:01 -0400
Subject: fixed following anchors to collection card/carousel to set the
 current/selected Doc

---
 src/client/documents/Documents.ts                  |  2 +-
 src/client/views/PinFuncs.ts                       |  6 +--
 src/client/views/ViewBoxInterface.ts               |  2 +-
 .../views/collections/CollectionCardDeckView.tsx   | 55 +++++++++-------------
 .../views/collections/CollectionCarousel3DView.tsx | 10 +---
 .../views/collections/CollectionCarouselView.tsx   | 10 +---
 .../views/collections/CollectionTimeView.tsx       |  2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  2 +-
 src/client/views/nodes/trails/PresBox.tsx          | 36 +++++++-------
 src/fields/ScriptField.ts                          |  6 +--
 10 files changed, 53 insertions(+), 78 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index be3b9c3bb..98e8bc932 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -373,7 +373,7 @@ export class DocumentOptions {
     config_panY?: NUMt = new NumInfo('panY saved as a view spec', false);
     config_zoom?: NUMt = new NumInfo('zoom saved as a view spec', false);
     config_carousel_index?: NUMt = new NumInfo('saved carousel index', false);
-    config_curDoc?: DOCt = new DocInfo('current doc in a collection view, e.g., cardView');
+    config_card_curDoc?: DOCt = new DocInfo('current doc in a collection view, e.g., cardView');
     config_viewScale?: NUMt = new NumInfo('viewScale saved as a view Spec', false);
     presentation_transition?: NUMt = new NumInfo('the time taken for the transition TO a document', false);
     presentation_duration?: NUMt = new NumInfo('the duration of the slide in presentation view', false);
diff --git a/src/client/views/PinFuncs.ts b/src/client/views/PinFuncs.ts
index 17964c656..bed473eec 100644
--- a/src/client/views/PinFuncs.ts
+++ b/src/client/views/PinFuncs.ts
@@ -16,7 +16,7 @@ export interface pinDataTypes {
     scrollable?: boolean;
     dataviz?: number[];
     pannable?: boolean;
-    type_collection?: boolean;
+    collectionType?: boolean;
     inkable?: boolean;
     filters?: boolean;
     pivot?: boolean;
@@ -65,7 +65,7 @@ export function PinDocView(pinDocIn: Doc, pinProps: PinProps, targetDoc: Doc) {
             pinProps.pinData.scrollable ||
             pinProps.pinData.temporal ||
             pinProps.pinData.pannable ||
-            pinProps.pinData.type_collection ||
+            pinProps.pinData.collectionType ||
             pinProps.pinData.clippable ||
             pinProps.pinData.datarange ||
             pinProps.pinData.dataview ||
@@ -118,7 +118,7 @@ export function PinDocView(pinDocIn: Doc, pinProps: PinProps, targetDoc: Doc) {
                     })
                 )
             );
-        if (pinProps.pinData.type_collection) pinDoc.config_type_collection = targetDoc._type_collection;
+        if (pinProps.pinData.collectionType) pinDoc.config_type_collection = targetDoc._type_collection;
         if (pinProps.pinData.filters) pinDoc.config_docFilters = ObjectField.MakeCopy(targetDoc.childFilters as ObjectField) ?? new List<string>();
         if (pinProps.pinData.pivot) pinDoc.config_pivotField = targetDoc._pivotField;
         if (pinProps.pinData.pannable) {
diff --git a/src/client/views/ViewBoxInterface.ts b/src/client/views/ViewBoxInterface.ts
index f66f6062e..b7980d74e 100644
--- a/src/client/views/ViewBoxInterface.ts
+++ b/src/client/views/ViewBoxInterface.ts
@@ -24,7 +24,7 @@ export abstract class ViewBoxInterface<P> extends ObservableReactComponent<React
     promoteCollection?: () => void; // moves contents of collection to parent
     updateIcon?: (usePanelDimensions?: boolean) => Promise<void>; // updates the icon representation of the document
     getAnchor?: (addAsAnnotation: boolean, pinData?: PinProps) => Doc; // returns an Anchor Doc that represents the current state of the doc's componentview (e.g., the current playhead location of a an audio/video box)
-    restoreView?: (viewSpec: Doc) => boolean;
+    restoreView?: (viewSpec: Doc) => boolean; // DEPRECATED: do not use, it will go away.  see PresBox.restoreTargetDocView
     scrollPreview?: (docView: DocumentView, doc: Doc, focusSpeed: number, options: FocusViewOptions) => Opt<number>; // returns the duration of the focus
     brushView?: (view: { width: number; height: number; panX: number; panY: number }, transTime: number, holdTime: number) => void; // highlight a region of a view (used by freeforms)
     getView?: (doc: Doc, options: FocusViewOptions) => Promise<Opt<DocumentView>>; // returns a nested DocumentView for the specified doc or undefined
diff --git a/src/client/views/collections/CollectionCardDeckView.tsx b/src/client/views/collections/CollectionCardDeckView.tsx
index 729a1585a..beb45f326 100644
--- a/src/client/views/collections/CollectionCardDeckView.tsx
+++ b/src/client/views/collections/CollectionCardDeckView.tsx
@@ -51,14 +51,13 @@ export class CollectionCardView extends CollectionSubView() {
     private _textToDoc = new Map<string, Doc>();
     private _oldWheel: HTMLElement | null = null;
     private _dropped = false; // set when a card doc has just moved and the drop method has been called - prevents the pointerUp method from hiding doc decorations (which needs to be done when clicking on a card to animate it to front/center)
-    private _clickScript = () => ScriptField.MakeScript('scriptContext._curDoc=this', { scriptContext: 'any' })!;
+    private _setCurDocScript = () => ScriptField.MakeScript('scriptContext.layoutDoc._card_curDoc=this', { scriptContext: 'any' })!;
 
     @observable _forceChildXf = 0;
     @observable _hoveredNodeIndex = -1;
     @observable _docRefs = new ObservableMap<Doc, DocumentView>();
     @observable _maxRowCount = 10;
     @observable _docDraggedIndex: number = -1;
-    @observable _curDoc: Doc | undefined = undefined;
 
     constructor(props: SubCollectionViewProps) {
         super(props);
@@ -165,10 +164,10 @@ export class CollectionCardView extends CollectionSubView() {
     /**
      * When in quiz mode, randomly selects a document
      */
-    quizMode = action(() => {
+    quizMode = () => {
         const randomIndex = Math.floor(Math.random() * this.childDocs.length);
-        this._curDoc = this.childDocs[randomIndex];
-    });
+        this.layoutDoc._card_curDoc = this.childDocs[randomIndex];
+    };
 
     setHoveredNodeIndex = action((index: number) => {
         if (!SnappingManager.IsDragging) this._hoveredNodeIndex = index;
@@ -319,7 +318,7 @@ export class CollectionCardView extends CollectionSubView() {
         (doc: Doc) => () =>
             this._props.isContentActive?.() === false
                 ? false
-                : this._props.isDocumentActive?.() && this._curDoc === doc
+                : this._props.isDocumentActive?.() && this.curDoc() === doc
                   ? true
                   : this._props.childDocumentsActive?.() === false || this.Document.childDocumentsActive === false
                     ? false
@@ -347,7 +346,7 @@ export class CollectionCardView extends CollectionSubView() {
             scriptContext={this}
             focus={this.focus}
             onDoubleClickScript={this.onChildDoubleClick}
-            onClickScript={this._curDoc === doc ? undefined : this._clickScript}
+            onClickScript={this.curDoc() === doc ? undefined : this._setCurDocScript}
             dontCenter="y" // Don't center it vertically, because the grid it's in is already doing that and we don't want to do it twice.
             dragAction={(this.Document.childDragAction ?? this._props.childDragAction) as dropActionType}
             showTags={BoolCast(this.layoutDoc.showChildTags)}
@@ -572,8 +571,8 @@ export class CollectionCardView extends CollectionSubView() {
      * @param doc doc that will be animated away from center focus
      */
     releaseCurDoc = action(() => {
-        const selDoc = this._curDoc;
-        this._curDoc = undefined;
+        const selDoc = this.curDoc();
+        this.layoutDoc._card_curDoc = undefined;
         const cardDocView = DocumentView.getDocumentView(selDoc, this.DocumentView?.());
         if (cardDocView && selDoc) {
             DocumentView.DeselectView(cardDocView);
@@ -590,7 +589,7 @@ export class CollectionCardView extends CollectionSubView() {
      * turns off the _dropped flag at the end of a drag/drop, or releases the focused Doc if a different Doc is clicked
      */
     cardPointerUp = action((doc: Doc) => {
-        if (this._curDoc === doc || this._dropped) {
+        if (this.curDoc() === doc || this._dropped) {
             this._dropped = false;
         } else {
             this.releaseCurDoc(); // NOTE: the onClick script for the card will select the new card (ie, 'doc')
@@ -601,28 +600,20 @@ export class CollectionCardView extends CollectionSubView() {
         const docs = DocListCast(this.Document[this.fieldKey]);
         if (anchor.type === DocumentType.CONFIG || docs.includes(anchor)) {
             const foundDoc = DocCast(
-                anchor.config_curDoc,
+                anchor.config_card_curDoc,
                 docs.find(doc => doc === DocCast(anchor.annotationOn, anchor))
             );
             options.didMove = foundDoc !== this.curDoc() ? true : false;
-            options.didMove && (this._curDoc = foundDoc);
+            options.didMove && (this.layoutDoc._card_curDoc = foundDoc);
         }
         return undefined;
     });
     getAnchor = (addAsAnnotation: boolean) => {
-        const anchor = Docs.Create.ConfigDocument({ annotationOn: this.Document, config_curDoc: this.curDoc() });
-        PinDocView(anchor, { pinData: { type_collection: true, filters: true } }, this.Document);
+        const anchor = Docs.Create.ConfigDocument({ annotationOn: this.Document, config_card_curDoc: this.curDoc() });
+        PinDocView(anchor, { pinData: { collectionType: true, filters: true } }, this.Document);
         addAsAnnotation && Doc.AddDocToList(this.dataDoc, this.fieldKey + '_annotations', anchor); // when added as an annotation, links to anchors can be found as links to the document even if the anchors are not rendered
         return anchor;
     };
-    // pinned / linked anchor doc includes selected rows, graph titles, and graph colors
-    restoreView = (viewData: Doc) => {
-        if (viewData.config_curDoc !== undefined && this.curDoc() !== viewData.config_curDoc) {
-            this._curDoc = DocCast(viewData.config_curDoc);
-            return true;
-        }
-        return false;
-    };
     addDocTab = this.addLinkedDocTab;
 
     /**
@@ -634,9 +625,9 @@ export class CollectionCardView extends CollectionSubView() {
         return this.sortedDocs.map((doc, index) => {
             const cardsInRow = this.cardsInRowThatIncludesCardIndex(index);
 
-            const childScreenToLocal = this.childScreenToLocal(doc, index, doc === this._curDoc);
+            const childScreenToLocal = this.childScreenToLocal(doc, index, doc === this.curDoc());
 
-            const translateToCenterIfActive = () => (doc === this._curDoc ? (cardsInRow / 2 - (index % this._maxRowCount)) * 100 - 50 : 0);
+            const translateToCenterIfActive = () => (doc === this.curDoc() ? (cardsInRow / 2 - (index % this._maxRowCount)) * 100 - 50 : 0);
 
             const aspect = NumCast(doc.height) / NumCast(doc.width, 1);
             const vscale = Math.max(1,Math.min((this._props.PanelHeight() * 0.95 * this.fitContentScale * this.nativeScaling) / (aspect * this.childPanelWidth()), 
@@ -645,15 +636,15 @@ export class CollectionCardView extends CollectionSubView() {
             return (
                 <div
                     key={doc[Id]}
-                    className={`card-item${doc === this._curDoc ? '-active' : this.isAnyChildContentActive() ? '-inactive' : ''}`}
+                    className={`card-item${doc === this.curDoc() ? '-active' : this.isAnyChildContentActive() ? '-inactive' : ''}`}
                     onPointerUp={() => this.cardPointerUp(doc)}
                     style={{
                         width: this.childPanelWidth(),
                         height: 'max-content',
-                        transform: `translateY(${this.adjustCardYtoFitArch(doc === this._curDoc, index)}px)
+                        transform: `translateY(${this.adjustCardYtoFitArch(doc === this.curDoc(), index)}px)
                                     translateX(calc(${translateToCenterIfActive()}% + ${this.horizontalAdjustmentForPartialRows(index, cardsInRow)}px))
-                                    rotate(${doc !== this._curDoc ? this.rotate(index) : 0}rad)
-                                    scale(${doc === this._curDoc ? `${Math.min(hscale, vscale) * 100}%` : this._hoveredNodeIndex === index ? 1.1 : 1})`,
+                                    rotate(${doc !== this.curDoc()? this.rotate(index) : 0}rad)
+                                    scale(${doc === this.curDoc()? `${Math.min(hscale, vscale) * 100}%` : this._hoveredNodeIndex === index ? 1.1 : 1})`,
                     }} // prettier-ignore
                     onPointerEnter={() => this.setHoveredNodeIndex(index)}
                     onPointerLeave={() => this.setHoveredNodeIndex(-1)}>
@@ -671,10 +662,10 @@ export class CollectionCardView extends CollectionSubView() {
         isContentActive: emptyFunction,
         ScreenToLocalTransform: this.contentScreenToLocalXf,
     });
-    answered = action(() => {
-        this._curDoc = this.curDoc ? this.filteredChildDocs()[(this.filteredChildDocs().findIndex(d => d === this.curDoc()) + 1) % (this.filteredChildDocs().length || 1)] : undefined;
-    });
-    curDoc = () => this._curDoc;
+    answered = () => {
+        this.layoutDoc._card_curDoc = this.curDoc() ? this.filteredChildDocs()[(this.filteredChildDocs().findIndex(d => d === this.curDoc()) + 1) % (this.filteredChildDocs().length || 1)] : undefined;
+    };
+    curDoc = () => DocCast(this.layoutDoc._card_curDoc);
 
     render() {
         const fitContentScale = this.childCards.length === 0 ? 1 : this.fitContentScale;
diff --git a/src/client/views/collections/CollectionCarousel3DView.tsx b/src/client/views/collections/CollectionCarousel3DView.tsx
index 081e2fe5a..61a74dd7a 100644
--- a/src/client/views/collections/CollectionCarousel3DView.tsx
+++ b/src/client/views/collections/CollectionCarousel3DView.tsx
@@ -103,18 +103,10 @@ export class CollectionCarousel3DView extends CollectionSubView() {
     };
     getAnchor = (addAsAnnotation: boolean) => {
         const anchor = Docs.Create.ConfigDocument({ annotationOn: this.Document, config_carousel_index: this.layoutDoc._carousel_index as number });
-        PinDocView(anchor, { pinData: { type_collection: true, filters: true } }, this.Document);
+        PinDocView(anchor, { pinData: { collectionType: true, filters: true } }, this.Document);
         addAsAnnotation && Doc.AddDocToList(this.dataDoc, this.fieldKey + '_annotations', anchor); // when added as an annotation, links to anchors can be found as links to the document even if the anchors are not rendered
         return anchor;
     };
-    // pinned / linked anchor doc includes selected rows, graph titles, and graph colors
-    restoreView = (viewData: Doc) => {
-        if (viewData.config_carousel_index !== undefined && this.layoutDoc._carousel_index !== viewData.config_carousel_index) {
-            this.layoutDoc._carousel_index = viewData.config_carousel_index;
-            return true;
-        }
-        return false;
-    };
     addDocTab = this.addLinkedDocTab;
     @computed get content() {
         const currentIndex = NumCast(this.layoutDoc._carousel_index);
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index bfb58733d..f714e2a00 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -86,18 +86,10 @@ export class CollectionCarouselView extends CollectionSubView() {
 
     getAnchor = (addAsAnnotation: boolean) => {
         const anchor = Docs.Create.ConfigDocument({ annotationOn: this.Document, config_carousel_index: this.carouselIndex });
-        PinDocView(anchor, { pinData: { type_collection: true, filters: true } }, this.Document);
+        PinDocView(anchor, { pinData: { collectionType: true, filters: true } }, this.Document);
         addAsAnnotation && Doc.AddDocToList(this.dataDoc, this.fieldKey + '_annotations', anchor); // when added as an annotation, links to anchors can be found as links to the document even if the anchors are not rendered
         return anchor;
     };
-    // pinned / linked anchor doc includes selected rows, graph titles, and graph colors
-    restoreView = (viewData: Doc) => {
-        if (viewData.config_carousel_index !== undefined && this.layoutDoc._carousel_index !== viewData.config_carousel_index) {
-            this.layoutDoc._carousel_index = viewData.config_carousel_index;
-            return true;
-        }
-        return false;
-    };
     addDocTab = this.addLinkedDocTab;
 
     captionStyleProvider = (doc: Doc | undefined, captionProps: Opt<FieldViewProps>, property: string) => {
diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index 5738e7ce3..d75c633ac 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -52,7 +52,7 @@ export class CollectionTimeView extends CollectionSubView() {
             title: ComputedField.MakeFunction(`"${this.pivotField}"])`) as unknown as string, // title can take a functiono or a string
             annotationOn: this.Document,
         });
-        PinDocView(anchor, { pinData: { type_collection: true, pivot: true, filters: true } }, this.Document);
+        PinDocView(anchor, { pinData: { collectionType: true, pivot: true, filters: true } }, this.Document);
 
         if (addAsAnnotation) {
             // when added as an annotation, links to anchors can be found as links to the document even if the anchors are not rendered
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 0241fc2dd..ff711314b 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1743,7 +1743,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         });
         PinDocView(
             anchor,
-            { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ? { ...pinProps.pinData, poslayoutview: pinProps.pinData.dataview } : {}), pannable: !this.Document.isGroup, type_collection: true, filters: true } },
+            { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ? { ...pinProps.pinData, poslayoutview: pinProps.pinData.dataview } : {}), pannable: !this.Document.isGroup, collectionType: true, filters: true } },
             this.Document
         );
 
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 212dfec08..5d3672aee 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -14,7 +14,7 @@ import { StopEvent, lightOrDark, returnFalse, returnOne, setupMoveUpEvents } fro
 import { emptyFunction, stringHash } from '../../../../Utils';
 import { Doc, DocListCast, Field, FieldResult, FieldType, NumListCast, Opt, StrListCast } from '../../../../fields/Doc';
 import { Animation, DocData, TransitionTimer } from '../../../../fields/DocSymbols';
-import { Copy } from '../../../../fields/FieldSymbols';
+import { Copy, Id } from '../../../../fields/FieldSymbols';
 import { InkField } from '../../../../fields/InkField';
 import { List } from '../../../../fields/List';
 import { ObjectField } from '../../../../fields/ObjectField';
@@ -382,7 +382,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             if (typeof res === 'string') {
                 const resObj = JSON.parse(res);
                 console.log('Parsed GPT Result ', resObj);
-                // eslint-disable-next-line no-restricted-syntax
                 for (const key in resObj) {
                     if (resObj[key]) {
                         console.log('typeof property', typeof resObj[key]);
@@ -563,11 +562,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const datarange = [DocumentType.FUNCPLOT].includes(targetType);
         const dataview = [DocumentType.INK, DocumentType.COL, DocumentType.IMG, DocumentType.RTF].includes(targetType) && target?.activeFrame === undefined;
         const poslayoutview = [DocumentType.COL].includes(targetType) && target?.activeFrame === undefined;
-        const typeCollection = targetType === DocumentType.COL;
+        const collectionType = targetType === DocumentType.COL;
         const filters = true;
         const pivot = true;
         const dataannos = false;
-        return { scrollable, pannable, inkable, type_collection: typeCollection, pivot, map, filters, temporal, clippable, dataview, datarange, poslayoutview, dataannos };
+        return { scrollable, pannable, inkable, collectionType, pivot, map, filters, temporal, clippable, dataview, datarange, poslayoutview, dataannos };
     }
 
     @action
@@ -575,7 +574,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         /* empty */
     };
     @action
-    // eslint-disable-next-line default-param-last
     static restoreTargetDocView(bestTargetView: Opt<DocumentView>, activeItem: Doc, transTime: number, pinDocLayout: boolean = BoolCast(activeItem.config_pinLayout), pinDataTypes?: dataTypes, targetDoc?: Doc) {
         const bestTarget = bestTargetView?.Document ?? (targetDoc?.layout_unrendered ? DocCast(targetDoc?.annotationOn) : targetDoc);
         if (!bestTarget) return undefined;
@@ -701,7 +699,19 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 changed = true;
             }
         }
-        if ((pinDataTypes?.type_collection && activeItem.config_type_collection !== undefined) || (!pinDataTypes && activeItem.config_type_collection !== undefined)) {
+        if ((pinDataTypes?.collectionType && activeItem.config_card_curDoc !== undefined) || (!pinDataTypes && activeItem.config_card_curDoc !== undefined)) {
+            if (bestTarget._card_curDoc !== activeItem.config_card_curDoc) {
+                bestTarget._card_curDoc = activeItem.config_card_curDoc;
+                changed = true;
+            }
+        }
+        if ((pinDataTypes?.collectionType && activeItem.config_carousel_index !== undefined) || (!pinDataTypes && activeItem.config_carousel_index !== undefined)) {
+            if (bestTarget._carousel_index !== activeItem.config_carousel_index) {
+                bestTarget._carousel_index = activeItem.config_carousel_index;
+                changed = true;
+            }
+        }
+        if ((pinDataTypes?.collectionType && activeItem.config_type_collection !== undefined) || (!pinDataTypes && activeItem.config_type_collection !== undefined)) {
             if (bestTarget._type_collection !== activeItem.config_type_collection) {
                 bestTarget._type_collection = activeItem.config_type_collection;
                 changed = true;
@@ -1135,7 +1145,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     return false;
                 }
             } else if (doc.type !== DocumentType.PRES) {
-                // eslint-disable-next-line operator-assignment
                 if (!doc.presentation_targetDoc) doc.title = doc.title + ' - Slide';
                 doc.presentation_targetDoc = doc.createdFrom ?? doc; // dropped document will be a new embedding of an embedded document somewhere else.
                 doc.presentation_movement = PresMovement.Zoom;
@@ -1167,8 +1176,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             const tagDoc = Cast(curDoc.presentation_targetDoc, Doc, null);
             if (curDoc && curDoc === this.activeItem)
                 return (
-                    // eslint-disable-next-line react/no-array-index-key
-                    <div key={index} className="selectedList-items">
+                    <div key={doc[Id]} className="selectedList-items">
                         <b>
                             {index + 1}. {StrCast(curDoc.title)})
                         </b>
@@ -1176,15 +1184,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 );
             if (tagDoc)
                 return (
-                    // eslint-disable-next-line react/no-array-index-key
-                    <div key={index} className="selectedList-items">
+                    <div key={doc[Id]} className="selectedList-items">
                         {index + 1}. {StrCast(curDoc.title)}
                     </div>
                 );
             if (curDoc)
                 return (
-                    // eslint-disable-next-line react/no-array-index-key
-                    <div key={index} className="selectedList-items">
+                    <div key={doc[Id]} className="selectedList-items">
                         {index + 1}. {StrCast(curDoc.title)}
                     </div>
                 );
@@ -1369,7 +1375,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             const tagDoc = PresBox.targetRenderedDoc(doc);
             const srcContext = Cast(tagDoc.embedContainer, Doc, null);
             const labelCreator = (top: number, left: number, edge: number, fontSize: number) => (
-                // eslint-disable-next-line react/no-array-index-key
                 <div className="pathOrder" key={tagDoc.id + 'pres' + index} style={{ top, left, width: edge, height: edge, fontSize }} onClick={() => this.selectElement(doc)}>
                     <div className="pathOrder-frame">{index + 1}</div>
                 </div>
@@ -1620,7 +1625,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         this.updateEffect(this.activeItem.presentation_effect as PresEffect, false, true);
         this.updateEffect(this.activeItem.presBulletEffect as PresEffect, true, true);
         this.updateEffectDirection(this.activeItem.presentation_effectDirection as PresEffectDirection, true);
-        // eslint-disable-next-line camelcase
         const { presentation_transition: pt, presentation_duration: pd, presentation_hideBefore: ph, presentation_hideAfter: pa } = this.activeItem;
         array.forEach(curDoc => {
             curDoc.presentation_transition = pt;
@@ -2018,7 +2022,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                 <div className="presBox-effects">
                                     {this.generatedAnimations.map((elem, i) => (
                                         <div
-                                            // eslint-disable-next-line react/no-array-index-key
                                             key={i}
                                             className="presBox-effect-container"
                                             onClick={() => {
@@ -3046,7 +3049,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     <div className="Slide">
                         {mode !== CollectionViewType.Invalid ? (
                             <CollectionView
-                                // eslint-disable-next-line react/jsx-props-no-spreading
                                 {...this._props}
                                 PanelWidth={this._props.PanelWidth}
                                 PanelHeight={this.panelHeight}
diff --git a/src/fields/ScriptField.ts b/src/fields/ScriptField.ts
index 582c09f29..0252961d3 100644
--- a/src/fields/ScriptField.ts
+++ b/src/fields/ScriptField.ts
@@ -85,6 +85,7 @@ async function deserializeScript(scriptIn: ScriptField) {
 }
 
 @scriptingGlobal
+// eslint-disable-next-line no-use-before-define
 @Deserializable('script', (obj: unknown) => deserializeScript(obj as ScriptField))
 export class ScriptField extends ObjectField {
     @serializable
@@ -137,7 +138,6 @@ export class ScriptField extends ObjectField {
     [ToString]() {
         return this.script.originalScript;
     }
-    // eslint-disable-next-line default-param-last
     public static CompileScript(script: string, params: object = {}, addReturn = false, capturedVariables?: { [name: string]: Doc | string | number | boolean }, transformer?: Transformer) {
         return CompileScript(script, {
             params: {
@@ -156,13 +156,11 @@ export class ScriptField extends ObjectField {
         });
     }
 
-    // eslint-disable-next-line default-param-last
     public static MakeFunction(script: string, params: object = {}, capturedVariables?: { [name: string]: Doc | string | number | boolean }) {
         const compiled = ScriptField.CompileScript(script, params, true, capturedVariables);
         return compiled.compiled ? new ScriptField(compiled) : undefined;
     }
 
-    // eslint-disable-next-line default-param-last
     public static MakeScript(script: string, params: object = {}, capturedVariables?: { [name: string]: Doc | string | number | boolean }) {
         const compiled = ScriptField.CompileScript(script, params, false, capturedVariables);
         return compiled.compiled ? new ScriptField(compiled) : undefined;
@@ -186,6 +184,7 @@ export class ScriptField extends ObjectField {
 }
 
 @scriptingGlobal
+// eslint-disable-next-line no-use-before-define
 @Deserializable('computed', (obj: unknown) => deserializeScript(obj as ComputedField))
 export class ComputedField extends ScriptField {
     static undefined = '__undefined';
@@ -229,7 +228,6 @@ export class ComputedField extends ScriptField {
     [ToValue](doc: Doc)   { return ComputedField.useComputed ? { value: this.value(doc) } : undefined; } // prettier-ignore
     [Copy](): ObjectField { return new ComputedField(this.script, this.setterscript, this.rawscript); } // prettier-ignore
 
-    // eslint-disable-next-line default-param-last
     public static MakeFunction(script: string, params: object = {}, capturedVariables?: { [name: string]: Doc | string | number | boolean }, setterscript?: string) {
         const compiled = ScriptField.CompileScript(script, params, true, { value: '', ...capturedVariables });
         const compiledsetter = setterscript ? ScriptField.CompileScript(setterscript, { ...params, value: 'any' }, false, capturedVariables) : undefined;
-- 
cgit v1.2.3-70-g09d2


From 6b070202843e1e2a11c09c4d29aee4ab8750f31f Mon Sep 17 00:00:00 2001
From: geireann <geireann.lindfield@gmail.com>
Date: Thu, 24 Oct 2024 12:01:21 -0400
Subject: fixed pinning of card and carousel views.

---
 src/client/views/collections/CollectionCardDeckView.tsx   | 6 +++---
 src/client/views/collections/CollectionCarousel3DView.tsx | 6 +++---
 src/client/views/collections/CollectionCarouselView.tsx   | 6 +++---
 3 files changed, 9 insertions(+), 9 deletions(-)

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

diff --git a/src/client/views/collections/CollectionCardDeckView.tsx b/src/client/views/collections/CollectionCardDeckView.tsx
index beb45f326..3a5cb5f37 100644
--- a/src/client/views/collections/CollectionCardDeckView.tsx
+++ b/src/client/views/collections/CollectionCardDeckView.tsx
@@ -19,7 +19,7 @@ import { dropActionType } from '../../util/DropActionTypes';
 import { SnappingManager } from '../../util/SnappingManager';
 import { Transform } from '../../util/Transform';
 import { undoable } from '../../util/UndoManager';
-import { PinDocView } from '../PinFuncs';
+import { PinDocView, PinProps } from '../PinFuncs';
 import { StyleProp } from '../StyleProp';
 import { TagItem } from '../TagsView';
 import { DocumentView, DocumentViewProps } from '../nodes/DocumentView';
@@ -608,9 +608,9 @@ export class CollectionCardView extends CollectionSubView() {
         }
         return undefined;
     });
-    getAnchor = (addAsAnnotation: boolean) => {
+    getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => {
         const anchor = Docs.Create.ConfigDocument({ annotationOn: this.Document, config_card_curDoc: this.curDoc() });
-        PinDocView(anchor, { pinData: { collectionType: true, filters: true } }, this.Document);
+        PinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { collectionType: true, filters: true } }, this.Document);
         addAsAnnotation && Doc.AddDocToList(this.dataDoc, this.fieldKey + '_annotations', anchor); // when added as an annotation, links to anchors can be found as links to the document even if the anchors are not rendered
         return anchor;
     };
diff --git a/src/client/views/collections/CollectionCarousel3DView.tsx b/src/client/views/collections/CollectionCarousel3DView.tsx
index c080ba27e..b7ecf9a2f 100644
--- a/src/client/views/collections/CollectionCarousel3DView.tsx
+++ b/src/client/views/collections/CollectionCarousel3DView.tsx
@@ -12,7 +12,7 @@ import { DocumentType } from '../../documents/DocumentTypes';
 import { Docs } from '../../documents/Documents';
 import { DragManager } from '../../util/DragManager';
 import { Transform } from '../../util/Transform';
-import { PinDocView } from '../PinFuncs';
+import { PinDocView, PinProps } from '../PinFuncs';
 import { StyleProp } from '../StyleProp';
 import { DocumentView } from '../nodes/DocumentView';
 import { FocusViewOptions } from '../nodes/FocusViewOptions';
@@ -107,9 +107,9 @@ export class CollectionCarousel3DView extends CollectionSubView() {
         }
         return undefined;
     };
-    getAnchor = (addAsAnnotation: boolean) => {
+    getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => {
         const anchor = Docs.Create.ConfigDocument({ annotationOn: this.Document, config_carousel_index: this.layoutDoc._carousel_index as number });
-        PinDocView(anchor, { pinData: { collectionType: true, filters: true } }, this.Document);
+        PinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { collectionType: true, filters: true } }, this.Document);
         addAsAnnotation && Doc.AddDocToList(this.dataDoc, this.fieldKey + '_annotations', anchor); // when added as an annotation, links to anchors can be found as links to the document even if the anchors are not rendered
         return anchor;
     };
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index f714e2a00..87c6e3e5c 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -8,7 +8,7 @@ import { BoolCast, DocCast, NumCast, ScriptCast, StrCast } from '../../../fields
 import { DocumentType } from '../../documents/DocumentTypes';
 import { Docs } from '../../documents/Documents';
 import { DragManager } from '../../util/DragManager';
-import { PinDocView } from '../PinFuncs';
+import { PinDocView, PinProps } from '../PinFuncs';
 import { StyleProp } from '../StyleProp';
 import { DocumentView } from '../nodes/DocumentView';
 import { FieldViewProps } from '../nodes/FieldView';
@@ -84,9 +84,9 @@ export class CollectionCarouselView extends CollectionSubView() {
         return undefined;
     };
 
-    getAnchor = (addAsAnnotation: boolean) => {
+    getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => {
         const anchor = Docs.Create.ConfigDocument({ annotationOn: this.Document, config_carousel_index: this.carouselIndex });
-        PinDocView(anchor, { pinData: { collectionType: true, filters: true } }, this.Document);
+        PinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { collectionType: true, filters: true } }, this.Document);
         addAsAnnotation && Doc.AddDocToList(this.dataDoc, this.fieldKey + '_annotations', anchor); // when added as an annotation, links to anchors can be found as links to the document even if the anchors are not rendered
         return anchor;
     };
-- 
cgit v1.2.3-70-g09d2


From 63f9f9573dab9745d2f570e1917b325a474fdd93 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 28 Oct 2024 20:07:51 -0400
Subject: reworked LabelBox's to be fully editable.  gor rid of singleLine for
 label boxes. made a text_fitBox field to render text with a labelBox. fixed
 styleprovider in freeformview clusters to be untracked() to avoid
 invalidations.  added a default text color to Settings panel.   fixed
 clicking text buttons to not lose focus in labelBox.

---
 package-lock.json                                  |   8 +-
 package.json                                       |   2 +-
 src/client/documents/DocUtils.ts                   |   5 +-
 src/client/documents/Documents.ts                  |  14 +-
 src/client/util/CurrentUserUtils.ts                |   9 +-
 src/client/util/SettingsManager.tsx                |  24 +-
 src/client/views/MarqueeAnnotator.tsx              |   3 +-
 src/client/views/PinFuncs.ts                       |   4 +-
 src/client/views/SidebarAnnos.tsx                  |   4 +-
 src/client/views/StyleProvider.tsx                 |   4 +-
 src/client/views/StyleProviderQuiz.tsx             |   2 +-
 .../views/collections/CollectionCardDeckView.tsx   |   1 -
 .../CollectionFreeFormClusters.ts                  |   6 +-
 .../collections/collectionSchema/SchemaRowBox.tsx  |   2 +-
 src/client/views/global/globalScripts.ts           |  24 +-
 .../views/nodes/DataVizBox/DocCreatorMenu.tsx      |  35 +--
 src/client/views/nodes/DocumentView.tsx            |  12 +-
 src/client/views/nodes/EquationBox.tsx             |   2 +-
 .../views/nodes/FontIconBox/FontIconBox.scss       |   4 +
 src/client/views/nodes/FontIconBox/FontIconBox.tsx |   4 +-
 src/client/views/nodes/LabelBox.scss               |   1 -
 src/client/views/nodes/LabelBox.tsx                | 168 +++++++++----
 .../views/nodes/formattedText/FormattedTextBox.tsx | 259 +++++++++++----------
 .../formattedText/ProsemirrorExampleTransfer.ts    |   4 +-
 .../views/nodes/formattedText/RichTextMenu.tsx     |  71 +++---
 .../views/nodes/formattedText/RichTextRules.ts     |   2 +-
 src/client/views/nodes/trails/PresBox.tsx          |  14 +-
 src/client/views/pdf/GPTPopup/GPTPopup.tsx         |  23 +-
 src/fields/documentSchemas.ts                      |   7 +-
 29 files changed, 426 insertions(+), 292 deletions(-)

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

diff --git a/package-lock.json b/package-lock.json
index 4e95fcee0..43dcb1c3f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -72,7 +72,7 @@
                 "body-parser": "^1.20.2",
                 "bootstrap": "^5.3.2",
                 "brotli": "^1.3.3",
-                "browndash-components": "^0.1.50",
+                "browndash-components": "0.1.53",
                 "browser-assert": "^1.2.1",
                 "bson": "^6.2.0",
                 "canvas": "^2.11.2",
@@ -13575,9 +13575,9 @@
             }
         },
         "node_modules/browndash-components": {
-            "version": "0.1.50",
-            "resolved": "https://registry.npmjs.org/browndash-components/-/browndash-components-0.1.50.tgz",
-            "integrity": "sha512-8EgU82os8/Tg3gayh+nYXCQZ8P7GGQWnibz0U2RM0sXTbc3BWTSYiC1Sj1VpL7HPkZ1KMGHzWynCo9QKIOWZBg==",
+            "version": "0.1.53",
+            "resolved": "https://registry.npmjs.org/browndash-components/-/browndash-components-0.1.53.tgz",
+            "integrity": "sha512-lBZlXe2u/6d9NlrR+fH+9ncCFGRUxGqJRejpfhbhVmQ4Q4uvP9LlmTuaVQElqys5wGUyK0SuqrLYPGhJthdQLQ==",
             "dependencies": {
                 "@emotion/react": "^11.11.1",
                 "@emotion/styled": "^11.11.0",
diff --git a/package.json b/package.json
index 06179fd7d..63d5e387d 100644
--- a/package.json
+++ b/package.json
@@ -155,7 +155,7 @@
         "body-parser": "^1.20.2",
         "bootstrap": "^5.3.2",
         "brotli": "^1.3.3",
-        "browndash-components": "^0.1.50",
+        "browndash-components": "0.1.53",
         "browser-assert": "^1.2.1",
         "bson": "^6.2.0",
         "canvas": "^2.11.2",
diff --git a/src/client/documents/DocUtils.ts b/src/client/documents/DocUtils.ts
index 19f3c89ef..e3cb5e72c 100644
--- a/src/client/documents/DocUtils.ts
+++ b/src/client/documents/DocUtils.ts
@@ -679,10 +679,13 @@ export namespace DocUtils {
                 ? {} // if the new doc will inherit from a template, don't set any layout fields since that would block the inheritance
                 : {
                       _width: width || 200,
-                      _height: 35,
+                      _height: BoolCast(Doc.UserDoc().fitBox) ? 70 : 35,
                       _layout_centered: BoolCast(Doc.UserDoc()._layout_centered),
                       _layout_fitWidth: true,
                       _layout_autoHeight: true,
+                      text_fitBox: BoolCast(Doc.UserDoc().fitBox),
+                      text_align: StrCast(Doc.UserDoc().textAlign),
+                      text_fontColor: StrCast(Doc.UserDoc().fontColor),
                   }),
         });
 
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 98e8bc932..b0e1a7545 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -294,7 +294,6 @@ export class DocumentOptions {
     _yMargin?: NUMt = new NumInfo('gap between top edge of dcoument and start of masonry/stacking layouts', false);
     _xPadding?: NUMt = new NumInfo('x padding', false);
     _yPadding?: NUMt = new NumInfo('y padding', false);
-    _singleLine?: boolean; // whether label box is restricted to one line of text
     _createDocOnCR?: boolean; // whether carriage returns and tabs create new text documents
     _columnWidth?: NUMt = new NumInfo('width of table column', false);
     _columnsHideIfEmpty?: BOOLt = new BoolInfo('whether stacking view column headings should be hidden');
@@ -302,10 +301,14 @@ export class DocumentOptions {
     _caption_yMargin?: NUMt = new NumInfo('y margin of caption inside of a carousel collection', false, true);
     icon_nativeWidth?: NUMt = new NumInfo('native width of icon view', false, true);
     icon_nativeHeight?: NUMt = new NumInfo('native height of icon view', false, true);
-    _text_fontSize?: string;
-    _text_fontFamily?: string;
-    _text_fontWeight?: string;
-    text_align?: STRt = new StrInfo('horizontal text alignment default');
+    text_fontSize?: string;
+    text_fontFamily?: string;
+    text_fontWeight?: string;
+    text_fitBox?: BOOLt = new BoolInfo("whether text box should be scaled to fit it's containing render box");
+    text_align?: STRt = new StrInfo('horizontal text alignment default', undefined, undefined, ['left', 'center', 'right']);
+    title_align?: STRt = new StrInfo('horizontal title alignment in label box', undefined, undefined, ['left', 'center', 'right']);
+    title_transform?: STRt = new StrInfo('transformation to apply to title in label box (eg., uppercase)', undefined, undefined, ['uppercase', 'lowercase', 'capitalize']);
+    text_transform?: STRt = new StrInfo('transformation to apply to text in text box (eg., uppercase)', undefined, undefined, ['uppercase', 'lowercase', 'capitalize']);
     text_placeholder?: BOOLt = new BoolInfo('makes the text act like a placeholder and automatically select when the text box is selected');
     fontSize?: string;
     _pivotField?: string; // field key used to determine headings for sections in stacking, masonry, pivot views
@@ -496,7 +499,6 @@ export class DocumentOptions {
     sidebar_type_collection?: string; // collection type of text sidebar
 
     data_dashboards?: List<FieldType>; // list of dashboards used in shareddocs;
-    textTransform?: string;
     letterSpacing?: string;
     iconTemplate?: string; // name of icon template style
     icon_fieldKey?: string; // specifies the icon template to use (e.g., icon_fieldKey='george', then the icon template's name is icon_george; otherwise, the template's name would be icon_<type> where type is the Doc's type(pdf,rich text, etc))
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 30c75c659..b3aa5e145 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -186,7 +186,7 @@ export class CurrentUserUtils {
         const templateIconsDoc = DocUtils.AssignOpts(DocCast(doc[field]), reqdOpts) ?? (doc[field] = Docs.Create.TreeDocument([], reqdOpts));
 
         const labelBox = (opts: DocumentOptions, fieldKey:string) => Docs.Create.LabelDocument({
-            layout: LabelBox.LayoutString(fieldKey), textTransform: "unset", letterSpacing: "unset", _singleLine: false, _label_minFontSize: 14, _label_maxFontSize: 14, layout_borderRounding: "5px",  _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, ...opts
+            layout: LabelBox.LayoutString(fieldKey), letterSpacing: "unset", _label_minFontSize: 14, _label_maxFontSize: 14, layout_borderRounding: "5px",  _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, ...opts
         });
         const imageBox = (opts: DocumentOptions, fieldKey:string) => Docs.Create.ImageDocument( "http://www.cs.brown.edu/~bcz/noImage.png", { layout:ImageBox.LayoutString(fieldKey), "icon_nativeWidth": 360 / 4, "icon_nativeHeight": 270 / 4, iconTemplate:DocumentType.IMG,  _width: 360 / 4, _height: 270 / 4,  _layout_showTitle: "title", ...opts });
         const fontBox = (opts:DocumentOptions, fieldKey:string) => Docs.Create.FontIconDocument({ layout:FontIconBox.LayoutString(fieldKey), _nativeHeight: 30, _nativeWidth: 30, _width: 30, _height: 30, ...opts  });
@@ -268,7 +268,7 @@ export class CurrentUserUtils {
             MakeTemplate(Docs.Create.MultirowDocument(
                     [
                         Docs.Create.MulticolumnDocument([], { title: "hero", _height: 200,  isSystem: true }),
-                        Docs.Create.TextDocument("", { title: "text", _layout_fitWidth:true,  _height: 100, isSystem: true, _text_fontFamily: StrCast(Doc.UserDoc().fontFamily), _text_fontSize: StrCast(Doc.UserDoc().fontSize) })
+                        Docs.Create.TextDocument("", { title: "text", _layout_fitWidth:true,  _height: 100, isSystem: true, text_fontFamily: StrCast(Doc.UserDoc().fontFamily), text_fontSize: StrCast(Doc.UserDoc().fontSize) })
                     ], {...opts, title: "Slide View Template"}));
         const plotlyApi = () => {
             let plotly = Doc.MyPublishedDocs.find(fdoc => fdoc.title === "@plotly");
@@ -392,7 +392,7 @@ pie title Minerals in my tap water
             {key: "Trail",      creator: Docs.Create.PresDocument,                          opts: { _width: 400, _height: 30, _type_collection: CollectionViewType.Stacking, _layout_dontCenter:'xy', dropAction: dropActionType.embed, treeView_HideTitle: true, _layout_fitWidth:true, layout_boxShadow: "0 0" }},
             {key: "Tab",        creator: opts => Docs.Create.FreeformDocument([], opts),    opts: { _width: 500, _height: 800, _layout_fitWidth: true, _freeform_backgroundGrid: true, }},
             {key: "Slide",      creator: opts => Docs.Create.TreeDocument([], opts),        opts: { _width: 300, _height: 200, _type_collection: CollectionViewType.Tree, 
-                                                                                                treeView_HasOverlay: true, _text_fontSize: "20px", _layout_autoHeight: true, 
+                                                                                                treeView_HasOverlay: true, text_fontSize: "20px", _layout_autoHeight: true, 
                                                                                                 dropAction:dropActionType.move, treeView_Type: TreeViewType.outline, 
                                                                                                 backgroundColor: "white", _xMargin: 0, _yMargin: 0, _createDocOnCR: true 
                                                                                             }, funcs: {title: 'this.text?.Text'}},
@@ -731,7 +731,7 @@ pie title Minerals in my tap water
         return [
             { 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: "Font Size",toolTip: "Font size (%size)",    btnType: ButtonType.NumberDropdownButton,              toolType:"fontSize", ignoreClick: true,   scripts: {script:  '{ return setFontAttr(this.toolType, value, _readOnly_);}'}, numBtnMax: 200, numBtnMin: 9 },
             { 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_);}'} },
@@ -746,6 +746,7 @@ pie title Minerals in my tap water
                 { 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: "Fit Box",  toolTip: "Fit text to box",      btnType: ButtonType.ToggleButton, icon: "object-group",toolType:"fitBox",    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()'}},
diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx
index 9200d68db..33d7d90a8 100644
--- a/src/client/util/SettingsManager.tsx
+++ b/src/client/util/SettingsManager.tsx
@@ -1,5 +1,5 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { Button, ColorPicker, Dropdown, DropdownType, EditableText, Group, NumberDropdown, Size, Toggle, ToggleType, Type } from 'browndash-components';
+import { Button, ColorPicker, Colors, Dropdown, DropdownType, EditableText, Group, NumberDropdown, Size, Toggle, ToggleType, Type } from 'browndash-components';
 import { action, computed, makeObservable, observable, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
@@ -45,7 +45,6 @@ export class SettingsManager extends React.Component<object> {
     public closeMgr = action(() => {
         this._isOpen = false;
     });
-    // eslint-disable-next-line react/no-unused-class-component-methods
     public openMgr = action(() => {
         this._isOpen = true;
     });
@@ -354,6 +353,27 @@ export class SettingsManager extends React.Component<object> {
                                     Doc.UserDoc().fontSize = val + 'px';
                                 }}
                             />
+                            <ColorPicker
+                                color={SettingsManager.userColor}
+                                type={Type.PRIM}
+                                defaultPickerType="Classic"
+                                selectedColor={StrCast(Doc.UserDoc().textBackgroundColor, Colors.LIGHT_GRAY)}
+                                background={SnappingManager.userBackgroundColor}
+                                icon={<FontAwesomeIcon icon="palette" size="lg" />}
+                                tooltip="default text background color"
+                                label="background"
+                                setSelectedColor={value => {
+                                    Doc.UserDoc().textBackgroundColor = value;
+                                    // if (!this.colorBatch) this.colorBatch = UndoManager.StartBatch(`Set ${tooltip} color`);
+                                    // this.colorScript?.script.run({ this: this.Document, value: value, _readOnly_: false });
+                                }}
+                                setFinalColor={value => {
+                                    Doc.UserDoc().textBackgroundColor = value;
+                                    // this.colorScript?.script.run({ this: this.Document, value: value, _readOnly_: false });
+                                    // this.colorBatch?.end();
+                                    // this.colorBatch = undefined;
+                                }}
+                            />
                             <Dropdown
                                 items={fontFamilies.map(val => ({
                                     text: val,
diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx
index 7266875c5..fa1123a2d 100644
--- a/src/client/views/MarqueeAnnotator.tsx
+++ b/src/client/views/MarqueeAnnotator.tsx
@@ -34,7 +34,7 @@ export interface MarqueeAnnotatorProps {
     getPageFromScroll?: (top: number) => number;
     finishMarquee: (x?: number, y?: number) => void;
     anchorMenuClick?: () => undefined | ((anchor: Doc) => void);
-    anchorMenuFlashcard?: () => Promise<String>;
+    anchorMenuFlashcard?: () => Promise<string>;
     anchorMenuCrop?: (anchor: Doc | undefined, addCrop: boolean) => Doc | undefined;
     highlightDragSrcColor?: string;
 }
@@ -220,7 +220,6 @@ export class MarqueeAnnotator extends ObservableReactComponent<MarqueeAnnotatorP
                   e.preventDefault();
                   e.stopPropagation();
                   let cropRegion: Doc | undefined;
-                  // eslint-disable-next-line no-return-assign
                   const sourceAnchorCreator = () => (cropRegion = this.highlight('', true, undefined, true)); // hyperlink color
                   const targetCreator = (/* annotationOn: Doc | undefined */) => this.props.anchorMenuCrop!(cropRegion, false)!;
                   DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(this.props.docView(), sourceAnchorCreator, targetCreator), e.pageX, e.pageY, {
diff --git a/src/client/views/PinFuncs.ts b/src/client/views/PinFuncs.ts
index bed473eec..ab02c2d07 100644
--- a/src/client/views/PinFuncs.ts
+++ b/src/client/views/PinFuncs.ts
@@ -1,4 +1,4 @@
-import { Doc, DocListCast } from '../../fields/Doc';
+import { Doc, DocListCast, Field } from '../../fields/Doc';
 import { DocData } from '../../fields/DocSymbols';
 import { Copy, Id } from '../../fields/FieldSymbols';
 import { List } from '../../fields/List';
@@ -74,7 +74,7 @@ export function PinDocView(pinDocIn: Doc, pinProps: PinProps, targetDoc: Doc) {
         const fkey = Doc.LayoutFieldKey(targetDoc);
         if (pinProps.pinData.dataview) {
             pinDoc.config_usePath = targetDoc[fkey + '_usePath'];
-            pinDoc.config_data = targetDoc[fkey] instanceof ObjectField ? (targetDoc[fkey] as ObjectField)[Copy]() : targetDoc.data;
+            pinDoc.config_data = Field.Copy(targetDoc[fkey]);
         }
         if (pinProps.pinData.dataannos) {
             const fieldKey = Doc.LayoutFieldKey(targetDoc);
diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx
index 1f3ad8444..87076bf65 100644
--- a/src/client/views/SidebarAnnos.tsx
+++ b/src/client/views/SidebarAnnos.tsx
@@ -78,8 +78,8 @@ export class SidebarAnnos extends ObservableReactComponent<FieldViewProps & Extr
             _height: 50,
             _layout_fitWidth: true,
             _layout_autoHeight: true,
-            _text_fontSize: StrCast(Doc.UserDoc().fontSize),
-            _text_fontFamily: StrCast(Doc.UserDoc().fontFamily),
+            text_fontSize: StrCast(Doc.UserDoc().fontSize),
+            text_fontFamily: StrCast(Doc.UserDoc().fontFamily),
         });
         Doc.SetSelectOnLoad(target);
         FormattedTextBox.DontSelectInitialText = true;
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index 8859f6464..12c032964 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -251,7 +251,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps &
                 case DocumentType.PRESELEMENT: docColor = docColor || ""; break;
                 case DocumentType.PRES:        docColor = docColor || 'transparent'; break;
                 case DocumentType.FONTICON:    docColor = boxBackground ? undefined : docColor || Colors.DARK_GRAY; break;
-                case DocumentType.RTF:         docColor = docColor || Colors.LIGHT_GRAY; break;
+                case DocumentType.RTF:         docColor = docColor || StrCast(Doc.UserDoc().textBackgroundColor, Colors.LIGHT_GRAY); break;
                 case DocumentType.LINK:        docColor = (isAnchor ? docColor : undefined); break;
                 case DocumentType.INK:         docColor = doc?.stroke_isInkMask ? 'rgba(0,0,0,0.7)' : undefined; break;
                 case DocumentType.EQUATION:    docColor = docColor || 'transparent'; break;
@@ -313,7 +313,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps &
                         ? undefined // if it's a background & has a cluster color, make the shadow spread really big
                         : fieldKey.includes('_inline')  // if doc is an inline document in a text box
                         ? `${Colors.DARK_GRAY} ${StrCast(doc.layout_boxShadow, '0vw 0vw 0.1vw')}`
-                        : DocCast(doc.embedContainer)?.type === DocumentType.RTF && !isInk() // if doc is embedded in a text document (but not an inline)
+                        :doc.rootDocument !== doc.embedContainer && DocCast(doc.embedContainer)?.type === DocumentType.RTF && !isInk() // if doc is embedded in a text document (but not an inline) and this isn't a simple text template (where the layoutDoc's rootDocument is its embed container)
                         ? `${Colors.DARK_GRAY} ${StrCast(doc.layout_boxShadow, '0.2vw 0.2vw 0.8vw')}`
                         : StrCast(doc.layout_boxShadow, '');
             }
diff --git a/src/client/views/StyleProviderQuiz.tsx b/src/client/views/StyleProviderQuiz.tsx
index 1f2ad1485..f07a2c803 100644
--- a/src/client/views/StyleProviderQuiz.tsx
+++ b/src/client/views/StyleProviderQuiz.tsx
@@ -66,7 +66,7 @@ export namespace styleProviderQuiz {
             newCol.zIndex = 1000;
             newCol.forceActive = true;
             newCol.quiz = text;
-            newCol[DocData].textTransform = 'none';
+            newCol[DocData][Doc.LayoutFieldKey(newCol) + '_transform'] = 'none';
             Doc.AddDocToList(img.Document, '_quizBoxes', newCol);
             img.addDocument(newCol);
             //   img._loading = false;
diff --git a/src/client/views/collections/CollectionCardDeckView.tsx b/src/client/views/collections/CollectionCardDeckView.tsx
index beb45f326..0ba6b679c 100644
--- a/src/client/views/collections/CollectionCardDeckView.tsx
+++ b/src/client/views/collections/CollectionCardDeckView.tsx
@@ -620,7 +620,6 @@ export class CollectionCardView extends CollectionSubView() {
      * Actually renders all the cards
      */
     @computed get renderCards() {
-        console.log('CHILDPw = ' + this.childPanelWidth());
         // Map sorted documents to their rendered components
         return this.sortedDocs.map((doc, index) => {
             const cardsInRow = this.cardsInRowThatIncludesCardIndex(index);
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormClusters.ts b/src/client/views/collections/collectionFreeForm/CollectionFreeFormClusters.ts
index 6ad67a864..8530f7a23 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormClusters.ts
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormClusters.ts
@@ -1,4 +1,4 @@
-import { action, observable } from 'mobx';
+import { action, observable, untracked } from 'mobx';
 import { CollectionFreeFormView } from '.';
 import { intersectRect } from '../../../../Utils';
 import { Doc, Opt } from '../../../../fields/Doc';
@@ -179,7 +179,9 @@ export class CollectionFreeFormClusters {
     };
 
     styleProvider = (doc: Opt<Doc>, props: Opt<FieldViewProps>, property: string) => {
-        if (doc && this.childDocs?.includes(doc))
+        // without untracked, every inquired style property for any Doc will be invalidated if a change is made to the collection's childDocs.
+        // this prevents that by assuming that a Doc is generally always (or never) a member of childDocs - if it's removed or added, then all of its properties get updated anyway.
+        if (doc && untracked(() => this.childDocs)?.includes(doc))
             switch (property.split(':')[0]) {
                 case StyleProp.BackgroundColor:
                     {
diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
index 6ffb0865a..7f519b065 100644
--- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
@@ -188,7 +188,7 @@ export class SchemaRowBox extends ViewBoxBaseComponent<SchemaRowBoxProps>() {
                             selectedCells={this.selectedCells}
                             selectedCol={this.selectedCol}
                             setColumnValues={this.setColumnValues}
-                            oneLine={BoolCast(this.schemaDoc?._singleLine)}
+                            oneLine={BoolCast(this.schemaDoc?._schema_singleLine)}
                             menuTarget={this.schemaView.MenuTarget}
                             transform={() => {
                                 const ind = index === this.schemaView.columnKeys.length - 1 ? this.schemaView.columnKeys.length - 3 : index;
diff --git a/src/client/views/global/globalScripts.ts b/src/client/views/global/globalScripts.ts
index 954c79f7d..013b18a97 100644
--- a/src/client/views/global/globalScripts.ts
+++ b/src/client/views/global/globalScripts.ts
@@ -295,10 +295,10 @@ ScriptingGlobals.add(function setTagFilter(tag: string, added: boolean, checkRes
 }, '');
 
 // eslint-disable-next-line prefer-arrow-callback
-ScriptingGlobals.add(function setFontAttr(attr: 'font' | 'fontColor' | 'highlight' | 'fontSize' | 'alignment', value: string | number, checkResult?: boolean) {
+ScriptingGlobals.add(function setFontAttr(attr: 'font' | 'fontColor' | 'highlight' | 'fontSize', value: string | number, checkResult?: boolean) {
     const editorView = RichTextMenu.Instance?.TextView?.EditorView;
     // prettier-ignore
-    const map: Map<'font'|'fontColor'|'highlight'|'fontSize'|'alignment', { checkResult: () => string | undefined; setDoc: () => void;}> = new Map([
+    const map: Map<'font'|'fontColor'|'highlight'|'fontSize', { checkResult: () => string | undefined; setDoc: () => void;}> = new Map([
         ['font', {
             checkResult: () => RichTextMenu.Instance?.fontFamily,
             setDoc: () => value && RichTextMenu.Instance?.setFontField(value.toString(), 'fontFamily'),
@@ -311,10 +311,6 @@ ScriptingGlobals.add(function setFontAttr(attr: 'font' | 'fontColor' | 'highligh
             checkResult: () => RichTextMenu.Instance?.fontColor,
             setDoc: () => value && RichTextMenu.Instance?.setFontField(value.toString(), 'fontColor'),
         }],
-        ['alignment', {
-            checkResult: () => RichTextMenu.Instance?.textAlign,
-            setDoc: () => { value && editorView?.state ? RichTextMenu.Instance?.align(editorView, editorView.dispatch, value.toString() as "center"|"left"|"right"):(Doc.UserDoc().textAlign = value); },
-        }],
         ['fontSize', {
             checkResult: () => RichTextMenu.Instance?.fontSize.replace('px', ''),
             setDoc: () => {
@@ -334,7 +330,7 @@ ScriptingGlobals.add(function setFontAttr(attr: 'font' | 'fontColor' | 'highligh
     return undefined;
 });
 
-type attrname = 'noAutoLink' | 'dictation' | 'bold' | 'italics' | 'elide' | 'underline' | 'left' | 'center' | 'right' | 'vcent' | 'bullet' | 'decimal';
+type attrname = 'noAutoLink' | 'dictation' | 'fitBox' | 'bold' | 'italics' | 'elide' | 'underline' | 'left' | 'center' | 'right' | 'vcent' | 'bullet' | 'decimal';
 type attrfuncs = [attrname, { checkResult: () => boolean; toggle?: () => unknown }];
 
 // eslint-disable-next-line prefer-arrow-callback
@@ -343,14 +339,10 @@ ScriptingGlobals.add(function toggleCharStyle(charStyle: attrname, checkResult?:
     const editorView = textView?.EditorView;
     // prettier-ignore
     const alignments:attrfuncs[] = (['left','right','center','vcent'] as ("left"|"center"|"right"|"vcent")[]).map((where) =>
-        [ where, {      checkResult: () => editorView ? (where === 'vcent' ?  RichTextMenu.Instance?.textVcenter ?? false:
-                                                                             (RichTextMenu.Instance?.textAlign === where)): 
-                                                         where === 'vcent' ?  BoolCast(Doc.UserDoc()._layout_centered):
-                                                                            (Doc.UserDoc().textAlign === where),
-                        toggle: () => { editorView?.state ? (where === 'vcent' ? RichTextMenu.Instance?.vcenterToggle():
-                                                                                 RichTextMenu.Instance?.align(editorView, editorView.dispatch, where)):
-                                                            where === 'vcent' ?  Doc.UserDoc()._layout_centered = !Doc.UserDoc()._layout_centered:
-                                                                                (Doc.UserDoc().textAlign = where); }
+        [ where, {      checkResult: () => (where === 'vcent' ?  RichTextMenu.Instance?.textVcenter ?? false:
+                                                             (RichTextMenu.Instance?.textAlign === where)),
+                        toggle: () => { (where === 'vcent' ? RichTextMenu.Instance?.vcenterToggle():
+                                                            RichTextMenu.Instance?.align(editorView, editorView?.dispatch, where)); }
         }]); // prettier-ignore
     // prettier-ignore
     const listings:attrfuncs[] = (['bullet','decimal'] as attrname[]).map(list =>
@@ -360,6 +352,8 @@ ScriptingGlobals.add(function toggleCharStyle(charStyle: attrname, checkResult?:
     const attrs:attrfuncs[] = [
         ['dictation', { checkResult: () => !!textView?._recordingDictation,
                         toggle: () => textView && runInAction(() => { textView._recordingDictation = !textView._recordingDictation;} ) }],
+        ['fitBox',    { checkResult: () => RichTextMenu.Instance?.fitBox ?? false,
+                        toggle: () => RichTextMenu.Instance?.toggleFitBox()}],
         ['elide',      { checkResult: () => false,
                         toggle: () => editorView ? RichTextMenu.Instance?.elideSelection(): 0}],
         ['noAutoLink',{ checkResult: () => ((editorView && RichTextMenu.Instance?.noAutoLink) ?? false),
diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx
index 16c016d6c..7c601185e 100644
--- a/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx
+++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx
@@ -245,7 +245,7 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> {
     }
 
     updateIcons = (docs: Doc[]) => {
-        console.log('called')
+        console.log('called');
         docs.map(this.getIcon);
     };
 
@@ -416,7 +416,7 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> {
                 const height = bottom - top;
                 const width = right - left;
                 const doc = !field.title.includes('$$')
-                    ? Docs.Create.TextDocument('', { _height: height, _width: width, title: field.title, x: left, y: top, _text_fontSize: `${height / 2}` })
+                    ? Docs.Create.TextDocument('', { _height: height, _width: width, title: field.title, x: left, y: top, text_fontSize: `${height / 2}` })
                     : Docs.Create.ImageDocument('', { _height: height, _width: width, title: field.title.replace(/\$\$/g, ''), x: left, y: top });
                 return doc;
             });
@@ -919,17 +919,17 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> {
     @action setExpandedView = (info: { icon: ImageField; doc: Doc } | undefined) => {
         if (info) {
             const doc = info.doc;
-            const wrapper: Doc = Docs.Create.FreeformDocument([info.doc], { _height: NumListCast(doc._height)[0], _width: NumListCast(doc._width)[0], title: ''});
-            const newInfo = {icon: new ImageField(''), doc: wrapper}
+            const wrapper: Doc = Docs.Create.FreeformDocument([info.doc], { _height: NumListCast(doc._height)[0], _width: NumListCast(doc._width)[0], title: '' });
+            const newInfo = { icon: new ImageField(''), doc: wrapper };
             this._expandedPreview = newInfo;
         } else {
             this._expandedPreview = info;
         }
     };
 
-    get editingWindow(){
+    get editingWindow() {
         const doc = this._expandedPreview?.doc ?? new Doc();
-        const rendered = 
+        const rendered = (
             <div className="docCreatorMenu-expanded-template-preview">
                 <CollectionFreeFormView
                     Document={this._expandedPreview!.doc}
@@ -945,7 +945,7 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> {
                     removeDocument={returnFalse}
                     PanelWidth={() => this._menuDimensions.width - 10}
                     PanelHeight={() => this._menuDimensions.height - 60}
-                    ScreenToLocalTransform={() => new Transform(-this._pageX,-this._pageY, 1)}
+                    ScreenToLocalTransform={() => new Transform(-this._pageX, -this._pageY, 1)}
                     renderDepth={5}
                     whenChildContentsActiveChanged={emptyFunction}
                     focus={emptyFunction}
@@ -961,14 +961,21 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> {
                     yPadding={0}
                 />
             </div>
-
+        );
 
         return (
             <div className="docCreatorMenu-expanded-template-preview">
-                <div className="top-panel"/>
+                <div className="top-panel" />
                 {rendered}
                 <div className="right-buttons-panel">
-                    <button className="docCreatorMenu-menu-button section-reveal-options top-right" onPointerDown={e => this.setUpButtonClick(e, () => {this._expandedPreview && this.updateIcons(this._suggestedTemplates.slice()); this.setExpandedView(undefined)})}>
+                    <button
+                        className="docCreatorMenu-menu-button section-reveal-options top-right"
+                        onPointerDown={e =>
+                            this.setUpButtonClick(e, () => {
+                                this._expandedPreview && this.updateIcons(this._suggestedTemplates.slice());
+                                this.setExpandedView(undefined);
+                            })
+                        }>
                         <FontAwesomeIcon icon="minimize" />
                     </button>
                     <button className="docCreatorMenu-menu-button section-reveal-options top-right-lower" onPointerDown={e => this.setUpButtonClick(e, () => this._expandedPreview && this._templateDocs.push(this._expandedPreview.doc))}>
@@ -976,7 +983,6 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> {
                     </button>
                 </div>
             </div>
-
         );
     }
 
@@ -2260,11 +2266,11 @@ export class FieldUtils {
             title: title,
             x: coord.x,
             y: coord.y,
-            _text_fontSize: `${FieldUtils.calculateFontSize(width, height, content, true)}`,
+            text_fontSize: `${FieldUtils.calculateFontSize(width, height, content, true)}`,
             backgroundColor: opts.backgroundColor ?? '',
             text_fontColor: opts.color,
             contentBold: opts.fontBold,
-            textTransform: opts.fontTransform,
+            text_transform: opts.fontTransform,
             color: opts.color,
             _layout_borderRounding: `${opts.cornerRounding ?? 0}px`,
             borderColor: opts.borderColor,
@@ -2306,7 +2312,7 @@ export class FieldUtils {
     public static CarouselField = (coords: { tl: [number, number]; br: [number, number] }, parentWidth: number, parentHeight: number, title: string, fields: Doc[]) => {
         const { width, height, coord } = FieldUtils.getDimensions(coords, parentWidth, parentHeight);
 
-        const doc = Docs.Create.Carousel3DDocument(fields, { _height: height, _width: width, title: title, x: coord.x, y: coord.y, _text_fontSize: `${height / 2}` });
+        const doc = Docs.Create.Carousel3DDocument(fields, { _height: height, _width: width, title: title, x: coord.x, y: coord.y, text_fontSize: `${height / 2}` });
 
         return doc;
     };
@@ -2359,4 +2365,3 @@ export class FieldUtils {
 //         }]
 //     };
 //  }
-
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index a343b9a39..eb7f333b9 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -880,7 +880,7 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document
                           background: this.backgroundBoxColor,
                           opacity: this.opacity,
                           cursor: Doc.ActiveTool === InkTool.None ? 'grab' : 'crosshair',
-                          color: StrCast(this.layoutDoc.color, 'inherit'),
+                          color: StrCast(this.Document._color, 'inherit'),
                           fontFamily: StrCast(this.Document._text_fontFamily, 'inherit'),
                           fontSize: Cast(this.Document._text_fontSize, 'string', null),
                           transform: this._animateScalingTo ? `scale(${this._animateScalingTo})` : undefined,
@@ -981,12 +981,10 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document
             type: SpringType.GENTLE,
             ...springMappings.gentle,
         };
-        switch (StrCast(presEffectDoc?.presentation_effect, StrCast(presEffectDoc?.followLinkAnimEffect))) {
-            case PresEffect.Expand:     return <SpringAnimation doc={root} startOpacity={0} dir={dir} presEffect={PresEffect.Expand} springSettings={timingConfig}>{renderDoc}</SpringAnimation>
-            case PresEffect.Flip:       return <SpringAnimation doc={root} startOpacity={0} dir={dir} presEffect={PresEffect.Flip}   springSettings={timingConfig}>{renderDoc}</SpringAnimation>
-            case PresEffect.Rotate:     return <SpringAnimation doc={root} startOpacity={0} dir={dir} presEffect={PresEffect.Rotate} springSettings={timingConfig}>{renderDoc}</SpringAnimation>
-            case PresEffect.Bounce:     return <SpringAnimation doc={root} startOpacity={0} dir={dir} presEffect={PresEffect.Bounce} springSettings={timingConfig}>{renderDoc}</SpringAnimation>
-            case PresEffect.Roll:       return <SpringAnimation doc={root} startOpacity={0} dir={dir} presEffect={PresEffect.Roll}   springSettings={timingConfig}>{renderDoc}</SpringAnimation>
+        const presEffect = StrCast(presEffectDoc?.presentation_effect, StrCast(presEffectDoc?.followLinkAnimEffect));
+        switch (presEffect) {
+            case PresEffect.Expand: case PresEffect.Flip: case PresEffect.Rotate: case PresEffect.Bounce:    
+            case PresEffect.Roll:       return <SpringAnimation doc={root} startOpacity={0} dir={dir} presEffect={presEffect}   springSettings={timingConfig}>{renderDoc}</SpringAnimation>
             // case PresEffect.Fade:    return <SlideEffect doc={root} dir={dir} presEffect={PresEffect.Fade} tension={timingConfig.stiffness} friction={timingConfig.damping} mass={timingConfig.mass}>{renderDoc}</SlideEffect>
             case PresEffect.Fade:       return <Fade {...effectProps}>{renderDoc}</Fade>
             // keep as preset, doesn't really make sense with spring config
diff --git a/src/client/views/nodes/EquationBox.tsx b/src/client/views/nodes/EquationBox.tsx
index fefe25764..290c90d6e 100644
--- a/src/client/views/nodes/EquationBox.tsx
+++ b/src/client/views/nodes/EquationBox.tsx
@@ -122,7 +122,7 @@ export class EquationBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     width: 'fit-content', // `${100 / scale}%`,
                     height: `${100 / scale}%`,
                     pointerEvents: !this._props.isSelected() ? 'none' : undefined,
-                    fontSize: StrCast(this.layoutDoc._text_fontSize),
+                    fontSize: StrCast(this.Document._text_fontSize),
                 }}
                 onKeyDown={e => e.stopPropagation()}>
                 <EquationEditor ref={this._ref} value={StrCast(this.dataDoc.text, 'x')} spaceBehavesLikeTab onChange={this.onChange} autoCommands="pi theta sqrt sum prod alpha beta gamma rho" autoOperatorNames="sin cos tan" />
diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.scss b/src/client/views/nodes/FontIconBox/FontIconBox.scss
index 2db285910..ab03a2318 100644
--- a/src/client/views/nodes/FontIconBox/FontIconBox.scss
+++ b/src/client/views/nodes/FontIconBox/FontIconBox.scss
@@ -10,6 +10,10 @@
         height: 3px !important;
     }
 }
+.fonticonbox {
+    margin: auto;
+    width: 100%;
+}
 .menuButton {
     height: 100%;
     display: flex;
diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
index d4898eb3c..b45774a75 100644
--- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
@@ -262,11 +262,13 @@ export class FontIconBox extends ViewBoxBaseComponent<ButtonProps>() {
 
         const script = ScriptCast(this.Document.onClick)?.script;
         const toggleStatus = script?.run({ this: this.Document, value: undefined, _readOnly_: true }).result as boolean;
+
         // Colors
         const color = this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Color) as string;
         const background = this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BackgroundColor) as string;
         const items = DocListCast(this.dataDoc.data);
         const selectedItems = items.filter(itemDoc => ScriptCast(itemDoc.onClick).script.run({ this: itemDoc, value: undefined, _readOnly_: true }).result).map(item => StrCast(item.toolType));
+
         return (
             <MultiToggle
                 tooltip={`Toggle ${tooltip}`}
@@ -392,7 +394,7 @@ export class FontIconBox extends ViewBoxBaseComponent<ButtonProps>() {
 
     render() {
         return (
-            <div style={{ margin: 'auto', width: '100%' }} onContextMenu={this.specificContextMenu}>
+            <div className="fonticonbox" onContextMenu={this.specificContextMenu}>
                 {this.renderButton()}
             </div>
         );
diff --git a/src/client/views/nodes/LabelBox.scss b/src/client/views/nodes/LabelBox.scss
index ca4b3d467..889cdc0ca 100644
--- a/src/client/views/nodes/LabelBox.scss
+++ b/src/client/views/nodes/LabelBox.scss
@@ -13,7 +13,6 @@
     height: 100%;
     border-radius: inherit;
     //letter-spacing: 2px; // bcz: doesn't work with LabelBigText
-    text-transform: uppercase;
     overflow: hidden;
     display: inline-block;
     margin: auto;
diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx
index 94a9541f2..dcf9e1fed 100644
--- a/src/client/views/nodes/LabelBox.tsx
+++ b/src/client/views/nodes/LabelBox.tsx
@@ -1,19 +1,22 @@
 import { Property } from 'csstype';
-import { action, computed, makeObservable } from 'mobx';
+import { action, computed, IReactionDisposer, makeObservable, observable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import * as textfit from 'textfit';
-import { Field, FieldType } from '../../../fields/Doc';
-import { BoolCast, NumCast, StrCast } from '../../../fields/Types';
+import { Doc, Field } from '../../../fields/Doc';
+import { NumCast, StrCast } from '../../../fields/Types';
 import { TraceMobx } from '../../../fields/util';
 import { DocumentType } from '../../documents/DocumentTypes';
 import { Docs } from '../../documents/Documents';
 import { DragManager } from '../../util/DragManager';
+import { undoable } from '../../util/UndoManager';
 import { ViewBoxBaseComponent } from '../DocComponent';
 import { PinDocView, PinProps } from '../PinFuncs';
 import { StyleProp } from '../StyleProp';
 import { FieldView, FieldViewProps } from './FieldView';
 import './LabelBox.scss';
+import { FormattedTextBox } from './formattedText/FormattedTextBox';
+import { RichTextMenu } from './formattedText/RichTextMenu';
 
 @observer
 export class LabelBox extends ViewBoxBaseComponent<FieldViewProps>() {
@@ -22,7 +25,8 @@ export class LabelBox extends ViewBoxBaseComponent<FieldViewProps>() {
     }
     private dropDisposer?: DragManager.DragDropDisposer;
     private _timeout: NodeJS.Timeout | undefined;
-    _divRef: HTMLDivElement | null = null;
+    private _divRef: HTMLDivElement | null = null;
+    private _reaction: IReactionDisposer | undefined;
 
     constructor(props: FieldViewProps) {
         super(props);
@@ -36,21 +40,29 @@ export class LabelBox extends ViewBoxBaseComponent<FieldViewProps>() {
         }
     };
 
-    @computed get Title() {
-        return Field.toString(this.dataDoc[this.fieldKey] as FieldType) || StrCast(this.Document.title);
-    }
-
-    @computed get backgroundColor() {
-        return this._props.styleProvider?.(this.Document, this._props, StyleProp.BackgroundColor) as string;
-    }
-
     componentDidMount() {
         this._props.setContentViewBox?.(this);
+        this._reaction = reaction(
+            () => this.Title,
+            () => document.activeElement !== this._divRef && this._forceRerender++
+        );
     }
     componentWillUnMount() {
         this._timeout && clearTimeout(this._timeout);
+        this.setText(this._divRef?.innerText ?? '');
+        this._reaction?.();
     }
 
+    @observable _forceRerender = 0;
+
+    @computed get Title()           { return Field.toString(this.dataDoc[this.fieldKey]); } // prettier-ignore
+    @computed get backgroundColor() { return this._props.styleProvider?.(this.Document, this._props, StyleProp.BackgroundColor) as string; } // prettier-ignore
+    @computed get boxShadow()       { return this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BoxShadow) as string; } // prettier-ignore
+
+    setText = undoable((text: string) => {
+        this.dataDoc[this.fieldKey] = text;
+    }, 'set label text');
+
     drop = (/* e: Event, de: DragManager.DropEvent */) => {
         return false;
     };
@@ -82,10 +94,11 @@ export class LabelBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const textfitParams = {
             minFontSize: NumCast(this.layoutDoc._label_minFontSize, 1),
             maxFontSize: NumCast(this.layoutDoc._label_maxFontSize, 100),
-            multiLine: BoolCast(this.layoutDoc._singleLine, true) ? false : true,
-            alignHoriz: true,
+            multiLine: r?.textContent?.includes('\n') ? true : false,
+            // hack because tetFit doesn't support align 'right', but we need mobx to invalidate, so treat null as false and set to right inline
+            alignHoriz: StrCast(this.layoutDoc[this.fieldKey + '_align']) === 'center' ? true : StrCast(this.layoutDoc[this.fieldKey + '_align']) === 'right' ? (null as unknown as boolean) : false,
             alignVert: true,
-            detectMultiLine: true,
+            detectMultiLine: false,
         };
         if (r) {
             if (!r.offsetHeight || !r.offsetWidth) {
@@ -94,65 +107,140 @@ export class LabelBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 this._timeout = setTimeout(() => this.fitTextToBox(r));
                 return textfitParams;
             }
+            r.style.whiteSpace = ''; // textfit sets to nowrap if not multiline, but doesn't reeset if it becomes multiline
+            r.style.textAlign = StrCast(this.layoutDoc[this.fieldKey + '_align']); // textfit doesn't reset textAlign if it has been set to center, so we just set it to what we want
+            r.firstChild instanceof HTMLElement && (r.firstChild.style.textAlign = StrCast(this.layoutDoc[this.fieldKey + '_align']));
             textfit(r, textfitParams);
         }
         return textfitParams;
     };
+    resetCursor = (cranchor?: number) => {
+        if (this._divRef && (cranchor || this._divRef === document.activeElement)) {
+            const range = document.createRange();
+            const anchor = cranchor ?? this._divRef.childNodes.length;
+            const container = cranchor === undefined ? this._divRef : (this._divRef.firstChild?.firstChild ?? this._divRef);
+            range.setStart(container, anchor);
+            range.setEnd(container, anchor);
+            const sel = window.getSelection();
+            sel?.removeAllRanges();
+            sel?.addRange(range);
+        }
+    };
+
+    beforeInput = action((event: InputEvent) => {
+        const spanChild = this._divRef?.firstChild?.firstChild;
+        if (spanChild?.nodeName === '#text' && ['insertLineBreak', 'insertParagraph'].includes(event.inputType)) {
+            event.preventDefault();
+            event.stopPropagation();
+
+            const selection = document.getSelection();
+            if (selection && document.activeElement === event.target) {
+                const text = spanChild.textContent ?? '';
+                const cranchor = selection.anchorNode === this._divRef ? (selection.anchorOffset ? text.length : 0) : selection.anchorOffset;
+                const addReturnHack = text.length <= cranchor && text[text.length - 1] !== '\n' ? '\n\n' : '\n'; // not sure why, but need to add a second carriage return if typing enter at the end of the text
+                const splitText = text.substring(0, cranchor) + addReturnHack + text.substring(cranchor);
+                spanChild.textContent = splitText;
+                this.resetCursor(cranchor + addReturnHack.length);
+            }
+            // const span = document.createElement('span');
+            // span.innerHTML = '&#8203;';
+            // this._divRef!.append(span);
+        }
+    });
+    // .labelBox-mainButton > div > span:nth-child(2) {
+
+    /**
+     * When an IconButton is clicked, it will receive focus.  However, we don't want that since we want or need that since we really want
+     * to maintain focus in the label's editing div (and cursor position).  so this relies on IconButton's having a tabindex set to -1 so that
+     * we can march up the tree from the 'relatedTarget' to determine if the loss of focus was caused by a fonticonbox.   If it is, we then
+     * restore focus
+     * @param e focusout event on the editing div
+     */
+    keepFocus = (e: FocusEvent) => {
+        if (e.relatedTarget instanceof HTMLElement && e.relatedTarget.tabIndex === -1) {
+            for (let ele: HTMLElement | null = e.relatedTarget; ele; ele = (ele as HTMLElement)?.parentElement) {
+                if ((ele as HTMLElement)?.className === 'fonticonbox') {
+                    setTimeout(() => this._divRef?.focus());
+                    break;
+                }
+            }
+        }
+    };
+
     render() {
         TraceMobx();
         const boxParams = this.fitTextToBox(undefined); // this causes mobx to trigger re-render when data changes
-        const label = this.Title.startsWith('#') ? null : this.Title;
         return (
-            <div key={label?.length} className="labelBox-outerDiv" ref={this.createDropTarget} style={{ boxShadow: this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BoxShadow) as string }}>
+            <div className="labelBox-outerDiv" ref={this.createDropTarget} style={{ boxShadow: this.boxShadow }}>
                 <div
                     className="labelBox-mainButton"
                     style={{
                         backgroundColor: this.backgroundColor,
-                        // fontSize: StrCast(this.layoutDoc._text_fontSize),
-                        color: StrCast(this.layoutDoc._color),
-                        fontFamily: StrCast(this.layoutDoc._text_fontFamily) || 'inherit',
+                        color: StrCast(this.layoutDoc._text_fontColor, StrCast(this.layoutDoc._color)),
+                        fontFamily: StrCast(this.layoutDoc._text_fontFamily, StrCast(Doc.UserDoc().fontFamily)) || 'inherit',
                         letterSpacing: StrCast(this.layoutDoc.letterSpacing),
-                        textTransform: StrCast(this.layoutDoc.textTransform) as Property.TextTransform,
+                        textTransform: StrCast(this.layoutDoc[this.fieldKey + '_transform']) as Property.TextTransform,
                         paddingLeft: NumCast(this.layoutDoc._xPadding),
                         paddingRight: NumCast(this.layoutDoc._xPadding),
                         paddingTop: NumCast(this.layoutDoc._yPadding),
                         paddingBottom: NumCast(this.layoutDoc._yPadding),
                         width: this._props.PanelWidth(),
                         height: this._props.PanelHeight(),
-                        whiteSpace: 'multiLine' in boxParams && boxParams.multiLine ? 'pre-wrap' : 'pre',
+                        whiteSpace: boxParams.multiLine ? 'pre-wrap' : 'pre',
                     }}>
                     <div
+                        key={this._forceRerender}
                         style={{
                             width: this._props.PanelWidth() - 2 * NumCast(this.layoutDoc._xPadding),
                             height: this._props.PanelHeight() - 2 * NumCast(this.layoutDoc._yPadding),
                             outline: 'unset !important',
                         }}
-                        onKeyDown={action(e => {
+                        onKeyDown={e => {
                             e.stopPropagation();
-                        })}
+                        }}
                         onKeyUp={action(e => {
                             e.stopPropagation();
-                            this.dataDoc[this.fieldKey] = this._divRef?.innerText ?? '';
-                            setTimeout(() => this._props.select(false));
+                            const text = this._divRef?.firstChild;
+                            if (text && (text as HTMLElement)?.nodeType === 3) {
+                                this._divRef?.removeChild(text);
+                                this._divRef?.firstChild?.appendChild(text);
+                                this.resetCursor();
+                            }
+                            this.fitTextToBox(this._divRef);
                         })}
+                        onFocus={() => {
+                            RichTextMenu.Instance?.updateMenu(undefined, undefined, undefined, this.dataDoc);
+                            this._divRef?.removeEventListener('focusout', this.keepFocus);
+                            this._divRef?.addEventListener('focusout', this.keepFocus);
+                        }}
                         onBlur={() => {
-                            this.dataDoc[this.fieldKey] = this._divRef?.innerText ?? '';
+                            this._divRef?.removeEventListener('focusout', this.keepFocus);
+                            this.setText(this._divRef?.innerText ?? '');
+                            RichTextMenu.Instance?.updateMenu(undefined, undefined, undefined, undefined);
+                            FormattedTextBox.LiveTextUndo?.end();
+                            FormattedTextBox.LiveTextUndo = undefined;
+                        }}
+                        dangerouslySetInnerHTML={{
+                            __html: `<span class="textFitted textFitAlignVert" style="display: inline-block; text-align: center; font-size: 100px; height: 0px;">${this.Title.startsWith('#') ? null : (this.Title ?? '')}</span>`,
                         }}
                         contentEditable={this._props.onClickScript?.() ? undefined : true}
                         ref={r => {
+                            this._divRef?.removeEventListener('beforeinput', this.beforeInput);
                             this._divRef = r;
-                            this.fitTextToBox(r);
-                            if (this._props.isSelected() && this._divRef) {
-                                const range = document.createRange();
-                                range.setStart(this._divRef, this._divRef.childNodes.length);
-                                range.setEnd(this._divRef, this._divRef.childNodes.length);
-                                const sel = window.getSelection();
-                                sel?.removeAllRanges();
-                                sel?.addRange(range);
+                            if (this._divRef) {
+                                this._divRef.addEventListener('beforeinput', this.beforeInput);
+
+                                if (Doc.SelectOnLoad === this.Document) {
+                                    Doc.SelectOnLoad = undefined;
+                                    this._divRef.focus();
+                                }
+                                this.fitTextToBox(this._divRef);
+                                if (this.Title) {
+                                    this.resetCursor();
+                                }
                             }
-                        }}>
-                        {label}
-                    </div>
+                        }}
+                    />
                 </div>
             </div>
         );
@@ -161,9 +249,9 @@ export class LabelBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
 Docs.Prototypes.TemplateMap.set(DocumentType.LABEL, {
     layout: { view: LabelBox, dataField: 'title' },
-    options: { acl: '', _singleLine: true, _layout_nativeDimEditable: true, _layout_reflowHorizontal: true, _layout_reflowVertical: true },
+    options: { acl: '', _layout_nativeDimEditable: true, _layout_reflowHorizontal: true, _layout_reflowVertical: true, title_align: 'center', title_transform: 'uppercase' },
 });
 Docs.Prototypes.TemplateMap.set(DocumentType.BUTTON, {
     layout: { view: LabelBox, dataField: 'title' },
-    options: { acl: '', _layout_nativeDimEditable: true, _layout_reflowHorizontal: true, _layout_reflowVertical: true },
+    options: { acl: '', _layout_nativeDimEditable: true, _layout_reflowHorizontal: true, _layout_reflowVertical: true, title_align: 'center', title_transform: 'uppercase' },
 });
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 2aee7b6b3..659cda9dd 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -65,6 +65,7 @@ import { RichTextMenu, RichTextMenuPlugin } from './RichTextMenu';
 import { RichTextRules } from './RichTextRules';
 import { schema } from './schema_rts';
 import { Property } from 'csstype';
+import { LabelBox } from '../LabelBox';
 // import * as applyDevTools from 'prosemirror-dev-tools';
 
 export interface FormattedTextBoxProps extends FieldViewProps {
@@ -158,6 +159,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
     @computed get titleHeight()                { return this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.HeaderMargin) as number || 0; } // prettier-ignore
     @computed get layout_autoHeightMargins()   { return this.titleHeight + NumCast(this.layoutDoc._layout_autoHeightMargins); } // prettier-ignore
     @computed get sidebarKey()                 { return this.fieldKey + '_sidebar'; } // prettier-ignore
+    @computed get isLabel()                    { return this.dataDoc[this.fieldKey+"_fitBox"]; } // prettier-ignore
 
     constructor(props: FormattedTextBoxProps) {
         super(props);
@@ -165,7 +167,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
         this._recordingStart = Date.now();
     }
 
-    public get EditorView() { return this._editorView; } // prettier-ignore
+    public get EditorView() { return this.isLabel ? undefined : this._editorView; } // prettier-ignore
 
     // public makeAIFlashcards: () => void = unimplementedFunction;
     public addToCollection: ((doc: Doc | Doc[], annotationKey?: string | undefined) => boolean) | undefined;
@@ -175,10 +177,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
     // but since removing one anchor from the list of attr anchors isn't implemented, this will end up removing nothing.
     public RemoveLinkFromDoc(linkDoc?: Doc) {
         this.unhighlightSearchTerms();
-        const state = this._editorView?.state;
+        const state = this.EditorView?.state;
         const a1 = DocCast(linkDoc?.link_anchor_1);
         const a2 = DocCast(linkDoc?.link_anchor_2);
-        if (state && a1 && a2 && this._editorView) {
+        if (state && a1 && a2 && this.EditorView) {
             this.removeDocument(a1);
             this.removeDocument(a2);
             let allFoundLinkAnchors: { href: string; title: string; anchorId: string }[] = [];
@@ -188,7 +190,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
                 return true;
             });
             if (allFoundLinkAnchors.length) {
-                this._editorView.dispatch(removeMarkWithAttrs(state.tr, 0, state.doc.nodeSize - 2, state.schema.marks.linkAnchor, { allAnchors: allFoundLinkAnchors }));
+                this.EditorView.dispatch(removeMarkWithAttrs(state.tr, 0, state.doc.nodeSize - 2, state.schema.marks.linkAnchor, { allAnchors: allFoundLinkAnchors }));
 
                 this.setupEditor(this.config, this.fieldKey);
             }
@@ -197,16 +199,16 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
     // removes all the specified link references from the selection.
     // NOTE: as above, this won't work correctly if there are marks with overlapping but not exact sets of link references.
     public RemoveAnchorFromSelection(allAnchors: { href: string; title: string; linkId: string; targetId: string }[]) {
-        const state = this._editorView?.state;
-        if (state && this._editorView) {
-            this._editorView.dispatch(removeMarkWithAttrs(state.tr, state.selection.from, state.selection.to, state.schema.marks.link, { allAnchors }));
+        const state = this.EditorView?.state;
+        if (state && this.EditorView) {
+            this.EditorView.dispatch(removeMarkWithAttrs(state.tr, state.selection.from, state.selection.to, state.schema.marks.link, { allAnchors }));
             this.setupEditor(this.config, this.fieldKey);
         }
     }
 
     getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => {
         const rootDoc: Doc = Doc.isTemplateDoc(this._props.docViewPath().lastElement()?.Document) ? this.Document : DocCast(this.Document.rootDocument, this.Document);
-        if (!pinProps && this._editorView?.state.selection.empty) return rootDoc;
+        if (!pinProps && this.EditorView?.state.selection.empty) return rootDoc;
         const anchor = Docs.Create.ConfigDocument({ title: StrCast(rootDoc.title), annotationOn: rootDoc });
         this.addDocument(anchor);
         this._finishingLink = true;
@@ -263,7 +265,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
                 }
             });
         };
-        AnchorMenu.Instance.Highlight = undoable((color: string) => this._editorView?.state && RichTextMenu.Instance?.setFontField(color, 'fontHighlight'), 'highlght text');
+        AnchorMenu.Instance.Highlight = undoable((color: string) => this.EditorView?.state && RichTextMenu.Instance?.setFontField(color, 'fontHighlight'), 'highlght text');
         AnchorMenu.Instance.onMakeAnchor = () => this.getAnchor(true);
         AnchorMenu.Instance.StartCropDrag = unimplementedFunction;
         /**
@@ -292,7 +294,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
         };
 
         AnchorMenu.Instance.setSelectedText(window.getSelection()?.toString() ?? '');
-        const coordsB = this._editorView!.coordsAtPos(this._editorView!.state.selection.to);
+        const coordsB = this.EditorView!.coordsAtPos(this.EditorView!.state.selection.to);
         this._props.rootSelected?.() && AnchorMenu.Instance.jumpTo(coordsB.left, coordsB.bottom);
         let ele: Opt<HTMLDivElement>;
         try {
@@ -309,7 +311,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
     };
 
     leafText = (node: Node) => {
-        if (node.type === this._editorView?.state.schema.nodes.dashField) {
+        if (node.type === this.EditorView?.state.schema.nodes.dashField) {
             const refDoc = !node.attrs.docId ? DocCast(this.Document.rootDocument, this.Document) : (DocServer.GetCachedRefField(node.attrs.docId as string) as Doc);
             const fieldKey = StrCast(node.attrs.fieldKey);
             return (
@@ -320,16 +322,16 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
         return '';
     };
     dispatchTransaction = (tx: Transaction) => {
-        if (this._editorView && !this._editorView.isDestroyed) {
-            const state = this._editorView.state.apply(tx);
-            this._editorView.updateState(state);
+        if (this.EditorView && !this.EditorView.isDestroyed) {
+            const state = this.EditorView.state.apply(tx);
+            this.EditorView.updateState(state);
             this.tryUpdateDoc(false);
         }
     };
 
     tryUpdateDoc = (force: boolean) => {
-        if (this._editorView) {
-            const { state } = this._editorView;
+        if (this.EditorView) {
+            const { state } = this.EditorView;
             const { dataDoc } = this;
             const newText = state.doc.textBetween(0, state.doc.content.size, ' \n', this.leafText);
             const newJson = JSON.stringify(state.toJSON());
@@ -372,7 +374,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
                         textChange && (dataDoc[this.fieldKey + '_modificationDate'] = new DateField(new Date(Date.now())));
                         // if we've deleted all the text in a note driven by a template, then restore the template data
                         dataDoc[this.fieldKey] = undefined;
-                        this._editorView.updateState(EditorState.fromJSON(this.config, JSON.parse(rtField.Data)));
+                        this.EditorView.updateState(EditorState.fromJSON(this.config, JSON.parse(rtField.Data)));
                         ScriptCast(this.layoutDoc.onTextChanged, null)?.script.run({ this: this.layoutDoc, text: newText });
                         unchanged = false;
                     }
@@ -387,7 +389,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
                 if (jsonstring) {
                     const json = JSON.parse(jsonstring);
                     json.selection = state.toJSON().selection;
-                    this._editorView.updateState(EditorState.fromJSON(this.config, json));
+                    this.EditorView.updateState(EditorState.fromJSON(this.config, json));
                 }
             }
             if (window.getSelection()?.isCollapsed && this._props.rootSelected?.()) {
@@ -407,8 +409,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
                 linkAnchor = anchor;
             }
         });
-        if (this._editorView && linkTime) {
-            const { state } = this._editorView;
+        if (this.EditorView && linkTime) {
+            const { state } = this.EditorView;
             const node = state.selection.$from.node();
             if (linkAnchor && node.type !== state.schema.nodes.code_block) {
                 const time = linkTime + Date.now() / 1000 - this._recordingStart / 1000;
@@ -416,7 +418,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
                 const { from } = state.selection;
                 const value = state.schema.nodes.audiotag.create({ timeCode: time, audioId: linkAnchor[Id] });
                 const replaced = state.tr.insert(from - 1, value);
-                this._editorView.dispatch(replaced.setSelection(new TextSelection(replaced.doc.resolve(from + 1))));
+                this.EditorView.dispatch(replaced.setSelection(new TextSelection(replaced.doc.resolve(from + 1))));
             }
         }
     };
@@ -431,16 +433,16 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
                  (Doc.isTemplateForField(this.Document) && (link.link_anchor_1 === this.Document || link.link_anchor_2 === this.Document))) &&
                 link.link_relationship === LinkManager.AutoKeywords
         ); // prettier-ignore
-        if (this._editorView?.state.doc.textContent) {
-            let { tr } = this._editorView.state;
-            const { from, to } = this._editorView.state.selection;
-            const { autoLinkAnchor } = this._editorView.state.schema.marks;
+        if (this.EditorView?.state.doc.textContent) {
+            let { tr } = this.EditorView.state;
+            const { from, to } = this.EditorView.state.selection;
+            const { autoLinkAnchor } = this.EditorView.state.schema.marks;
             tr = tr.removeMark(0, tr.doc.content.size, autoLinkAnchor);
             Doc.MyPublishedDocs.filter(term => term.title).forEach(term => {
                 tr = this.hyperlinkTerm(tr, term, newAutoLinks);
             });
             tr = tr.setSelection(new TextSelection(tr.doc.resolve(from), tr.doc.resolve(to)));
-            this._editorView?.dispatch(tr);
+            this.EditorView?.dispatch(tr);
         }
         oldAutoLinks.filter(oldLink => !newAutoLinks.has(oldLink) && oldLink.link_anchor_2 !== this.Document).forEach(doc => Doc.DeleteLink?.(doc));
     };
@@ -450,11 +452,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
         if (
             !this._props.dontRegisterView && // (this.Document.isTemplateForField === "text" || !this.Document.isTemplateForField) && // only update the title if the data document's data field is changing
             title.startsWith('-') &&
-            this._editorView &&
+            this.EditorView &&
             !this.dataDoc.title_custom &&
             (Doc.LayoutFieldKey(this.Document) === this.fieldKey || this.fieldKey === 'text')
         ) {
-            let node = this._editorView.state.doc;
+            let node = this.EditorView.state.doc;
             while (node.firstChild && node.firstChild.type.name !== 'text') node = node.firstChild;
             const str = node.textContent;
             const prefix = '-';
@@ -477,7 +479,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
      */
     hyperlinkTerm = (trIn: Transaction, target: Doc, newAutoLinks: Set<Doc>) => {
         let tr = trIn;
-        const editorView = this._editorView;
+        const editorView = this.EditorView;
         if (editorView && !Doc.AreProtosEqual(target, this.Document)) {
             const autoLinkTerm = Field.toString(target.title as FieldType).replace(/^@/, '');
             let alink: Doc | undefined;
@@ -553,18 +555,18 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
     };
 
     unhighlightSearchTerms = () => {
-        if (this._editorView) {
-            const { state } = this._editorView;
+        if (this.EditorView) {
+            const { state } = this.EditorView;
             if (state) {
                 const mark = state.schema.mark(state.schema.marks.search_highlight);
                 const activeMark = state.schema.mark(state.schema.marks.search_highlight, { selected: true });
                 const end = state.doc.nodeSize - 2;
-                this._editorView.dispatch(state.tr.removeMark(0, end, mark).removeMark(0, end, activeMark));
+                this.EditorView.dispatch(state.tr.removeMark(0, end, mark).removeMark(0, end, activeMark));
             }
         }
     };
     adoptAnnotation = (start: number, end: number, mark: Mark) => {
-        const view = this._editorView!;
+        const view = this.EditorView!;
         const nmark = view.state.schema.marks.user_mark.create({ ...mark.attrs, userid: ClientUtils.CurrentUserEmail() });
         view.dispatch(view.state.tr.removeMark(start, end, nmark).addMark(start, end, nmark));
     };
@@ -616,7 +618,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
                     if (added) {
                         draggedDoc._freeform_fitContentsToBox = true;
                         Doc.SetContainer(draggedDoc, this.Document);
-                        const view = this._editorView!;
+                        const view = this.EditorView!;
                         try {
                             this._inDrop = true;
                             const pos = view.posAtCoords({ left: de.x, top: de.y })?.pos;
@@ -810,7 +812,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
 
         let target: Element | HTMLElement | null = e.target as HTMLElement; // hrefs are stored on the database of the <a> node that wraps the hyerlink <span>
         while (target && (!(target instanceof HTMLElement) || !target.dataset?.targethrefs)) target = target.parentElement;
-        const editor = this._editorView;
+        const editor = this.EditorView;
         if (editor && target && !(e.nativeEvent instanceof simMouseEvent ? e.nativeEvent.dash : false)) {
             const hrefs = (target.dataset?.targethrefs as string)
                 ?.trim()
@@ -996,8 +998,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
         const c = this.ProseRef?.getElementsByTagName('img');
         if (c) {
             for (const i of c) {
-                console.log(i);
-
                 // console.log(canvas.toDataURL());
                 // canvas.style.zIndex = '2000000';
                 // document.body.appendChild(canvas);
@@ -1022,8 +1022,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
 
     animateRes = (resIndex: number, newText: string) => {
         if (resIndex < newText.length) {
-            const marks = this._editorView?.state.storedMarks ?? [];
-            this._editorView?.dispatch(this._editorView?.state.tr.insertText(newText[resIndex]).setStoredMarks(marks));
+            const marks = this.EditorView?.state.storedMarks ?? [];
+            this.EditorView?.dispatch(this.EditorView?.state.tr.insertText(newText[resIndex]).setStoredMarks(marks));
             setTimeout(() => this.animateRes(resIndex + 1, newText), 20);
         }
     };
@@ -1035,8 +1035,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
             const res = await gptAPICall((this.dataDoc.text as RichTextField)?.Text, GPTCallType.COMPLETION);
             if (!res) {
                 this.animateRes(0, 'Something went wrong.');
-            } else if (this._editorView) {
-                const { dispatch, state } = this._editorView;
+            } else if (this.EditorView) {
+                const { dispatch, state } = this.EditorView;
                 // for no animation, use: dispatch(state.tr.insertText(res));
                 // for animted response starting at end of text, use:
                 dispatch(state.tr.setSelection(Selection.atEnd(state.doc)));
@@ -1057,13 +1057,13 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
     };
 
     breakupDictation = () => {
-        if (this._editorView && this._recordingDictation) {
+        if (this.EditorView && this._recordingDictation) {
             this.stopDictation(/* true */);
             this._break = true;
-            const { state } = this._editorView;
+            const { state } = this.EditorView;
             const { to } = state.selection;
             const updated = TextSelection.create(state.doc, to, to);
-            this._editorView.dispatch(state.tr.setSelection(updated).insert(to, state.schema.nodes.paragraph.create({})));
+            this.EditorView.dispatch(state.tr.setSelection(updated).insert(to, state.schema.nodes.paragraph.create({})));
             if (this._recordingDictation) {
                 this.recordDictation();
             }
@@ -1082,7 +1082,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
     stopDictation = (/* abort: boolean */) => DictationManager.Controls.stop(/* !abort */);
 
     setDictationContent = (value: string) => {
-        if (this._editorView && this._recordingStart) {
+        if (this.EditorView && this._recordingStart) {
             if (this._break) {
                 const textanchorFunc = () => {
                     const tanch = Docs.Create.ConfigDocument({ title: 'dictation anchor' });
@@ -1095,22 +1095,22 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
                     const textanchor = Cast(link.link_anchor_1, Doc, null);
                     if (audioanchor) {
                         audioanchor.backgroundColor = 'tan';
-                        const audiotag = this._editorView.state.schema.nodes.audiotag.create({
+                        const audiotag = this.EditorView.state.schema.nodes.audiotag.create({
                             timeCode: NumCast(audioanchor._timecodeToShow),
                             audioId: audioanchor[Id],
                             textId: textanchor[Id],
                         });
                         textanchor[DocData].title = 'dictation:' + audiotag.attrs.timeCode;
-                        const tr = this._editorView.state.tr.insert(this._editorView.state.doc.content.size, audiotag);
+                        const tr = this.EditorView.state.tr.insert(this.EditorView.state.doc.content.size, audiotag);
                         const tr2 = tr.setSelection(TextSelection.create(tr.doc, tr.doc.content.size));
-                        this._editorView.dispatch(tr.setSelection(TextSelection.create(tr2.doc, tr2.doc.content.size)));
+                        this.EditorView.dispatch(tr.setSelection(TextSelection.create(tr2.doc, tr2.doc.content.size)));
                     }
                 }
             }
-            const { from } = this._editorView.state.selection;
+            const { from } = this.EditorView.state.selection;
             this._break = false;
-            const tr = this._editorView.state.tr.insertText(value);
-            this._editorView.dispatch(tr.setSelection(TextSelection.create(tr.doc, from, tr.doc.content.size)).scrollIntoView());
+            const tr = this.EditorView.state.tr.insertText(value);
+            this.EditorView.dispatch(tr.setSelection(TextSelection.create(tr.doc, from, tr.doc.content.size)).scrollIntoView());
         }
     };
 
@@ -1143,7 +1143,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
                     }
                 });
                 this.dataDoc[ForceServerWrite] = this.dataDoc[UpdatingFromServer] = true; // need to allow permissions for adding links to readonly/augment only documents
-                this._editorView!.dispatch(tr.removeMark(selection.from, selection.to, splitter));
+                this.EditorView!.dispatch(tr.removeMark(selection.from, selection.to, splitter));
                 this.dataDoc[UpdatingFromServer] = this.dataDoc[ForceServerWrite] = false;
                 anchor.text = selectedText;
                 anchor.text_html = this._selectionHTML ?? selectedText;
@@ -1177,7 +1177,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
             let hadStart = start !== 0;
             frag.forEach((node, index) => {
                 const examinedNode = findAnchorNode(node, editor);
-                if (examinedNode?.node && (examinedNode.node.textContent || examinedNode.node.type === this._editorView?.state.schema.nodes.dashDoc || examinedNode.node.type === this._editorView?.state.schema.nodes.audiotag)) {
+                if (examinedNode?.node && (examinedNode.node.textContent || examinedNode.node.type === this.EditorView?.state.schema.nodes.dashDoc || examinedNode.node.type === this.EditorView?.state.schema.nodes.audiotag)) {
                     nodes.push(examinedNode.node);
                     !hadStart && (start = index + examinedNode.start);
                     hadStart = true;
@@ -1186,13 +1186,13 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
             return { frag: Fragment.fromArray(nodes), start };
         };
         const findAnchorNode = (node: Node, editor: EditorView) => {
-            if (node.type === this._editorView?.state.schema.nodes.audiotag) {
+            if (node.type === this.EditorView?.state.schema.nodes.audiotag) {
                 if (node.attrs.textId === textAnchorId) {
                     return { node, start: 0 };
                 }
                 return undefined;
             }
-            if (node.type === this._editorView?.state.schema.nodes.dashDoc) {
+            if (node.type === this.EditorView?.state.schema.nodes.dashDoc) {
                 if (node.attrs.docId === textAnchorId) {
                     return { node, start: 0 };
                 }
@@ -1208,9 +1208,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
         };
 
         this._didScroll = false; // assume we don't need to scroll.  if we do, this will get set to true in handleScrollToSelextion when we dispatch the setSelection below
-        if (this._editorView && textAnchorId) {
-            const { state } = this._editorView;
-            const ret = findAnchorFrag(state.doc.content, this._editorView);
+        if (this.EditorView && textAnchorId) {
+            const { state } = this.EditorView;
+            const ret = findAnchorFrag(state.doc.content, this.EditorView);
 
             const firstChild = ret.frag.childCount ? ret.frag.child(0) : undefined;
             if (ret.start >= 0 && (ret.frag.size || (firstChild && [state.schema.nodes.dashDoc, state.schema.nodes.audioTag].includes(firstChild.type)))) {
@@ -1219,7 +1219,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
                 if (ret.frag.firstChild) {
                     selection = TextSelection.between(state.doc.resolve(ret.start), state.doc.resolve(ret.start + ret.frag.firstChild.nodeSize)); // bcz: looks better to not have the target selected
                 }
-                this._editorView.dispatch(state.tr.setSelection(new TextSelection(selection.$from, selection.$from)).scrollIntoView());
+                this.EditorView.dispatch(state.tr.setSelection(new TextSelection(selection.$from, selection.$from)).scrollIntoView());
                 const escAnchorId = textAnchorId[0] >= '0' && textAnchorId[0] <= '9' ? `\\3${textAnchorId[0]} ${textAnchorId.substr(1)}` : textAnchorId;
                 addStyleSheetRule(FormattedTextBox._highlightStyleSheet, `${escAnchorId}`, { background: 'yellow', transform: 'scale(3)', 'transform-origin': 'left bottom' });
                 setTimeout(() => {
@@ -1307,15 +1307,15 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
                 return !whichData ? undefined : { data: RTFCast(whichData), str: Field.toString(DocCast(whichData) ?? StrCast(whichData)) };
             },
             incomingValue => {
-                if (this._editorView && this._applyingChange !== this.fieldKey) {
+                if (this.EditorView && this._applyingChange !== this.fieldKey) {
                     if (incomingValue?.data) {
                         const updatedState = JSON.parse(incomingValue.data.Data);
-                        if (JSON.stringify(this._editorView.state.toJSON()) !== JSON.stringify(updatedState)) {
-                            this._editorView.updateState(EditorState.fromJSON(this.config, updatedState));
+                        if (JSON.stringify(this.EditorView.state.toJSON()) !== JSON.stringify(updatedState)) {
+                            this.EditorView.updateState(EditorState.fromJSON(this.config, updatedState));
                             this.tryUpdateScrollHeight();
                         }
-                    } else if (this._editorView.state.doc.textContent !== incomingValue?.str) {
-                        selectAll(this._editorView.state, tx => this._editorView?.dispatch(tx.insertText(incomingValue?.str ?? '')));
+                    } else if (this.EditorView.state.doc.textContent !== (incomingValue?.str ?? '')) {
+                        selectAll(this.EditorView.state, tx => this.EditorView?.dispatch(tx.insertText(incomingValue?.str ?? '')));
                     }
                 }
             },
@@ -1333,9 +1333,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
             action(selected => {
                 if (selected && this.dataDoc[this.fieldKey + '_placeholder']) {
                     setTimeout(() => {
-                        selectAll(this._editorView!.state, (tx: Transaction) => {
-                            this._editorView?.dispatch(tx);
-                            this._editorView!.focus();
+                        selectAll(this.EditorView!.state, (tx: Transaction) => {
+                            this.EditorView?.dispatch(tx);
+                            this.EditorView!.focus();
                         });
                     });
                 }
@@ -1343,12 +1343,12 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
                 if (FormattedTextBox._globalHighlights.has('Bold Text')) {
                     this.layoutDoc[DocCss] = this.layoutDoc[DocCss] + 1; // css change happens outside of mobx/react, so this will notify anyone interested in the layout that it has changed
                 }
-                if (RichTextMenu.Instance?.view === this._editorView && !selected) {
+                if (((RichTextMenu.Instance?.view === this.EditorView && this.EditorView) || this.isLabel) && !selected) {
                     RichTextMenu.Instance?.updateMenu(undefined, undefined, undefined, undefined);
                 }
-                if (this._editorView && selected) {
-                    RichTextMenu.Instance?.updateMenu(this._editorView, undefined, this._props, this.layoutDoc);
-                    setTimeout(this.autoLink, 20);
+                if (selected) {
+                    RichTextMenu.Instance?.updateMenu(this.EditorView, undefined, this._props, this.dataDoc);
+                    this.EditorView && setTimeout(this.autoLink, 20);
                 }
             }),
             { fireImmediately: true }
@@ -1389,7 +1389,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
 
             // DocCast(this.Document.image)._freeform_fitContentsToBox = true;
             // Doc.SetContainer(DocCast(this.Document.image), this.Document);
-            // const view = this._editorView!;
+            // const view = this.EditorView!;
             // try {
             //     this._inDrop = true;
             //     const pos = view.posAtCoords({ left: 0, top: 0 })?.pos;
@@ -1436,7 +1436,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
     };
 
     addPdfReference = (pdfAnchorId: string) => {
-        const view = this._editorView!;
+        const view = this.EditorView!;
         if (pdfAnchorId) {
             DocServer.GetRefField(pdfAnchorId).then(pdfAnchor => {
                 if (pdfAnchor instanceof Doc) {
@@ -1487,7 +1487,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
         const curText = Cast(this.dataDoc[this.fieldKey], RichTextField, null) || StrCast(this.dataDoc[this.fieldKey]);
         const rtfField = Cast((!curText && this.layoutDoc[this.fieldKey]) || this.dataDoc[fieldKey], RichTextField);
         if (this.ProseRef) {
-            this._editorView?.destroy();
+            this.EditorView?.destroy();
             this._editorView = new EditorView(this.ProseRef, {
                 state: rtfField?.Data ? EditorState.fromJSON(config, JSON.parse(rtfField.Data)) : EditorState.create(config),
                 handleScrollToSelection: editorView => {
@@ -1519,14 +1519,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
             if (!rtfField) {
                 const dataDoc = Doc.IsDelegateField(DocCast(this.layoutDoc.proto), this.fieldKey) ? DocCast(this.layoutDoc.proto) : this.dataDoc;
                 const startupText = Field.toString(dataDoc[fieldKey] as FieldType);
-                const textAlign = StrCast(this.dataDoc.text_align, StrCast(Doc.UserDoc().textAlign)) || 'left';
+                const textAlign = StrCast(this.dataDoc[this.fieldKey + '_align'], StrCast(Doc.UserDoc().textAlign)) || 'left';
                 if (textAlign !== 'left') {
                     selectAll(this._editorView.state, tr => {
-                        this._editorView?.dispatch(tr.replaceSelectionWith(state.schema.nodes.paragraph.create({ align: textAlign })));
+                        this.EditorView?.dispatch(tr.replaceSelectionWith(state.schema.nodes.paragraph.create({ align: textAlign })));
                     });
                 }
                 if (startupText) {
-                    this._editorView?.dispatch(this._editorView.state.tr.insertText(startupText));
+                    this.EditorView?.dispatch(this.EditorView.state.tr.insertText(startupText));
                 }
                 this.tryUpdateDoc(true);
             }
@@ -1539,56 +1539,57 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
             Doc.SetSelectOnLoad(undefined);
             FormattedTextBox.SelectOnLoadChar = '';
         }
-        if (this._editorView && selectOnLoad && !this._props.dontRegisterView && !this._props.dontSelectOnLoad && this.isActiveTab(this.ProseRef)) {
+        if (this.EditorView && selectOnLoad && !this._props.dontRegisterView && !this._props.dontSelectOnLoad && this.isActiveTab(this.ProseRef)) {
             this._props.select(false);
             if (selLoadChar) {
-                const $from = this._editorView.state.selection.anchor ? this._editorView.state.doc.resolve(this._editorView.state.selection.anchor - 1) : undefined;
+                const $from = this.EditorView.state.selection.anchor ? this.EditorView.state.doc.resolve(this.EditorView.state.selection.anchor - 1) : undefined;
                 const mark = schema.marks.user_mark.create({ userid: ClientUtils.CurrentUserEmail(), modified: Math.floor(Date.now() / 1000) });
-                const curMarks = this._editorView.state.storedMarks ?? $from?.marksAcross(this._editorView.state.selection.$head) ?? [];
+                const curMarks = this.EditorView.state.storedMarks ?? $from?.marksAcross(this.EditorView.state.selection.$head) ?? [];
                 const storedMarks = [...curMarks.filter(m => m.type !== mark.type), mark];
-                const tr1 = this._editorView.state.tr.setStoredMarks(storedMarks);
-                const tr2 = selLoadChar === 'Enter' ? tr1.insert(this._editorView.state.doc.content.size - 1, schema.nodes.paragraph.create()) : tr1.insertText(selLoadChar, this._editorView.state.doc.content.size - 1);
+                const tr1 = this.EditorView.state.tr.setStoredMarks(storedMarks);
+                const tr2 = selLoadChar === 'Enter' ? tr1.insert(this.EditorView.state.doc.content.size - 1, schema.nodes.paragraph.create()) : tr1.insertText(selLoadChar, this.EditorView.state.doc.content.size - 1);
                 const tr = tr2.setStoredMarks(storedMarks);
 
-                this._editorView.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(tr.doc.content.size))));
+                this.EditorView.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(tr.doc.content.size))));
                 this.tryUpdateDoc(true); // calling select() above will make isContentActive() true only after a render .. which means the selectAll() above won't write to the Document and the incomingValue will overwrite the selection with the non-updated data
             } else if (!FormattedTextBox.DontSelectInitialText) {
                 const mark = schema.marks.user_mark.create({ userid: ClientUtils.CurrentUserEmail(), modified: Math.floor(Date.now() / 1000) });
-                selectAll(this._editorView.state, (tx: Transaction) => {
-                    this._editorView?.dispatch(tx.addStoredMark(mark));
+                selectAll(this.EditorView.state, (tx: Transaction) => {
+                    this.EditorView?.dispatch(tx.addStoredMark(mark));
                 });
+                this.EditorView?.dispatch(this.EditorView.state.tr.setSelection(new TextSelection(this.EditorView.state.doc.resolve(1))));
                 this.tryUpdateDoc(true); // calling select() above will make isContentActive() true only after a render .. which means the selectAll() above won't write to the Document and the incomingValue will overwrite the selection with the non-updated data
             } else {
-                const $from = this._editorView.state.selection.anchor ? this._editorView.state.doc.resolve(this._editorView.state.selection.anchor - 1) : undefined;
+                const $from = this.EditorView.state.selection.anchor ? this.EditorView.state.doc.resolve(this.EditorView.state.selection.anchor - 1) : undefined;
                 const mark = schema.marks.user_mark.create({ userid: ClientUtils.CurrentUserEmail(), modified: Math.floor(Date.now() / 1000) });
-                const curMarks = this._editorView.state.storedMarks ?? $from?.marksAcross(this._editorView.state.selection.$head) ?? [];
+                const curMarks = this.EditorView.state.storedMarks ?? $from?.marksAcross(this.EditorView.state.selection.$head) ?? [];
                 const storedMarks = [...curMarks.filter(m => m.type !== mark.type), mark];
-                const { tr } = this._editorView.state;
-                this._editorView.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(tr.doc.content.size))).setStoredMarks(storedMarks));
+                const { tr } = this.EditorView.state;
+                this.EditorView.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(tr.doc.content.size))).setStoredMarks(storedMarks));
                 this.tryUpdateDoc(true); // calling select() above will make isContentActive() true only after a render .. which means the selectAll() above won't write to the Document and the incomingValue will overwrite the selection with the non-updated data
             }
         }
         if (selectOnLoad) {
             FormattedTextBox.DontSelectInitialText = false;
-            this._editorView!.focus();
+            this.EditorView!.focus();
         }
         if (this._props.isContentActive()) this.prepareForTyping();
-        if (this._editorView && FormattedTextBox.PasteOnLoad) {
+        if (this.EditorView && FormattedTextBox.PasteOnLoad) {
             const pdfAnchorId = FormattedTextBox.PasteOnLoad.clipboardData?.getData('dash/pdfAnchor');
             FormattedTextBox.PasteOnLoad = undefined;
             pdfAnchorId && this.addPdfReference(pdfAnchorId);
         }
-        if (this._props.autoFocus) setTimeout(() => this._editorView!.focus()); // not sure why setTimeout is needed but editing dashFieldView's doesn't work without it.
+        if (this._props.autoFocus) setTimeout(() => this.EditorView!.focus()); // not sure why setTimeout is needed but editing dashFieldView's doesn't work without it.
     }
 
     // add user mark for any first character that was typed since the user mark that gets set in KeyPress won't have been called yet.
     prepareForTyping = () => {
-        if (this._editorView) {
+        if (this.EditorView) {
             const { text, paragraph } = schema.nodes;
-            const selNode = this._editorView.state.selection.$anchor.node();
-            if (this._editorView.state.selection.from === 1 && this._editorView.state.selection.empty && [undefined, text, paragraph].includes(selNode?.type)) {
+            const selNode = this.EditorView.state.selection.$anchor.node();
+            if (this.EditorView.state.selection.from === 1 && this.EditorView.state.selection.empty && [undefined, text, paragraph].includes(selNode?.type)) {
                 const docDefaultMarks = [schema.marks.user_mark.create({ userid: ClientUtils.CurrentUserEmail(), modified: Math.floor(Date.now() / 1000) })];
-                this._editorView.state.selection.empty && this._editorView.state.selection.from === 1 && this._editorView?.dispatch(this._editorView?.state.tr.setStoredMarks(docDefaultMarks).removeStoredMark(schema.marks.pFontColor));
+                this.EditorView.state.selection.empty && this.EditorView.state.selection.from === 1 && this.EditorView?.dispatch(this.EditorView?.state.tr.setStoredMarks(docDefaultMarks).removeStoredMark(schema.marks.pFontColor));
             }
         }
     };
@@ -1602,7 +1603,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
         FormattedTextBox.LiveTextUndo?.end();
         FormattedTextBox.LiveTextUndo = undefined;
         this.unhighlightSearchTerms();
-        this._editorView?.destroy();
+        this.EditorView?.destroy();
         RichTextMenu.Instance?.TextView === this && RichTextMenu.Instance.updateMenu(undefined, undefined, undefined, undefined);
         FormattedTextBoxComment.tooltip && (FormattedTextBoxComment.tooltip.style.display = 'none');
     }
@@ -1681,26 +1682,26 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
         }
     };
     setFocus = (ipos?: number) => {
-        const pos = ipos ?? (this._editorView?.state.selection.$from.pos || 1);
-        setTimeout(() => this._editorView?.dispatch(this._editorView.state.tr.setSelection(TextSelection.near(this._editorView.state.doc.resolve(pos)))), 100);
+        const pos = ipos ?? (this.EditorView?.state.selection.$from.pos || 1);
+        setTimeout(() => this.EditorView?.dispatch(this.EditorView.state.tr.setSelection(TextSelection.near(this.EditorView.state.doc.resolve(pos)))), 100);
         setTimeout(() => (this.ProseRef?.children?.[0] as HTMLElement).focus(), 200);
     };
     @action
     onFocused = (e: React.FocusEvent): void => {
-        // applyDevTools.applyDevTools(this._editorView);
+        // applyDevTools.applyDevTools(this.EditorView);
         e.stopPropagation();
     };
 
     onClick = (e: React.MouseEvent): void => {
         if (!this._props.isContentActive()) return;
-        const editorView = this._editorView;
+        const editorView = this.EditorView;
         const editorRoot = editorView?.root instanceof Document ? editorView.root : undefined;
         if (editorView && (!this._forceUncollapse || editorRoot?.getSelection()?.isCollapsed)) {
             // this is a hack to allow the cursor to be placed at the end of a document when the document ends in an inline dash comment.  Apparently Chrome on Windows has a bug/feature which breaks this when clicking after the end of the text.
             const pcords = editorView.posAtCoords({ left: e.clientX, top: e.clientY });
             const node = pcords && editorView.state.doc.nodeAt(pcords.pos); // get what prosemirror thinks the clicked node is (if it's null, then we didn't click on any text)
             if (pcords && node?.type === editorView.state.schema.nodes.dashComment) {
-                this._editorView!.dispatch(editorView.state.tr.setSelection(TextSelection.create(editorView.state.doc, pcords.pos + 2)));
+                this.EditorView!.dispatch(editorView.state.tr.setSelection(TextSelection.create(editorView.state.doc, pcords.pos + 2)));
                 e.preventDefault();
             }
             if (!node && this.ProseRef) {
@@ -1726,33 +1727,33 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
     hitBulletTargets(x: number, y: number, collapse: boolean, highlightOnly: boolean, selectOrderedList: boolean = false) {
         this._forceUncollapse = false;
         clearStyleSheetRules(FormattedTextBox._bulletStyleSheet);
-        const clickPos = this._editorView!.posAtCoords({ left: x, top: y });
+        const clickPos = this.EditorView!.posAtCoords({ left: x, top: y });
         const clickPosVal = clickPos?.pos || 1;
         let olistPos = clickPosVal;
         if (clickPos && olistPos && this._props.rootSelected?.()) {
-            const clickNode = this._editorView?.state.doc.resolve(olistPos).node();
-            const nodeBef = this._editorView?.state.doc.resolve(Math.max(0, olistPos - 1)).node();
-            olistPos = nodeBef?.type === this._editorView?.state.schema.nodes.ordered_list ? olistPos - 1 : olistPos;
-            let $olistPos = this._editorView?.state.doc.resolve(olistPos);
-            let olistNode = (nodeBef !== null || clickNode?.type === this._editorView?.state.schema.nodes.list_item) && olistPos === clickPos?.pos ? clickNode : nodeBef;
-            if (olistNode?.type === this._editorView?.state.schema.nodes.list_item) {
+            const clickNode = this.EditorView?.state.doc.resolve(olistPos).node();
+            const nodeBef = this.EditorView?.state.doc.resolve(Math.max(0, olistPos - 1)).node();
+            olistPos = nodeBef?.type === this.EditorView?.state.schema.nodes.ordered_list ? olistPos - 1 : olistPos;
+            let $olistPos = this.EditorView?.state.doc.resolve(olistPos);
+            let olistNode = (nodeBef !== null || clickNode?.type === this.EditorView?.state.schema.nodes.list_item) && olistPos === clickPos?.pos ? clickNode : nodeBef;
+            if (olistNode?.type === this.EditorView?.state.schema.nodes.list_item) {
                 if ($olistPos && $olistPos.depth) {
                     olistNode = $olistPos.parent;
-                    $olistPos = this._editorView?.state.doc.resolve($olistPos.start($olistPos.depth - 1));
+                    $olistPos = this.EditorView?.state.doc.resolve($olistPos.start($olistPos.depth - 1));
                 }
             }
-            const maxSize = this._editorView?.state.doc.content.size ?? 0;
-            const listPos = this._editorView?.state.doc.resolve(Math.min(maxSize, clickPosVal === olistPos ? clickPosVal + 1 : clickPosVal));
+            const maxSize = this.EditorView?.state.doc.content.size ?? 0;
+            const listPos = this.EditorView?.state.doc.resolve(Math.min(maxSize, clickPosVal === olistPos ? clickPosVal + 1 : clickPosVal));
             const listNode = listPos?.node();
-            if (olistNode && olistNode.type === this._editorView?.state.schema.nodes.ordered_list && listNode) {
+            if (olistNode && olistNode.type === this.EditorView?.state.schema.nodes.ordered_list && listNode) {
                 if (!highlightOnly) {
                     if (selectOrderedList) {
-                        this._editorView.dispatch(this._editorView.state.tr.setSelection(new NodeSelection(selectOrderedList ? $olistPos! : listPos!)));
+                        this.EditorView.dispatch(this.EditorView.state.tr.setSelection(new NodeSelection(selectOrderedList ? $olistPos! : listPos!)));
                     } else {
                         const nodePos = clickPosVal - (olistPos === clickPosVal ? 0 : 1);
-                        if (this._editorView.state.doc.nodeAt(nodePos)) {
-                            const tr = this._editorView.state.tr.setNodeMarkup(nodePos, listNode.type, { ...listNode.attrs, visibility: !listNode.attrs.visibility });
-                            this._editorView.dispatch(tr.setSelection(TextSelection.create(tr.doc, nodePos)));
+                        if (this.EditorView.state.doc.nodeAt(nodePos)) {
+                            const tr = this.EditorView.state.tr.setNodeMarkup(nodePos, listNode.type, { ...listNode.attrs, visibility: !listNode.attrs.visibility });
+                            this.EditorView.dispatch(tr.setSelection(TextSelection.create(tr.doc, nodePos)));
                         }
                     }
                 }
@@ -1772,19 +1773,19 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
     onBlur = (e: React.FocusEvent) => {
         if (this.ProseRef?.children[0] !== e.nativeEvent.target) return;
         if (!(this.EditorView?.state.selection instanceof NodeSelection) || this.EditorView.state.selection.node.type !== this.EditorView.state.schema.nodes.footnote) {
-            const stordMarks = this._editorView?.state.storedMarks?.slice();
+            const stordMarks = this.EditorView?.state.storedMarks?.slice();
             if (!(this.EditorView?.state.selection instanceof NodeSelection)) {
                 this.autoLink();
-                if (this._editorView?.state.tr) {
+                if (this.EditorView?.state.tr) {
                     const tr = stordMarks?.reduce((tr2, m) => {
                         tr2.addStoredMark(m);
                         return tr2;
-                    }, this._editorView.state.tr);
-                    tr && this._editorView.dispatch(tr);
+                    }, this.EditorView.state.tr);
+                    tr && this.EditorView.dispatch(tr);
                 }
             }
         }
-        if (RichTextMenu.Instance?.view === this._editorView && !(this._props.isContentActive() || this._props.rootSelected?.())) {
+        if (RichTextMenu.Instance?.view === this.EditorView && !(this._props.isContentActive() || this._props.rootSelected?.())) {
             RichTextMenu.Instance?.updateMenu(undefined, undefined, undefined, undefined);
         }
 
@@ -1831,7 +1832,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
         }
         switch (e.key) {
             case 'Escape':
-                this._editorView!.dispatch(state.tr.setSelection(TextSelection.create(state.doc, state.selection.from, state.selection.from)));
+                this.EditorView!.dispatch(state.tr.setSelection(TextSelection.create(state.doc, state.selection.from, state.selection.from)));
                 (document.activeElement as HTMLElement).blur?.();
                 DocumentView.DeselectAll();
                 RichTextMenu.Instance?.updateMenu(undefined, undefined, undefined, undefined);
@@ -1857,7 +1858,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
         this.startUndoTypingBatch();
     };
     ondrop = (e: React.DragEvent) => {
-        this._editorView!.dispatch(updateBullets(this._editorView!.state.tr, this._editorView!.state.schema));
+        this.EditorView?.dispatch(updateBullets(this.EditorView.state.tr, this.EditorView.state.schema));
         e.stopPropagation(); // drag n drop of text within text note will generate a new note if not caughst, as will dragging in from outside of Dash.
     };
     onScroll = (e: React.UIEvent) => {
@@ -1872,7 +1873,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
     tryUpdateScrollHeight = () => {
         const margins = 2 * NumCast(this.layoutDoc._yMargin, this._props.yPadding || 0);
         const children = this.ProseRef?.children.length ? Array.from(this.ProseRef.children[0].children) : undefined;
-        if (children && !SnappingManager.IsDragging) {
+        if (this.EditorView && children && !SnappingManager.IsDragging) {
             const getChildrenHeights = (kids: Element[] | undefined) => kids?.reduce((p, child) => p + toHgt(child), margins) ?? 0;
             const toNum = (val: string) => Number(val.replace('px', ''));
             const toHgt = (node: Element): number => {
@@ -2093,7 +2094,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
         const paddingX = Math.max(NumCast(this.layoutDoc._xMargin), this._props.xPadding ?? 0, 0, ((this._props.screenXPadding?.() ?? 0) - scrMargin[0]) * this.ScreenToLocalBoxXf().Scale);
         const paddingY = Math.max(NumCast(this.layoutDoc._yMargin), 0, ((this._props.yPadding ?? 0) - scrMargin[1]) * this.ScreenToLocalBoxXf().Scale);
         const styleFromLayout = styleFromLayoutString(this.Document, this._props, scale); // this converts any expressions in the format string to style props.  e.g., <FormattedTextBox height='{this._header_height}px' >
-        return styleFromLayout?.height === '0px' ? null : (
+        return this.isLabel ? (
+            <LabelBox {...this._props} />
+        ) : styleFromLayout?.height === '0px' ? null : (
             <div
                 className="formattedTextBox"
                 ref={r => {
@@ -2156,8 +2159,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
                                 paddingTop: StrCast(this.layoutDoc._textBoxPaddingY, `${paddingY}px`),
                                 paddingBottom: StrCast(this.layoutDoc._textBoxPaddingY, `${paddingY}px`),
                                 color: StrCast(this.layoutDoc.text_fontColor),
-                                fontWeight: `${this.layoutDoc.contentBold ? 'bold' : ''}`,
-                                textTransform: `${this.layoutDoc.textTransform}` as Property.TextTransform,
+                                fontWeight: this.layoutDoc.contentBold ? 'bold' : '',
+                                textTransform: StrCast(this.dataDoc[this.fieldKey + '_transform']) as Property.TextTransform,
                             }}
                         />
                     </div>
diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
index 7a8b72be0..3c84e5a10 100644
--- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
+++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
@@ -349,7 +349,9 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any): KeyMa
                                 dispatch(tx4);
                             }
 
-                            if (view.state.selection.$anchor.nodeAfter?.type === schema.nodes.text && once) {
+                            if (view.state.selection.$anchor.depth > 0 &&
+                                view.state.selection.$anchor.node(view.state.selection.$anchor.depth-1).type === schema.nodes.list_item &&
+                                view.state.selection.$anchor.nodeAfter?.type === schema.nodes.text && once) {
                                 // if text is selected across list items, then we need to forcibly insert a new line since the splitBlock code joins the two list items.
                                 enter(view.state, dispatch, view, false);
                             }
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index 55e6a3a5b..65a415a23 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -1,6 +1,6 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Tooltip } from '@mui/material';
-import { action, computed, IReactionDisposer, makeObservable, observable, runInAction } from 'mobx';
+import { action, computed, makeObservable, observable, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import { lift, toggleMark, wrapIn } from 'prosemirror-commands';
 import { Mark, MarkType } from 'prosemirror-model';
@@ -32,7 +32,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
     public overMenu: boolean = false; // kind of hacky way to prevent selects not being selectable
     private _linkToRef = React.createRef<HTMLInputElement>();
 
-    layoutDoc: Doc | undefined;
+    dataDoc: Doc | undefined;
     @observable public view?: EditorView & { TextView?: FormattedTextBox } = undefined;
     public editorProps: FieldViewProps | AntimodeMenuProps | undefined;
 
@@ -49,6 +49,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
 
     @observable private _activeFontSize: string = '13px';
     @observable private _activeFontFamily: string = '';
+    @observable private _activeFitBox: boolean = false;
     @observable private _activeListType: string = '';
     @observable private _activeAlignment: string = 'left';
 
@@ -64,13 +65,12 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
     @observable private currentLink: string | undefined = '';
     @observable private showLinkDropdown: boolean = false;
 
-    _reaction: IReactionDisposer | undefined;
     constructor(props: AntimodeMenuProps) {
         super(props);
         makeObservable(this);
         runInAction(() => {
             RichTextMenu._instance.menu = this;
-            this.updateMenu(undefined, undefined, props, this.layoutDoc);
+            this.updateMenu(undefined, undefined, props, this.dataDoc);
             this._canFade = false;
             this.Pinned = true;
         });
@@ -101,6 +101,9 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
     @computed get fontHighlight() {
         return this._activeHighlightColor;
     }
+    @computed get fitBox() {
+        return this._activeFitBox;
+    }
     @computed get fontFamily() {
         return this._activeFontFamily;
     }
@@ -114,26 +117,16 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
         return this._activeAlignment;
     }
     @computed get textVcenter() {
-        return BoolCast(this.layoutDoc?._layout_centered);
-    }
-    _disposer: IReactionDisposer | undefined;
-    componentDidMount() {
-        // this._disposer = reaction(
-        //     () => DocumentView.Selected().slice(),
-        //     () => this.updateMenu(undefined, undefined, undefined, undefined)
-        // );
-    }
-    componentWillUnmount() {
-        this._disposer?.();
+        return BoolCast(this.dataDoc?._layout_centered, BoolCast(Doc.UserDoc().layout_centered));
     }
 
     @action
-    public updateMenu(view: EditorView | undefined, lastState: EditorState | undefined, props: FormattedTextBoxProps | AntimodeMenuProps | undefined, layoutDoc: Doc | undefined) {
+    public updateMenu(view: EditorView | undefined, lastState: EditorState | undefined, props: FormattedTextBoxProps | AntimodeMenuProps | undefined, dataDoc: Doc | undefined) {
         if (this._linkToRef.current?.getBoundingClientRect().width) {
             return;
         }
         this.view = view;
-        this.layoutDoc = layoutDoc;
+        this.dataDoc = dataDoc;
         props && (this.editorProps = props);
 
         // Don't do anything if the document/selection didn't change
@@ -147,12 +140,13 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
         const { activeSizes } = active;
         const { activeColors } = active;
         const { activeHighlights } = active;
-        const refDoc = DocumentView.Selected().lastElement()?.layoutDoc ?? Doc.UserDoc();
+        const refDoc = DocumentView.Selected().lastElement()?.dataDoc ?? Doc.UserDoc();
         const refField = (pfx => (pfx ? pfx + '_' : ''))(DocumentView.Selected().lastElement()?.LayoutFieldKey);
         const refVal = (field: string, dflt: string) => StrCast(refDoc[refField + field], StrCast(Doc.UserDoc()[field], dflt));
 
         this._activeListType = this.getActiveListStyle();
         this._activeAlignment = this.getActiveAlignment();
+        this._activeFitBox = BoolCast(refDoc[refField + 'fitBox'], BoolCast(Doc.UserDoc().fitBox));
         this._activeFontFamily = !activeFamilies.length ? StrCast(this.TextView?.Document._text_fontFamily, refVal('fontFamily', 'Arial')) : activeFamilies.length === 1 ? String(activeFamilies[0]) : 'various';
         this._activeFontSize = !activeSizes.length ? StrCast(this.TextView?.Document.fontSize, refVal('fontSize', '10px')) : activeSizes[0];
         this._activeFontColor = !activeColors.length ? StrCast(this.TextView?.Document.fontColor, refVal('fontColor', 'black')) : activeColors.length > 0 ? String(activeColors[0]) : '...';
@@ -181,7 +175,6 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
                     toggleMark(mark.type, mark.attrs)(state, dispatch);
                 }
             }
-            //    this.updateMenu(this.view, undefined, undefined, this.layoutDoc);
         }
     };
 
@@ -195,8 +188,10 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
                     return node.attrs.align || 'left';
                 }
             }
+        } else if (this.dataDoc) {
+            return StrCast(this.dataDoc.text_align) || 'left';
         }
-        return 'left';
+        return StrCast(Doc.UserDoc().textAlign) || 'left';
     };
 
     // finds font sizes and families in selection
@@ -330,6 +325,17 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
             this.view.focus();
         }
     };
+    toggleFitBox = () => {
+        if (this.dataDoc) {
+            const doc = this.dataDoc;
+            (document.activeElement as HTMLElement)?.blur();
+            doc.text_fitBox = !doc.text_fitBox;
+        } else {
+            Doc.UserDoc().fitBox = !Doc.UserDoc().fitBox;
+            Doc.UserDoc().textAlign = Doc.UserDoc().fitBox ? 'center' : undefined;
+        }
+        this.updateMenu(undefined, undefined, undefined, this.dataDoc);
+    };
     toggleBold = () => {
         if (this.view) {
             const mark = this.view.state.schema.mark(this.view.state.schema.marks.strong);
@@ -354,11 +360,14 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
         }
     };
 
-    setFontField = (value: string, fontField: 'fontSize' | 'fontFamily' | 'fontColor' | 'fontHighlight') => {
-        if (this.TextView && this.view) {
+    setFontField = (value: string, fontField: 'fitBox' | 'fontSize' | 'fontFamily' | 'fontColor' | 'fontHighlight') => {
+        if (this.dataDoc) {
+            this.dataDoc[`text_${fontField}`] = value;
+            this.updateMenu(undefined, undefined, undefined, this.dataDoc);
+        } else if (this.TextView && this.view) {
             const { text, paragraph } = this.view.state.schema.nodes;
             const selNode = this.view.state.selection.$anchor.node();
-            if (this.view.state.selection.from === 1 && this.view.state.selection.empty && [undefined, text, paragraph].includes(selNode?.type)) {
+            if ((fontField === 'fontSize' && value === '0px') || (this.view.state.selection.from === 1 && this.view.state.selection.empty && [undefined, text, paragraph].includes(selNode?.type))) {
                 this.TextView.dataDoc[this.TextView.fieldKey + `_${fontField}`] = value;
                 this.view.focus();
             }
@@ -369,7 +378,6 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
             this.view.focus();
         } else {
             Doc.UserDoc()[fontField] = value;
-            // this.updateMenu(this.view, undefined, this.props, this.layoutDoc);
         }
     };
 
@@ -395,7 +403,6 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
                 this.view!.dispatch(tx3);
             });
         this.view.focus();
-        // this.updateMenu(this.view, undefined, this.props, this.layoutDoc);
     };
 
     insertSummarizer(state: EditorState, dispatch: (tr: Transaction) => void) {
@@ -410,10 +417,11 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
     }
 
     vcenterToggle = () => {
-        this.layoutDoc && (this.layoutDoc._layout_centered = !this.layoutDoc._layout_centered);
+        if (this.dataDoc) this.dataDoc._layout_centered = !this.dataDoc._layout_centered;
+        else Doc.UserDoc()._layout_centered = !Doc.UserDoc()._layout_centered;
     };
-    align = (view: EditorView, dispatch: (tr: Transaction) => void, alignment: 'left' | 'right' | 'center') => {
-        if (this.RootSelected) {
+    align = (view: EditorView | undefined, dispatch: undefined | ((tr: Transaction) => void), alignment: 'left' | 'right' | 'center') => {
+        if (view && dispatch && this.RootSelected) {
             let { tr } = view.state;
             view.state.doc.nodesBetween(view.state.selection.from, view.state.selection.to, (node, pos) => {
                 if ([schema.nodes.paragraph, schema.nodes.heading].includes(node.type)) {
@@ -425,6 +433,11 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
             });
             view.focus();
             dispatch?.(tr);
+        } else {
+            if (this.dataDoc) {
+                this.dataDoc.text_align = alignment;
+            } else Doc.UserDoc().textAlign = alignment;
+            this.updateMenu(undefined, undefined, undefined, this.dataDoc);
         }
     };
 
@@ -702,7 +715,7 @@ interface RichTextMenuPluginProps {
 }
 export class RichTextMenuPlugin extends React.Component<RichTextMenuPluginProps> {
     update(view: EditorView & { TextView?: FormattedTextBox }, lastState: EditorState | undefined) {
-        RichTextMenu.Instance?.updateMenu(view, lastState, this.props.editorProps, view.TextView?.layoutDoc);
+        RichTextMenu.Instance?.updateMenu(view, lastState, this.props.editorProps, view.TextView?.dataDoc);
     }
     render() {
         return null;
diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts
index f58434906..3d14ce4c0 100644
--- a/src/client/views/nodes/formattedText/RichTextRules.ts
+++ b/src/client/views/nodes/formattedText/RichTextRules.ts
@@ -121,7 +121,7 @@ export class RichTextRules {
                     annotationOn: textDoc,
                     _layout_fitWidth: true,
                     _layout_autoHeight: true,
-                    _text_fontSize: '9px',
+                    text_fontSize: '9px',
                     title: 'inline comment',
                 });
                 textDocInline.title = inlineFieldKey; // give the annotation its own title
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 5d3672aee..06869717a 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -2616,13 +2616,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     createTemplate = (layout: string, input?: string) => {
         const x = this.activeItem && this.targetDoc ? NumCast(this.targetDoc.x) : 0;
         const y = this.activeItem && this.targetDoc ? NumCast(this.targetDoc.y) + NumCast(this.targetDoc._height) + 20 : 0;
-        const title = () => Docs.Create.TextDocument('Click to change title', { title: 'Slide title', _width: 380, _height: 60, x: 10, y: 58, _text_fontSize: '24pt' });
-        const subtitle = () => Docs.Create.TextDocument('Click to change subtitle', { title: 'Slide subtitle', _width: 380, _height: 50, x: 10, y: 118, _text_fontSize: '16pt' });
-        const header = () => Docs.Create.TextDocument('Click to change header', { title: 'Slide header', _width: 380, _height: 65, x: 10, y: 80, _text_fontSize: '20pt' });
-        const contentTitle = () => Docs.Create.TextDocument('Click to change title', { title: 'Slide title', _width: 380, _height: 60, x: 10, y: 10, _text_fontSize: '24pt' });
-        const content = () => Docs.Create.TextDocument('Click to change text', { title: 'Slide text', _width: 380, _height: 145, x: 10, y: 70, _text_fontSize: '14pt' });
-        const content1 = () => Docs.Create.TextDocument('Click to change text', { title: 'Column 1', _width: 185, _height: 140, x: 10, y: 80, _text_fontSize: '14pt' });
-        const content2 = () => Docs.Create.TextDocument('Click to change text', { title: 'Column 2', _width: 185, _height: 140, x: 205, y: 80, _text_fontSize: '14pt' });
+        const title = () => Docs.Create.TextDocument('Click to change title', { title: 'Slide title', _width: 380, _height: 60, x: 10, y: 58, text_fontSize: '24pt' });
+        const subtitle = () => Docs.Create.TextDocument('Click to change subtitle', { title: 'Slide subtitle', _width: 380, _height: 50, x: 10, y: 118, text_fontSize: '16pt' });
+        const header = () => Docs.Create.TextDocument('Click to change header', { title: 'Slide header', _width: 380, _height: 65, x: 10, y: 80, text_fontSize: '20pt' });
+        const contentTitle = () => Docs.Create.TextDocument('Click to change title', { title: 'Slide title', _width: 380, _height: 60, x: 10, y: 10, text_fontSize: '24pt' });
+        const content = () => Docs.Create.TextDocument('Click to change text', { title: 'Slide text', _width: 380, _height: 145, x: 10, y: 70, text_fontSize: '14pt' });
+        const content1 = () => Docs.Create.TextDocument('Click to change text', { title: 'Column 1', _width: 185, _height: 140, x: 10, y: 80, text_fontSize: '14pt' });
+        const content2 = () => Docs.Create.TextDocument('Click to change text', { title: 'Column 2', _width: 185, _height: 140, x: 205, y: 80, text_fontSize: '14pt' });
         // prettier-ignore
         switch (layout) {
             case 'blank': return Docs.Create.FreeformDocument([], { title: input || 'Blank slide', _width: 400, _height: 225, x, y });
diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.tsx b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
index d5f5f620c..a7e78bcea 100644
--- a/src/client/views/pdf/GPTPopup/GPTPopup.tsx
+++ b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
@@ -572,18 +572,19 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> {
         <div style={{ height: '80%' }}>
             {this.heading(this.mode === GPTPopupMode.SORT ? 'SORTING' : 'QUIZ')}
             <>
-                {!this.cardsDoneLoading ? (
-                    <div className="content-wrapper">
-                        <div className="loading-spinner">
-                            <ReactLoading type="spin" color={StrCast(Doc.UserDoc().userVariantColor)} height={30} width={30} />
-                            {this.loading ? <span>Loading...</span> : <span>Reading Cards...</span>}
+                {
+                    !this.cardsDoneLoading ? (
+                        <div className="content-wrapper">
+                            <div className="loading-spinner">
+                                <ReactLoading type="spin" color={StrCast(Doc.UserDoc().userVariantColor)} height={30} width={30} />
+                                {this.loading ? <span>Loading...</span> : <span>Reading Cards...</span>}
+                            </div>
                         </div>
-                    </div>
-                ) : this.mode === GPTPopupMode.CARD ? (
-                    this.cardMenu()
-                ) : (
-                    this.cardActual(this.mode)
-                ) // Call the functions to render JSX
+                    ) : this.mode === GPTPopupMode.CARD ? (
+                        this.cardMenu()
+                    ) : (
+                        this.cardActual(this.mode)
+                    ) // Call the functions to render JSX
                 }
             </>
         </div>
diff --git a/src/fields/documentSchemas.ts b/src/fields/documentSchemas.ts
index 335683270..b27816f55 100644
--- a/src/fields/documentSchemas.ts
+++ b/src/fields/documentSchemas.ts
@@ -48,8 +48,8 @@ export const documentSchema = createSchema({
     _columnsHideIfEmpty: 'boolean', // whether empty stacking view column headings should be hidden
     // _columnHeaders: listSpec(SchemaHeaderField), // header descriptions for stacking/masonry
     // _schemaHeaders: listSpec(SchemaHeaderField), // header descriptions for schema views
-    _text_fontSize: 'string',
-    _text_fontFamily: 'string',
+    text_fontSize: 'string',
+    text_fontFamily: 'string',
     _layout_sidebarWidthPercent: 'string', // percent of text window width taken up by sidebar
 
     // appearance properties on the data document
@@ -70,7 +70,7 @@ export const documentSchema = createSchema({
     stroke_startMarker: 'string',
     stroke_endMarker: 'string',
     stroke_dash: 'string',
-    textTransform: 'string',
+    text_transform: 'string',
     treeView_Open: 'boolean', //  flag denoting whether the documents sub-tree (contents) is visible or hidden
     treeView_ExpandedView: 'string', // name of field whose contents are being displayed as the document's subtree
     treeView_ExpandedViewLock: 'boolean', // whether the expanded view can be changed
@@ -112,5 +112,4 @@ export const collectionSchema = createSchema({
 });
 
 export type Document = makeInterface<[typeof documentSchema]>;
-// eslint-disable-next-line no-redeclare
 export const Document = makeInterface(documentSchema);
-- 
cgit v1.2.3-70-g09d2


From b6411c0c053db2b4495d0c545c491d9f18238e21 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 1 Nov 2024 10:53:37 -0400
Subject: fixed resize problem where NaN is generated by scaling very narrow in
 a card view.

---
 src/ClientUtils.ts                                      | 2 +-
 src/client/documents/Documents.ts                       | 2 ++
 src/client/views/DocumentDecorations.tsx                | 4 ++--
 src/client/views/collections/CollectionCardDeckView.tsx | 2 +-
 4 files changed, 6 insertions(+), 4 deletions(-)

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

diff --git a/src/ClientUtils.ts b/src/ClientUtils.ts
index baad9a06c..8f62b9060 100644
--- a/src/ClientUtils.ts
+++ b/src/ClientUtils.ts
@@ -165,7 +165,7 @@ export namespace ClientUtils {
             return { scale: 0, translateX: 1, translateY: 1 };
         }
         const rect = ele.getBoundingClientRect();
-        const scale = ele.offsetWidth === 0 && rect.width === 0 ? 1 : rect.width / ele.offsetWidth;
+        const scale = ele.offsetWidth === 0 && rect.width === 0 ? 1 : rect.width / (ele.offsetWidth || 1);
         const translateX = rect.left;
         const translateY = rect.top;
 
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index efd6ce3c9..d898fe0c5 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -926,6 +926,8 @@ export namespace Docs {
             I.stroke_isInkMask = isInkMask;
             I.text_align = 'center';
             I.rotation = 0;
+            I.width_min = 1;
+            I.height_min = 1;
             I.defaultDoubleClick = 'ignore';
             I.author_date = new DateField();
             I.acl_Guest = Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.View;
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 5a48b6c62..66043c033 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -559,8 +559,8 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
                 if (setData) Doc.SetNativeHeight(doc[DocData], NumCast(doc._nativeHeight));
             }
 
-            doc._width = Math.max(1, NumCast(doc._width) * scale.x);
-            doc._height = Math.max(1, NumCast(doc._height) * scale.y);
+            doc._width = Math.max(NumCast(doc._width_min, 25), NumCast(doc._width) * scale.x);
+            doc._height = Math.max(NumCast(doc._height_min, 25), NumCast(doc._height) * scale.y);
             const { deltaX, deltaY } = this.realignRefPt(doc, refCent, initWidth, initHeight);
             doc.x = NumCast(doc.x) + deltaX;
             doc.y = NumCast(doc.y) + deltaY;
diff --git a/src/client/views/collections/CollectionCardDeckView.tsx b/src/client/views/collections/CollectionCardDeckView.tsx
index 5e6c40400..39d9fe69d 100644
--- a/src/client/views/collections/CollectionCardDeckView.tsx
+++ b/src/client/views/collections/CollectionCardDeckView.tsx
@@ -555,7 +555,7 @@ export class CollectionCardView extends CollectionSubView() {
 
         const dref = this._docRefs.get(doc);
         const { translateX, translateY, scale } = ClientUtils.GetScreenTransform(dref?.ContentDiv);
-        if (!scale) return new Transform(0, 0, 0);
+        if (!scale) return new Transform(0, 0, 1);
 
         return new Transform(-translateX + (dref?.centeringX || 0) * scale, 
                              -translateY + (dref?.centeringY || 0) * scale, 1)
-- 
cgit v1.2.3-70-g09d2


From 4b9b61dc9b0a7f1fd50fce7646f9312ea826364c Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 1 Nov 2024 12:04:28 -0400
Subject: fixed inifnite loop in card view caused by bug in formattedText of
 adding tagsView height to doc height.  made selected docs also be focused
 docs in cardview.  fixed tags text color to be set to black

---
 src/client/views/TagsView.scss                            |  1 +
 src/client/views/collections/CollectionCardDeckView.tsx   | 12 ++++++++++--
 src/client/views/nodes/formattedText/FormattedTextBox.tsx |  6 +++---
 3 files changed, 14 insertions(+), 5 deletions(-)

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

diff --git a/src/client/views/TagsView.scss b/src/client/views/TagsView.scss
index 24f9e86bc..b741e7bfd 100644
--- a/src/client/views/TagsView.scss
+++ b/src/client/views/TagsView.scss
@@ -59,6 +59,7 @@
     margin: auto;
     align-self: center;
     width: 90%;
+    color: black;
 }
 
 .tagsView-buttons {
diff --git a/src/client/views/collections/CollectionCardDeckView.tsx b/src/client/views/collections/CollectionCardDeckView.tsx
index 39d9fe69d..836a5a2c3 100644
--- a/src/client/views/collections/CollectionCardDeckView.tsx
+++ b/src/client/views/collections/CollectionCardDeckView.tsx
@@ -1,4 +1,4 @@
-import { IReactionDisposer, ObservableMap, action, computed, makeObservable, observable, reaction } from 'mobx';
+import { IReactionDisposer, ObservableMap, action, computed, makeObservable, observable, reaction, trace } from 'mobx';
 import { observer } from 'mobx-react';
 import { computedFn } from 'mobx-utils';
 import * as React from 'react';
@@ -113,6 +113,12 @@ export class CollectionCardView extends CollectionSubView() {
                 }
             }
         );
+        this._disposers.select = reaction(
+            () => this.childDocs.find(d => this._docRefs.get(d)?.IsSelected),
+            selected => {
+                selected && (this.layoutDoc._card_curDoc = selected);
+            }
+        );
     }
 
     componentWillUnmount() {
@@ -349,7 +355,7 @@ export class CollectionCardView extends CollectionSubView() {
             onClickScript={this.curDoc() === doc ? undefined : this._setCurDocScript}
             dontCenter="y" // Don't center it vertically, because the grid it's in is already doing that and we don't want to do it twice.
             dragAction={(this.Document.childDragAction ?? this._props.childDragAction) as dropActionType}
-            showTags={BoolCast(this.layoutDoc.showChildTags)}
+            showTags={BoolCast(this.layoutDoc.showChildTags) || BoolCast(this.Document._layout_showTags)}
             whenChildContentsActiveChanged={this._props.whenChildContentsActiveChanged}
             dontHideOnDrag
         />
@@ -620,6 +626,7 @@ export class CollectionCardView extends CollectionSubView() {
      * Actually renders all the cards
      */
     @computed get renderCards() {
+        trace();
         // Map sorted documents to their rendered components
         return this.sortedDocs.map((doc, index) => {
             const cardsInRow = this.cardsInRowThatIncludesCardIndex(index);
@@ -667,6 +674,7 @@ export class CollectionCardView extends CollectionSubView() {
     curDoc = () => DocCast(this.layoutDoc._card_curDoc);
 
     render() {
+        trace();
         const fitContentScale = this.childCards.length === 0 ? 1 : this.fitContentScale;
         return (
             <div
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 905c69bb8..35f1ec9ef 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1274,9 +1274,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
         );
         this._disposers.componentHeights = reaction(
             // set the document height when one of the component heights changes and layout_autoHeight is on
-            () => ({ sidebarHeight: this.sidebarHeight, textHeight: this.textHeight, layoutAutoHeight: this.layout_autoHeight, marginsHeight: this.layout_autoHeightMargins, tagsHeight: this.tagsHeight }),
-            ({ sidebarHeight, textHeight, layoutAutoHeight, marginsHeight, tagsHeight }) => {
-                const newHeight = this.contentScaling * (tagsHeight + marginsHeight + Math.max(sidebarHeight, textHeight));
+            () => ({ sidebarHeight: this.sidebarHeight, textHeight: this.textHeight, layoutAutoHeight: this.layout_autoHeight, marginsHeight: this.layout_autoHeightMargins }),
+            ({ sidebarHeight, textHeight, layoutAutoHeight, marginsHeight }) => {
+                const newHeight = this.contentScaling * (marginsHeight + Math.max(sidebarHeight, textHeight));
                 if (
                     (!Array.from(FormattedTextBox._globalHighlights).includes('Bold Text') || this._props.isSelected()) && //
                     layoutAutoHeight &&
-- 
cgit v1.2.3-70-g09d2


From e7b0b92adf3404bc24e1b9d5d6a857a0ad6e6418 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 28 Jan 2025 10:53:58 -0500
Subject: cleaning up card deck view.

---
 src/client/apis/gpt/GPT.ts                         | 26 ++++++++--
 src/client/views/TagsView.tsx                      |  2 +-
 .../views/collections/CollectionCardDeckView.scss  |  5 +-
 .../views/collections/CollectionCardDeckView.tsx   | 60 +++++++++++++++++++---
 src/client/views/pdf/GPTPopup/GPTPopup.tsx         | 49 +++++++++---------
 5 files changed, 101 insertions(+), 41 deletions(-)

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

diff --git a/src/client/apis/gpt/GPT.ts b/src/client/apis/gpt/GPT.ts
index cf3a28a8e..d96972784 100644
--- a/src/client/apis/gpt/GPT.ts
+++ b/src/client/apis/gpt/GPT.ts
@@ -16,10 +16,10 @@ enum GPTCallType {
     PRONUNCIATION = 'pronunciation',
     DRAW = 'draw',
     COLOR = 'color',
-    RUBRIC = 'rubric',
-    TYPE = 'type',
-    SUBSET = 'subset',
-    INFO = 'info',
+    RUBRIC = 'rubric', // needs to be filled in below
+    TYPE = 'type', // needs to be filled in below
+    SUBSET = 'subset', // needs to be filled in below
+    INFO = 'info', // needs to be filled in below
     TEMPLATE = 'template',
     VIZSUM = 'vizsum',
     VIZSUM2 = 'vizsum2',
@@ -84,6 +84,18 @@ const callTypeMap: { [type: string]: GPTCallOpts } = {
         temp: 0.1, //0.3
         prompt: "BRIEFLY (<50 words) describe any differences between the rubric and the user's answer answer in second person. If there are no differences, say correct",
     },
+    type: {
+        model: 'gpt-4-turbo',
+        maxTokens: 512,
+        temp: 0.5,
+        prompt: '',
+    },
+    info: {
+        model: 'gpt-4-turbo',
+        maxTokens: 512,
+        temp: 0.5,
+        prompt: '',
+    },
     template: {
         model: 'gpt-4-turbo',
         maxTokens: 512,
@@ -131,8 +143,12 @@ let lastResp = '';
  * @returns AI Output
  */
 const gptAPICall = async (inputTextIn: string, callType: GPTCallType, prompt?: string, dontCache?: boolean) => {
-    const inputText = [GPTCallType.SUMMARY, GPTCallType.FLASHCARD, GPTCallType.QUIZ, GPTCallType.STACK].includes(callType) ? inputTextIn + '.' : inputTextIn;
+    const inputText = inputTextIn + ([GPTCallType.SUMMARY, GPTCallType.FLASHCARD, GPTCallType.QUIZ, GPTCallType.STACK].includes(callType) ? '.' : '');
     const opts = callTypeMap[callType];
+    if (!opts) {
+        console.log('The query type:' + callType + ' requires a configuration.');
+        return 'Error connecting with API.';
+    }
     if (lastCall === inputText && dontCache !== true) return lastResp;
     try {
         lastCall = inputText;
diff --git a/src/client/views/TagsView.tsx b/src/client/views/TagsView.tsx
index 73f404596..9e936ea54 100644
--- a/src/client/views/TagsView.tsx
+++ b/src/client/views/TagsView.tsx
@@ -9,7 +9,7 @@ import { emptyFunction } from '../../Utils';
 import { Doc, DocListCast, Field, Opt, StrListCast } from '../../fields/Doc';
 import { DocData } from '../../fields/DocSymbols';
 import { List } from '../../fields/List';
-import { DocCast, NumCast, StrCast } from '../../fields/Types';
+import { DocCast, StrCast } from '../../fields/Types';
 import { DocumentType } from '../documents/DocumentTypes';
 import { DragManager } from '../util/DragManager';
 import { SnappingManager } from '../util/SnappingManager';
diff --git a/src/client/views/collections/CollectionCardDeckView.scss b/src/client/views/collections/CollectionCardDeckView.scss
index 5283601bf..79c53db08 100644
--- a/src/client/views/collections/CollectionCardDeckView.scss
+++ b/src/client/views/collections/CollectionCardDeckView.scss
@@ -28,11 +28,14 @@
 
 .collectionCardView-cardwrapper {
     display: grid;
-    grid-template-columns: repeat(10, 1fr);
     transform-origin: left 50%;
     align-items: center;
     z-index: 0; // so that setting z-index of active card doesn't make it land on top of things outside of the card-wrapper
 }
+.collectionCardView-cardSizeDragger {
+    position: absolute;
+    top: 0;
+}
 
 .no-card-span {
     position: relative;
diff --git a/src/client/views/collections/CollectionCardDeckView.tsx b/src/client/views/collections/CollectionCardDeckView.tsx
index 836a5a2c3..800eb4914 100644
--- a/src/client/views/collections/CollectionCardDeckView.tsx
+++ b/src/client/views/collections/CollectionCardDeckView.tsx
@@ -1,8 +1,9 @@
-import { IReactionDisposer, ObservableMap, action, computed, makeObservable, observable, reaction, trace } from 'mobx';
+import { IReactionDisposer, ObservableMap, action, computed, makeObservable, observable, reaction, runInAction, trace } from 'mobx';
 import { observer } from 'mobx-react';
 import { computedFn } from 'mobx-utils';
 import * as React from 'react';
-import { ClientUtils, DashColor, imageUrlToBase64, returnFalse, returnNever, returnZero } from '../../../ClientUtils';
+import * as CSS from 'csstype';
+import { ClientUtils, DashColor, imageUrlToBase64, returnFalse, returnNever, returnZero, setupMoveUpEvents } from '../../../ClientUtils';
 import { emptyFunction } from '../../../Utils';
 import { Doc, DocListCast, Opt } from '../../../fields/Doc';
 import { Animation, DocData } from '../../../fields/DocSymbols';
@@ -18,7 +19,7 @@ import { DragManager } from '../../util/DragManager';
 import { dropActionType } from '../../util/DropActionTypes';
 import { SnappingManager } from '../../util/SnappingManager';
 import { Transform } from '../../util/Transform';
-import { undoable } from '../../util/UndoManager';
+import { undoable, UndoManager } from '../../util/UndoManager';
 import { PinDocView, PinProps } from '../PinFuncs';
 import { StyleProp } from '../StyleProp';
 import { TagItem } from '../TagsView';
@@ -27,6 +28,8 @@ import { FocusViewOptions } from '../nodes/FocusViewOptions';
 import { GPTPopup, GPTPopupMode } from '../pdf/GPTPopup/GPTPopup';
 import './CollectionCardDeckView.scss';
 import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { SettingsManager } from '../../util/SettingsManager';
 
 enum cardSortings {
     Time = 'time',
@@ -52,12 +55,13 @@ export class CollectionCardView extends CollectionSubView() {
     private _oldWheel: HTMLElement | null = null;
     private _dropped = false; // set when a card doc has just moved and the drop method has been called - prevents the pointerUp method from hiding doc decorations (which needs to be done when clicking on a card to animate it to front/center)
     private _setCurDocScript = () => ScriptField.MakeScript('scriptContext.layoutDoc._card_curDoc=this', { scriptContext: 'any' })!;
+    private _draggerRef = React.createRef<HTMLDivElement>();
 
     @observable _forceChildXf = 0;
     @observable _hoveredNodeIndex = -1;
     @observable _docRefs = new ObservableMap<Doc, DocumentView>();
-    @observable _maxRowCount = 10;
     @observable _docDraggedIndex: number = -1;
+    @observable _cursor: CSS.Property.Cursor = 'ew-resize';
 
     constructor(props: SubCollectionViewProps) {
         super(props);
@@ -74,6 +78,12 @@ export class CollectionCardView extends CollectionSubView() {
         // prevent wheel events from passively propagating up through containers and prevents containers from preventDefault which would block scrolling
         ele?.addEventListener('wheel', this.onPassiveWheel, { passive: false });
     };
+    @computed get cardWidth() {
+        return NumCast(this.layoutDoc._cardWidth, 50);
+    }
+    @computed get _maxRowCount() {
+        return Math.ceil(this.cardDeckWidth / this.cardWidth);
+    }
     /**
      * Callback to ensure gpt's text versions of the child docs are updated
      */
@@ -167,6 +177,14 @@ export class CollectionCardView extends CollectionSubView() {
         return this._props.NativeDimScaling?.() || 1;
     }
 
+    @computed get xMargin() {
+        return NumCast(this.layoutDoc._xMargin, Math.max(3, 0.05 * this._props.PanelWidth()));
+    }
+
+    @computed get cardDeckWidth() {
+        return this._props.PanelWidth() - 2 * this.xMargin;
+    }
+
     /**
      * When in quiz mode, randomly selects a document
      */
@@ -505,7 +523,6 @@ export class CollectionCardView extends CollectionSubView() {
             const normalizedItem = item.trim();
             // find the corresponding Doc in the textToDoc map
             const doc = this._textToDoc.get(normalizedItem);
-
             if (doc) {
                 switch (questionType) {
                     case '6':
@@ -513,9 +530,7 @@ export class CollectionCardView extends CollectionSubView() {
                         break;
                     case '1': {
                         const allHotKeys = Doc.MyFilterHotKeys;
-
                         let myTag = '';
-
                         if (tag) {
                             for (let i = 0; i < allHotKeys.length; i++) {
                                 // bcz: CHECK THIS CODE OUT -- SOMETHING CHANGED
@@ -526,7 +541,7 @@ export class CollectionCardView extends CollectionSubView() {
                                 }
                             }
 
-                            if (myTag != '') {
+                            if (myTag) {
                                 doc[myTag] = true;
                             }
                         }
@@ -591,6 +606,26 @@ export class CollectionCardView extends CollectionSubView() {
         }
     });
 
+    cardSizerDown = (e: React.PointerEvent) => {
+        runInAction(() => {
+            this._cursor = 'grabbing';
+        });
+        const batch = UndoManager.StartBatch('card view size');
+        setupMoveUpEvents(
+            this,
+            e,
+            (e: PointerEvent, down: number[], delta: number[]) => {
+                this.layoutDoc._cardWidth = Math.max(10, delta[0] < 0 ? Math.floor(this.cardWidth + delta[0]) : Math.ceil(this.cardWidth + delta[0]));
+                return false;
+            },
+            action(() => {
+                this._cursor = 'ew-resize';
+                batch.end();
+            }),
+            emptyFunction
+        );
+    };
+
     /**
      * turns off the _dropped flag at the end of a drag/drop, or releases the focused Doc if a different Doc is clicked
      */
@@ -701,6 +736,7 @@ export class CollectionCardView extends CollectionSubView() {
                     <div
                         className="collectionCardView-cardwrapper"
                         style={{
+                            gridTemplateColumns: `repeat(${this._maxRowCount}, 1fr)`,
                             gridAutoRows: `${100 / this.numRows}%`,
                             height: `${this.cardSpacing}%`,
                         }}>
@@ -717,6 +753,14 @@ export class CollectionCardView extends CollectionSubView() {
                         {this.flashCardUI(this.curDoc, this.docViewProps, this.answered)}
                     </div>
                 </div>
+
+                <div
+                    className="collectionCardView-cardSizeDragger"
+                    onPointerDown={this.cardSizerDown}
+                    ref={this._draggerRef}
+                    style={{ display: this._props.isContentActive() ? undefined : 'none', cursor: this._cursor, color: SettingsManager.userColor, left: `${this.cardWidth + this.xMargin}px` }}>
+                    <FontAwesomeIcon icon="arrows-alt-h" />
+                </div>
             </div>
         );
     }
diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.tsx b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
index 22ca9dcd7..4028ef479 100644
--- a/src/client/views/pdf/GPTPopup/GPTPopup.tsx
+++ b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
@@ -1,6 +1,6 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Button, IconButton, Type } from '@dash/components';
-import { action, makeObservable, observable } from 'mobx';
+import { action, makeObservable, observable, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { CgClose, CgCornerUpLeft } from 'react-icons/cg';
@@ -37,10 +37,8 @@ export enum GPTQuizType {
     MULTIPLE = 2,
 }
 
-interface GPTPopupProps {}
-
 @observer
-export class GPTPopup extends ObservableReactComponent<GPTPopupProps> {
+export class GPTPopup extends ObservableReactComponent<object> {
     // eslint-disable-next-line no-use-before-define
     static Instance: GPTPopup;
     private messagesEndRef: React.RefObject<HTMLDivElement>;
@@ -188,16 +186,9 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> {
         this.setLoading(true);
         this.setSortDone(false);
 
-        const quizType = this.quizMode;
-
         const selected = DocumentView.SelectedDocs().lastElement();
 
         const questionText = 'Question: ' + StrCast(selected['gptInputText']);
-
-        if (StrCast(selected['gptRubric']) === '') {
-            const rubricText = 'Rubric: ' + (await this.generateRubric(StrCast(selected['gptInputText']), selected));
-        }
-
         const rubricText = 'Rubric: ' + StrCast(selected['gptRubric']);
         const queryText = questionText + ' UserAnswer: ' + this.quizAnswer + '. ' + 'Rubric' + rubricText;
 
@@ -214,7 +205,7 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> {
             this.setLoading(false);
             this.setSortDone(true);
         } catch (err) {
-            console.error('GPT call failed');
+            console.error('GPT call failed', err);
         }
 
         if (this.onQuizRandom) {
@@ -234,7 +225,7 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> {
             doc['gptRubric'] = res;
             return res;
         } catch (err) {
-            console.error('GPT call failed');
+            console.error('GPT call failed', err);
         }
     };
 
@@ -262,7 +253,6 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> {
      * Generates a response to the user's question depending on the type of their question
      */
     generateCard = async () => {
-        console.log(this.chatSortPrompt + 'USER PROMPT');
         this.setLoading(true);
         this.setSortDone(false);
 
@@ -271,10 +261,8 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> {
         }
 
         try {
-            // const res = await gptAPICall(this.sortDesc, GPTCallType.SORT, this.chatSortPrompt);
             const questionType = await gptAPICall(this.chatSortPrompt, GPTCallType.TYPE);
             const questionNumber = questionType.split(' ')[0];
-            console.log(questionType);
             let res = '';
 
             switch (questionNumber) {
@@ -287,10 +275,12 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> {
                     res = await gptAPICall(this.sortDesc, GPTCallType.SORT, this.chatSortPrompt);
                     break;
                 default:
-                    const selected = DocumentView.SelectedDocs().lastElement();
-                    const questionText = StrCast(selected!['gptInputText']);
+                    {
+                        const selected = DocumentView.SelectedDocs().lastElement();
+                        const questionText = StrCast(selected?.gptInputText);
 
-                    res = await gptAPICall(questionText, GPTCallType.INFO, this.chatSortPrompt);
+                        res = await gptAPICall(questionText, GPTCallType.INFO, this.chatSortPrompt);
+                    }
                     break;
             }
 
@@ -308,7 +298,7 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> {
 
                 // Set the extracted explanation to sortRespText
                 this.setSortRespText(explanation);
-                this.conversationArray.push(this.sortRespText);
+                runInAction(() => this.conversationArray.push(this.sortRespText));
                 this.scrollToBottom();
 
                 console.log(res);
@@ -448,7 +438,7 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> {
 
     private getPreviewUrl = (source: string) => source.split('.').join('_m.');
 
-    constructor(props: GPTPopupProps) {
+    constructor(props: object) {
         super(props);
         makeObservable(this);
         GPTPopup.Instance = this;
@@ -515,18 +505,25 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> {
         </div>
     );
 
-    handleKeyPress = async (e: React.KeyboardEvent, isSort: boolean) => {
+    @action
+    handleKeyPress = (e: React.KeyboardEvent, isSort: boolean) => {
         if (e.key === 'Enter') {
             e.stopPropagation();
 
             if (isSort) {
                 this.conversationArray.push(this.chatSortPrompt);
-                await this.generateCard();
-                this.chatSortPrompt = '';
+                this.generateCard().then(
+                    action(() => {
+                        this.chatSortPrompt = '';
+                    })
+                );
             } else {
                 this.conversationArray.push(this.quizAnswer);
-                await this.generateQuiz();
-                this.quizAnswer = '';
+                this.generateQuiz().then(
+                    action(() => {
+                        this.quizAnswer = '';
+                    })
+                );
             }
 
             this.scrollToBottom();
-- 
cgit v1.2.3-70-g09d2


From 519a28c4ef5b5a70d29377c9baed50b455459ebd Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 28 Jan 2025 12:02:37 -0500
Subject: card view cleanup. moved 'pile' into shiftclick on Perspective
 dropdown. fixed tags sorting.

---
 .../src/components/Dropdown/Dropdown.tsx           | 371 ++++++++++-----------
 .../components/src/components/ListBox/ListBox.tsx  |  90 ++---
 .../src/components/ListItem/ListItem.tsx           | 197 +++++------
 src/client/util/CurrentUserUtils.ts                |   4 +-
 src/client/views/animationtimeline/Timeline.tsx    |  14 +-
 .../views/collections/CollectionCardDeckView.tsx   |   3 +-
 src/client/views/global/globalScripts.ts           |  36 +-
 src/client/views/nodes/FontIconBox/FontIconBox.tsx |   9 +-
 8 files changed, 313 insertions(+), 411 deletions(-)

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

diff --git a/packages/components/src/components/Dropdown/Dropdown.tsx b/packages/components/src/components/Dropdown/Dropdown.tsx
index d9fec5e9d..0953f412c 100644
--- a/packages/components/src/components/Dropdown/Dropdown.tsx
+++ b/packages/components/src/components/Dropdown/Dropdown.tsx
@@ -1,31 +1,32 @@
-import React, { useEffect, useState } from 'react'
-import { FaCaretDown, FaCaretLeft, FaCaretRight, FaCaretUp } from 'react-icons/fa'
-import { Popup, PopupTrigger } from '..'
-import { Colors, IGlobalProps, Placement, Type, getFontSize, getHeight, isDark , getFormLabelSize } from '../../global'
-import { IconButton } from '../IconButton'
-import { ListBox } from '../ListBox'
-import { IListItemProps, ListItem } from '../ListItem'
-import './Dropdown.scss'
-import { Tooltip } from '@mui/material'
+import React, { useState } from 'react';
+import { FaCaretDown, FaCaretLeft, FaCaretRight, FaCaretUp } from 'react-icons/fa';
+import { Popup, PopupTrigger } from '..';
+import { Colors, IGlobalProps, Placement, Type, getFontSize, getHeight, isDark, getFormLabelSize } from '../../global';
+import { IconButton } from '../IconButton';
+import { ListBox } from '../ListBox';
+import { IListItemProps, ListItem } from '../ListItem';
+import './Dropdown.scss';
+import { Tooltip } from '@mui/material';
 
 export enum DropdownType {
-  SELECT = "select",
-  CLICK = "click"
+    SELECT = 'select',
+    CLICK = 'click',
 }
 
 export interface IDropdownProps extends IGlobalProps {
-  items: IListItemProps[]
-  placement?: Placement
-  dropdownType: DropdownType
-  title?: string
-  closeOnSelect?: boolean;
-  iconProvider?: (active:boolean, placement?:Placement) => JSX.Element,
-  selectedVal?: string,
-  setSelectedVal?: (val: string | number) => unknown,
-  maxItems?: number,
-  uppercase?: boolean,
-  activeChanged?: (isOpen:boolean) => void,
-  onItemDown?: (e:React.PointerEvent, val:number | string) => boolean, // returns whether to select item
+    items: IListItemProps[];
+    placement?: Placement;
+    dropdownType: DropdownType;
+    title?: string;
+    toolTip?: string;
+    closeOnSelect?: boolean;
+    iconProvider?: (active: boolean, placement?: Placement) => JSX.Element;
+    selectedVal?: string;
+    setSelectedVal?: (val: string | number, e?: React.MouseEvent) => unknown;
+    maxItems?: number;
+    uppercase?: boolean;
+    activeChanged?: (isOpen: boolean) => void;
+    onItemDown?: (e: React.PointerEvent, val: number | string) => boolean; // returns whether to select item
 }
 
 /**
@@ -37,189 +38,159 @@ export interface IDropdownProps extends IGlobalProps {
  * Look at: import Select from "react-select";
  */
 export const Dropdown = (props: IDropdownProps) => {
-  const {
-    size,
-    height,
-    maxItems,
-    items,
-    dropdownType,
-    selectedVal,
-    setSelectedVal,
-    iconProvider,
-    placement = 'bottom-start',
-    tooltip,
-    tooltipPlacement = 'top',
-    inactive,
-    color = Colors.MEDIUM_BLUE,
-    background,
-    closeOnSelect,
-    title = "Dropdown",
-    type, 
-    width,
-    formLabel,
-    formLabelPlacement,
-    fillWidth = true,
-    onItemDown,
-    uppercase
-  } = props
+    const {
+        size,
+        height,
+        maxItems,
+        items,
+        dropdownType,
+        selectedVal,
+        toolTip,
+        setSelectedVal,
+        iconProvider,
+        placement = 'bottom-start',
+        tooltip,
+        tooltipPlacement = 'top',
+        inactive,
+        color = Colors.MEDIUM_BLUE,
+        background,
+        closeOnSelect,
+        title = 'Dropdown',
+        type,
+        width,
+        formLabel,
+        formLabelPlacement,
+        fillWidth = true,
+        onItemDown,
+        uppercase,
+    } = props;
 
-  const [active, setActive] = useState<boolean>(false)
-  const itemsMap = new Map();
-  items.forEach((item) => {
-    itemsMap.set(item.val, item)
-  })
+    const [active, setActive] = useState<boolean>(false);
+    const itemsMap = new Map();
+    items.forEach(item => {
+        itemsMap.set(item.val, item);
+    });
 
-  const getBorderColor = (): Colors | string | undefined => {
-    switch(type){
-      case Type.PRIM:
-        return undefined;
-      case Type.SEC:
-        return color;
-      case Type.TERT:
-        if (active) return color;
-        else return color;
-    }
-  }
+    const getBorderColor = (): Colors | string | undefined => {
+        switch (type) {
+            case Type.PRIM:
+                return undefined;
+            case Type.SEC:
+                return color;
+            case Type.TERT:
+                if (active) return color;
+                else return color;
+        }
+    };
 
-  const defaultProperties: React.CSSProperties = {
-    height: getHeight(height, size),
-    width: fillWidth ? '100%' : width,
-    fontWeight: 500,
-    fontSize: getFontSize(size),
-    fontFamily: 'sans-serif',
-    textTransform: uppercase ? 'uppercase' : undefined,
-    borderColor: getBorderColor(),
-    background,
-    color: color && background? color :  type == (Type.TERT) ? isDark(color) ? Colors.WHITE : Colors.BLACK : color
-  }
+    const defaultProperties: React.CSSProperties = {
+        height: getHeight(height, size),
+        width: fillWidth ? '100%' : width,
+        fontWeight: 500,
+        fontSize: getFontSize(size),
+        fontFamily: 'sans-serif',
+        textTransform: uppercase ? 'uppercase' : undefined,
+        borderColor: getBorderColor(),
+        background,
+        color: color && background ? color : type == Type.TERT ? (isDark(color) ? Colors.WHITE : Colors.BLACK) : color,
+    };
 
-  const backgroundProperties: React.CSSProperties = {
-    background: background ?? color
-  }
+    const backgroundProperties: React.CSSProperties = {
+        background: background ?? color,
+    };
 
-  const getCaretDirection = (active: boolean, placement:Placement = 'left'): JSX.Element => {
-    if (iconProvider) return iconProvider(active, placement);
-    switch (placement) {
-      case 'bottom':
-        if (active) return <FaCaretUp/>
-        return <FaCaretDown/>
-      case 'right':
-        if (active) return <FaCaretLeft/>
-        return <FaCaretRight/>
-      case 'top':
-        if (active) return <FaCaretDown/>
-        return <FaCaretUp/>
-      default: 
-        if (active) return <FaCaretUp/>
-        return <FaCaretDown/>
-    }
-  }
+    const getCaretDirection = (active: boolean, placement: Placement = 'left'): JSX.Element => {
+        if (iconProvider) return iconProvider(active, placement);
+        switch (placement) {
+            case 'bottom':
+                if (active) return <FaCaretUp />;
+                return <FaCaretDown />;
+            case 'right':
+                if (active) return <FaCaretLeft />;
+                return <FaCaretRight />;
+            case 'top':
+                if (active) return <FaCaretDown />;
+                return <FaCaretUp />;
+            default:
+                if (active) return <FaCaretUp />;
+                return <FaCaretDown />;
+        }
+    };
 
-  const getToggle = () => {
-    switch (dropdownType) {
-      case DropdownType.SELECT:
-        return (
-          <div
-            className={`dropdown-toggle${!selectedVal?"-mini":""} ${type} ${inactive && 'inactive'}`}
-            style={{...defaultProperties, height: getHeight(height, size), width: width }}
-          >
-            {selectedVal && (
-              <ListItem 
-                size={size}
-                {...itemsMap.get(selectedVal)} 
-                style={{ color: defaultProperties.color, background: defaultProperties.background}} 
-                inactive 
-              />
-            )}
-            <div className="toggle-caret">
-              <IconButton
+    const getToggle = () => {
+        switch (dropdownType) {
+            case DropdownType.SELECT:
+                return (
+                    <div className={`dropdown-toggle${!selectedVal ? '-mini' : ''} ${type} ${inactive && 'inactive'}`} style={{ ...defaultProperties, height: getHeight(height, size), width: width }}>
+                        {selectedVal && <ListItem size={size} {...itemsMap.get(selectedVal)} style={{ color: defaultProperties.color, background: defaultProperties.background }} inactive />}
+                        <div className="toggle-caret">
+                            <IconButton size={size} icon={getCaretDirection(active, placement)} color={defaultProperties.color} inactive />
+                        </div>
+                        <div className={`background ${active && 'active'}`} style={{ ...backgroundProperties }} />
+                    </div>
+                );
+            case DropdownType.CLICK:
+            default:
+                return (
+                    <div className={`dropdown-toggle${!selectedVal ? '-mini' : ''} ${type} ${inactive && 'inactive'}`} style={{ ...defaultProperties, height: getHeight(height, size), width: width }}>
+                        <ListItem val="title" text={title} size={size} style={{ color: defaultProperties.color, background: defaultProperties.backdropFilter }} inactive />
+                        <div className="toggle-caret">
+                            <IconButton size={size} icon={getCaretDirection(active, placement)} color={defaultProperties.color} inactive />
+                        </div>
+                        <div className={`background ${active && 'active'}`} style={{ ...backgroundProperties }} />
+                    </div>
+                );
+        }
+    };
+
+    const setActiveChanged = (active: boolean) => {
+        setActive(active);
+        props.activeChanged?.(active);
+    };
+
+    const dropdown: JSX.Element = (
+        <div className="dropdown-container">
+            <Popup
+                toggle={
+                    <Tooltip disableInteractive={true} arrow={true} placement={tooltipPlacement} title={toolTip || (itemsMap.get(selectedVal)?.text ?? title)}>
+                        {getToggle()}
+                    </Tooltip>
+                }
+                placement={placement}
+                tooltip={tooltip}
+                tooltipPlacement={tooltipPlacement}
+                trigger={PopupTrigger.CLICK}
+                isOpen={active}
+                setOpen={setActiveChanged}
                 size={size}
-                icon={getCaretDirection(active,placement)}
-                color={defaultProperties.color}
-                inactive
-              />
-            </div>
-            <div className={`background ${active && 'active'}`} style={{...backgroundProperties}}/>
-          </div>
-        )
-      case DropdownType.CLICK:
-      default:
-        return (
-          <div
-            className={`dropdown-toggle${!selectedVal?"-mini":""} ${type} ${inactive && 'inactive'}`}
-            style={{...defaultProperties, height: getHeight(height, size), width: width }}
-          >
-            <ListItem val='title'
-                text={title} 
-                size={size} 
-                style={{ color: defaultProperties.color, background: defaultProperties.backdropFilter}} 
-                inactive 
+                fillWidth={true}
+                color={color}
+                popup={
+                    <ListBox
+                        maxItems={maxItems}
+                        items={items}
+                        color={color}
+                        onItemDown={onItemDown}
+                        selectedVal={selectedVal}
+                        setSelectedVal={(val, e) => {
+                            setSelectedVal?.(val, e);
+                            closeOnSelect && setActive(false);
+                        }}
+                        size={size}
+                    />
+                }
             />
-            <div className="toggle-caret">
-              <IconButton
-                size={size}
-                icon={getCaretDirection(active,placement)}
-                color={defaultProperties.color}
-                inactive
-              />
-            </div>
-            <div className={`background ${active && 'active'}`} style={{...backgroundProperties}}/>
-          </div>
-        )
-    }
-  }
-  
-  const setActiveChanged = (active:boolean) => {
-    setActive(active);
-    props.activeChanged?.(active);
-  }
-
-  const dropdown: JSX.Element = 
-  (
-    <div
-      className="dropdown-container"
-    >
-      <Popup
-        toggle={
-          <Tooltip disableInteractive={true} arrow={true} placement={tooltipPlacement} title={itemsMap.get(selectedVal) ? itemsMap.get(selectedVal).text : title}>
-            {getToggle()}
-          </Tooltip>
-        }
-        placement={placement}
-        tooltip={tooltip}
-        tooltipPlacement={tooltipPlacement}
-        trigger={PopupTrigger.CLICK}
-        isOpen={active}
-        setOpen={setActiveChanged}
-        size={size}
-        fillWidth={true}
-        color={color}
-        popup={
-          <ListBox
-            maxItems={maxItems}
-            items={items}
-            color={color}
-            onItemDown={onItemDown}
-            selectedVal={selectedVal}
-            setSelectedVal={val => {
-              setSelectedVal?.(val);
-              closeOnSelect && setActive(false);
-            }}
-            size={size}
-          />
-        } 
-        />
-    </div>
-  )
+        </div>
+    );
 
-  return (
-    formLabel ? 
-      <div className={`form-wrapper ${formLabelPlacement}`}
-style={{ width: fillWidth ? '100%' : undefined}}>
-        <div className={'formLabel'} style={{fontSize: getFormLabelSize(size)}}>{formLabel}</div>
-        {dropdown}
-      </div>
-    :
-      dropdown
-  )
-}
+    return formLabel ? (
+        <div className={`form-wrapper ${formLabelPlacement}`} style={{ width: fillWidth ? '100%' : undefined }}>
+            <div className={'formLabel'} style={{ fontSize: getFormLabelSize(size) }}>
+                {formLabel}
+            </div>
+            {dropdown}
+        </div>
+    ) : (
+        dropdown
+    );
+};
diff --git a/packages/components/src/components/ListBox/ListBox.tsx b/packages/components/src/components/ListBox/ListBox.tsx
index abdfd38f3..aa5eb6b44 100644
--- a/packages/components/src/components/ListBox/ListBox.tsx
+++ b/packages/components/src/components/ListBox/ListBox.tsx
@@ -1,15 +1,15 @@
-import React, { ReactText } from 'react'
-import { IListItemProps, ListItem } from '../ListItem'
-import './ListBox.scss'
-import { Colors, IGlobalProps, isDark , getFormLabelSize } from '../../global'
+import React from 'react';
+import { IListItemProps, ListItem } from '../ListItem';
+import './ListBox.scss';
+import { Colors, IGlobalProps } from '../../global';
 
 export interface IListBoxProps extends IGlobalProps {
-  items: IListItemProps[]
-  filter?: string
-  selectedVal?: string | number
-  setSelectedVal?: (val: string | number) => unknown
-  maxItems?: number
-  onItemDown?: (e:React.PointerEvent, val:number|string) => void
+    items: IListItemProps[];
+    filter?: string;
+    selectedVal?: string | number;
+    setSelectedVal?: (val: string | number, e?: React.MouseEvent) => unknown;
+    maxItems?: number;
+    onItemDown?: (e: React.PointerEvent, val: number | string) => void;
 }
 
 /**
@@ -21,56 +21,24 @@ export interface IListBoxProps extends IGlobalProps {
  * Look at: import Select from "react-select";
  */
 export const ListBox = (props: IListBoxProps) => {
-  const {
-    items,
-    selectedVal,
-    setSelectedVal,
-    filter,
-    onItemDown,
-    color = Colors.MEDIUM_BLUE
-  } = props
+    const { items, selectedVal, setSelectedVal, filter, onItemDown, color = Colors.MEDIUM_BLUE } = props;
 
-  const getListItem = (
-    item: IListItemProps,
-    ind: number,
-    selected: boolean
-  ): JSX.Element => {
+    const getListItem = (item: IListItemProps, ind: number, selected: boolean): JSX.Element => {
+        return <ListItem key={ind} ind={ind} onItemDown={onItemDown} selected={selected} color={color} setSelectedVal={setSelectedVal} onClick={item.onClick} {...item} />;
+    };
+    const itemElements: JSX.Element[] = [];
+    items.forEach((item, ind) => {
+        if (filter) {
+            if (filter.toLowerCase() === item.text?.substring(0, filter.length).toLowerCase()) {
+                itemElements.push(getListItem(item, ind, item.val === selectedVal));
+            }
+        } else {
+            itemElements.push(getListItem(item, ind, item.val === selectedVal));
+        }
+    });
     return (
-      <ListItem
-        key={ind}
-        ind={ind}
-        onItemDown={onItemDown}
-        selected={selected}
-        color={color}
-        setSelectedVal={setSelectedVal}
-        onClick={item.onClick}
-        {...item}
-      />
-    )
-  }
-  let itemElements: JSX.Element[] = []
-  items.forEach((item, ind) => {
-    if (filter) {
-      if (
-        filter.toLowerCase() ===
-        item.text?.substring(0, filter.length).toLowerCase()
-      ) {
-        itemElements.push(
-          getListItem(item, ind, item.val === selectedVal)
-        )
-      }
-    } else {
-      itemElements.push(
-        getListItem(item, ind, item.val === selectedVal)
-      )
-    }
-  })
-  return (
-    <div
-      className="listBox-container"
-      style={{ color: color }}
-    >
-      {itemElements}
-    </div>
-  )
-}
+        <div className="listBox-container" style={{ color: color }}>
+            {itemElements}
+        </div>
+    );
+};
diff --git a/packages/components/src/components/ListItem/ListItem.tsx b/packages/components/src/components/ListItem/ListItem.tsx
index d76c84b3e..e04c6fbee 100644
--- a/packages/components/src/components/ListItem/ListItem.tsx
+++ b/packages/components/src/components/ListItem/ListItem.tsx
@@ -1,25 +1,25 @@
-import React, { useState } from 'react'
-import * as fa from 'react-icons/fa'
-import { getFontSize, IGlobalProps, Type , getFormLabelSize, getHeight } from '../../global'
-import { Size } from '../../global/globalEnums'
-import { IconButton } from '../IconButton'
-import { ListBox } from '../ListBox'
-import { Popup, PopupTrigger } from '../Popup'
-import './ListItem.scss'
+import React, { useState } from 'react';
+import * as fa from 'react-icons/fa';
+import { getFontSize, IGlobalProps, Type, getHeight } from '../../global';
+import { Size } from '../../global/globalEnums';
+import { IconButton } from '../IconButton';
+import { ListBox } from '../ListBox';
+import { Popup, PopupTrigger } from '../Popup';
+import './ListItem.scss';
 
 export interface IListItemProps extends IGlobalProps {
-  ind?: number
-  text?: string
-  val: string | number
-  icon?: JSX.Element
-  description?: string
-  shortcut?: string
-  items?: IListItemProps[]
-  selected?: boolean
-  setSelectedVal?: (val: string | number) => unknown
-  onClick?: () => void
-  onItemDown?: (e:React.PointerEvent, val:string| number) => void
-  uppercase?: boolean
+    ind?: number;
+    text?: string;
+    val: string | number;
+    icon?: JSX.Element;
+    description?: string;
+    shortcut?: string;
+    items?: IListItemProps[];
+    selected?: boolean;
+    setSelectedVal?: (val: string | number, e?: React.MouseEvent) => unknown;
+    onClick?: () => void;
+    onItemDown?: (e: React.PointerEvent, val: string | number) => void;
+    uppercase?: boolean;
 }
 
 /**
@@ -31,104 +31,67 @@ export interface IListItemProps extends IGlobalProps {
  * Look at: import Select from "react-select";
  */
 export const ListItem = (props: IListItemProps) => {
-  const {
-    ind,
-    val,
-    description,
-    text,
-    shortcut,
-    items,
-    icon,
-    selected,
-    setSelectedVal,
-    onClick,
-    onItemDown,
-    inactive,
-    size = Size.SMALL,
-    style,
-    color,
-    background,
-    uppercase
-  } = props
+    const { val, description, text, shortcut, items, icon, selected, setSelectedVal, onClick, onItemDown, inactive, size = Size.SMALL, style, color, background, uppercase } = props;
 
-  const [isHovered, setIsHovered] = useState<boolean>(false);
+    const [isHovered, setIsHovered] = useState<boolean>(false);
 
-  let listItem:JSX.Element = (
-    <div
-      tabIndex={-1}
-      className="listItem-container"
-      onPointerDown={(e) => onItemDown?.(e, val) && setSelectedVal?.(val)}
-      onClick={(e: React.MouseEvent) => {
-        if (!items) {
-          !inactive && onClick?.()
-          !inactive && onClick && e.stopPropagation()
-          setSelectedVal?.(val)
-        }
-      }}
-      style={{
-        minHeight: getHeight(undefined, size),
-        userSelect: 'none',
-        ...style
-      }}
-      onPointerEnter={() => {
-        setIsHovered(true)
-      }}
-      onPointerLeave={() => {
-        setIsHovered(false)
-      }}
-    >
-      <div className="listItem-top">
-        <div className="content" 
-         style={{
-          fontSize: getFontSize(size), 
-          color: style?.color ? style.color : color
-         }}>
-          {icon}
-          <div className="text" style={{
-            textTransform: uppercase ? 'uppercase' : undefined
-          }}>{text}</div>
+    const listItem: JSX.Element = (
+        <div
+            tabIndex={-1}
+            className="listItem-container"
+            onPointerDown={e => onItemDown?.(e, val) && setSelectedVal?.(val, e)}
+            onClick={(e: React.MouseEvent) => {
+                if (!items) {
+                    !inactive && onClick?.();
+                    !inactive && onClick && e.stopPropagation();
+                    setSelectedVal?.(val, e);
+                }
+            }}
+            style={{
+                minHeight: getHeight(undefined, size),
+                userSelect: 'none',
+                ...style,
+            }}
+            onPointerEnter={() => {
+                setIsHovered(true);
+            }}
+            onPointerLeave={() => {
+                setIsHovered(false);
+            }}>
+            <div className="listItem-top">
+                <div
+                    className="content"
+                    style={{
+                        fontSize: getFontSize(size),
+                        color: style?.color ? style.color : color,
+                    }}>
+                    {icon}
+                    <div
+                        className="text"
+                        style={{
+                            textTransform: uppercase ? 'uppercase' : undefined,
+                        }}>
+                        {text}
+                    </div>
+                </div>
+                {shortcut && !inactive && (
+                    <div className="shortcut" color={style?.color ? style.color : color}>
+                        {shortcut}
+                    </div>
+                )}
+                {items && !inactive && <IconButton type={Type.PRIM} size={Size.SMALL} icon={<fa.FaCaretRight />} color={style?.color ? style.color : color} background={background} inactive />}
+            </div>
+            {description && !inactive && <div className="listItem-description">{description}</div>}
+            <div
+                className="listItem-background"
+                style={{
+                    background: background ? background : style?.color ? style.color : color,
+                    filter: selected ? 'opacity(0.3)' : isHovered && !inactive ? 'opacity(0.2)' : 'opacity(0)',
+                }}
+            />
         </div>
-        {shortcut && !inactive && (
-          <div 
-            className="shortcut"
-            color={style?.color ? style.color : color}
-          >
-            {shortcut}
-          </div>
-        )}
-        {items && !inactive && (
-          <IconButton 
-            type={Type.PRIM}
-            size={Size.SMALL} 
-            icon={<fa.FaCaretRight/>}
-            color={style?.color ? style.color : color}
-            background={background}
-            inactive
-          />
-        )}
-      </div>
-      {description && !inactive && (
-        <div className="listItem-description">{description}</div>
-      )}
-      <div className="listItem-background" 
-        style={{
-          background: background ? background : style?.color ? style.color : color,
-          filter: selected ? 'opacity(0.3)' : isHovered && !inactive ? 'opacity(0.2)' : 'opacity(0)'
-        }}
-      />
-    </div>
-)
+    );
 
-  if (items && !inactive) return <Popup
-    placement={'right'}
-    toggle={listItem}
-    color={color}
-    background={background}
-    trigger={PopupTrigger.CLICK}
-    popup={
-      <ListBox color={color} background={background} items={items}/>
-    }
-    fillWidth={true}
-  />
-  else return <>{listItem}</>
-}
+    if (items && !inactive) return <Popup placement={'right'} toggle={listItem} color={color} background={background} trigger={PopupTrigger.CLICK} popup={<ListBox color={color} background={background} items={items} />} fillWidth={true} />;
+    else return <>{listItem}</>;
+};
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index b41fd09dc..7c36a82f2 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -694,10 +694,8 @@ 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: "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: "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_);}'} },
@@ -814,7 +812,7 @@ pie title Minerals in my tap water
                     CollectionViewType.Masonry, CollectionViewType.Multicolumn, CollectionViewType.Multirow, CollectionViewType.Linear, 
                     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, _readOnly_); }'}},
+              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_); }'} },
diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx
index d9ff21035..15683ebf2 100644
--- a/src/client/views/animationtimeline/Timeline.tsx
+++ b/src/client/views/animationtimeline/Timeline.tsx
@@ -16,6 +16,7 @@ import { RegionHelpers } from './Region';
 import './Timeline.scss';
 import { TimelineOverview } from './TimelineOverview';
 import { Track } from './Track';
+import { Id } from '../../../fields/FieldSymbols';
 
 /**
  * Timeline class controls most of timeline functions besides individual region and track mechanism. Main functions are 
@@ -56,7 +57,7 @@ export class Timeline extends ObservableReactComponent<FieldViewProps> {
     private DEFAULT_CONTAINER_HEIGHT: number = 330;
     private MIN_CONTAINER_HEIGHT: number = 205;
 
-    constructor(props: any) {
+    constructor(props: FieldViewProps) {
         super(props);
         makeObservable(this);
     }
@@ -89,7 +90,7 @@ export class Timeline extends ObservableReactComponent<FieldViewProps> {
      */
     @computed
     private get children(): Doc[] {
-        const annotatedDoc = [DocumentType.IMG, DocumentType.VID, DocumentType.PDF, DocumentType.MAP].includes(StrCast(this._props.Document.type) as any);
+        const annotatedDoc = [DocumentType.IMG, DocumentType.VID, DocumentType.PDF, DocumentType.MAP].includes(StrCast(this._props.Document.type) as unknown as DocumentType);
         if (annotatedDoc) {
             return DocListCast(this._props.Document[Doc.LayoutFieldKey(this._props.Document) + '_annotations']);
         }
@@ -272,9 +273,9 @@ export class Timeline extends ObservableReactComponent<FieldViewProps> {
      * for displaying time to standard min:sec
      */
     @action
-    toReadTime = (time: number): string => {
-        time = time / 1000;
-        const inSeconds = Math.round(time * 100) / 100;
+    toReadTime = (timeIn: number): string => {
+        const timeSecs = timeIn / 1000;
+        const inSeconds = Math.round(timeSecs * 100) / 100;
 
         const min = Math.floor(inSeconds / 60);
         const sec = Math.round((inSeconds % 60) * 100) / 100;
@@ -552,6 +553,7 @@ export class Timeline extends ObservableReactComponent<FieldViewProps> {
                             <div key="timeline_trackbox" className="trackbox" ref={this._trackbox} style={{ width: `${this._totalLength}px` }}>
                                 {[...this.children, this._props.Document].map(doc => (
                                     <Track
+                                        key={doc[Id]}
                                         ref={ref => this.mapOfTracks.push(ref)}
                                         timeline={this}
                                         animatedDoc={doc}
@@ -570,7 +572,7 @@ export class Timeline extends ObservableReactComponent<FieldViewProps> {
                         <div className="currentTime">Current: {this.getCurrentTime()}</div>
                         <div key="timeline_title" className="title-container" ref={this._titleContainer}>
                             {[...this.children, this._props.Document].map(doc => (
-                                <div style={{ height: `${this._titleHeight}px` }} className="datapane" onPointerOver={() => Doc.BrushDoc(doc)} onPointerOut={() => Doc.UnBrushDoc(doc)}>
+                                <div key={doc[Id]} style={{ height: `${this._titleHeight}px` }} className="datapane" onPointerOver={() => Doc.BrushDoc(doc)} onPointerOut={() => Doc.UnBrushDoc(doc)}>
                                     <p>{StrCast(doc.title)}</p>
                                 </div>
                             ))}
diff --git a/src/client/views/collections/CollectionCardDeckView.tsx b/src/client/views/collections/CollectionCardDeckView.tsx
index 800eb4914..1bca68846 100644
--- a/src/client/views/collections/CollectionCardDeckView.tsx
+++ b/src/client/views/collections/CollectionCardDeckView.tsx
@@ -5,7 +5,7 @@ import * as React from 'react';
 import * as CSS from 'csstype';
 import { ClientUtils, DashColor, imageUrlToBase64, returnFalse, returnNever, returnZero, setupMoveUpEvents } from '../../../ClientUtils';
 import { emptyFunction } from '../../../Utils';
-import { Doc, DocListCast, Opt } from '../../../fields/Doc';
+import { Doc, DocListCast, Opt, StrListCast } from '../../../fields/Doc';
 import { Animation, DocData } from '../../../fields/DocSymbols';
 import { Id } from '../../../fields/FieldSymbols';
 import { List } from '../../../fields/List';
@@ -314,6 +314,7 @@ export class CollectionCardView extends CollectionSubView() {
                         case cardSortings.Chat: return [NumCast(docA.chatIndex, 9999),                        NumCast(docB.chatIndex,9999)];
                         case cardSortings.Time: return [DateCast(docA.author_date)?.date ?? Date.now(),       DateCast(docB.author_date)?.date ?? Date.now()];
                         case cardSortings.Color:return [DashColor(StrCast(docA.backgroundColor)).hsv().hue(), DashColor(StrCast(docB.backgroundColor)).hsv().hue()];
+                        case cardSortings.Tag:  return [StrListCast(docA.tags).join(""),                      StrListCast(docB.tags).join("")];
                     }
                 })(); //prettier-ignore
                 return (typeA < typeB ? -1 : typeA > typeB ? 1 : 0) * (isDesc ? 1 : -1);
diff --git a/src/client/views/global/globalScripts.ts b/src/client/views/global/globalScripts.ts
index 1738802b7..542417531 100644
--- a/src/client/views/global/globalScripts.ts
+++ b/src/client/views/global/globalScripts.ts
@@ -48,10 +48,20 @@ ScriptingGlobals.add(function IsNoneSelected() {
 
 // toggle: Set overlay status of selected document
 // eslint-disable-next-line prefer-arrow-callback
-ScriptingGlobals.add(function setView(view: string, getSelected: boolean) {
-    if (getSelected) return DocumentView.SelectedDocs();
-    const selected = DocumentView.SelectedDocs().lastElement();
-    selected ? (selected._type_collection = view) : console.log('[FontIconBox.tsx] changeView failed');
+ScriptingGlobals.add(function setView(view: string, shiftKey: boolean, checkResult?: boolean) {
+    if (checkResult) return DocumentView.SelectedDocs();
+    const selected = DocumentView.Selected().lastElement();
+    if (selected) {
+        if (shiftKey) {
+            const newCol = Doc.MakeEmbedding(selected.Document);
+            newCol._type_collection = view;
+            selected._props.addDocTab?.(newCol, OpenWhere.addRight);
+        } else {
+            selected.Document._type_collection = view;
+        }
+    } else {
+        console.log('[FontIconBox.tsx] changeView failed');
+    }
     return undefined;
 });
 
@@ -141,7 +151,7 @@ ScriptingGlobals.add(function toggleOverlay(checkResult?: boolean) {
 
 // eslint-disable-next-line prefer-arrow-callback
 ScriptingGlobals.add(function showFreeform(
-    attr: 'flashcards' | 'hcenter' | 'vcenter' | 'grid' | 'snaplines' | 'clusters' | 'viewAll' | 'fitOnce' | 'time' | 'docType' | 'color' | 'chat' | 'up' | 'down' | 'pile' | 'toggle-chat' | 'toggle-tags' | 'tag',
+    attr: 'flashcards' | 'hcenter' | 'vcenter' | 'grid' | 'snaplines' | 'clusters' | 'viewAll' | 'fitOnce' | 'time' | 'docType' | 'color' | 'chat' | 'up' | 'down' | 'toggle-chat' | 'toggle-tags' | 'tag',
     checkResult?: boolean,
     persist?: boolean
 ) {
@@ -152,7 +162,7 @@ ScriptingGlobals.add(function showFreeform(
     }
 
     // prettier-ignore
-    const map: Map<'flashcards' | 'hcenter' | 'vcenter' | 'grid' | 'snaplines' | 'clusters' | 'viewAll' | 'fitOnce' | 'time' | 'docType' | 'color' | 'chat' | 'up' | 'down' | 'pile' | 'toggle-chat' | 'toggle-tags' | 'tag', 
+    const map: Map<'flashcards' | 'hcenter' | 'vcenter' | 'grid' | 'snaplines' | 'clusters' | 'viewAll' | 'fitOnce' | 'time' | 'docType' | 'color' | 'chat' | 'up' | 'down'| 'toggle-chat' | 'toggle-tags' | 'tag', 
     { 
         waitForRender?: boolean; 
         checkResult: (doc: Doc) => boolean; 
@@ -242,20 +252,6 @@ ScriptingGlobals.add(function showFreeform(
                 doc.showChildTags = !doc.showChildTags;
             },
         }],
-        ['pile', {
-            checkResult: (doc: Doc) => doc._type_collection == CollectionViewType.Freeform,
-            setDoc: (doc: Doc, dv: DocumentView) => {
-                const newCol = Docs.Create.CarouselDocument(DocListCast(doc[Doc.LayoutFieldKey(doc)]), {
-                    title: doc.title + "_carousel",
-                    _width: 250,
-                    _height: 200,
-                    _layout_fitWidth: false,
-                    _layout_autoHeight: true,
-                    childFilters: new List<string>(StrListCast(doc.childFilters))
-                });
-                dv._props.addDocTab?.(newCol, OpenWhere.addRight);
-            },
-        }],
     ]);
 
     if (checkResult) {
diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
index 60b2a7519..f58862028 100644
--- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
@@ -160,6 +160,7 @@ export class FontIconBox extends ViewBoxBaseComponent<ButtonProps>() {
             buttonList,
             jsx: undefined,
             selectedVal: script(),
+            toolTip: 'Set text font',
             getStyle: (val: string) => ({ fontFamily: val }),
         };
     };
@@ -174,6 +175,7 @@ export class FontIconBox extends ViewBoxBaseComponent<ButtonProps>() {
                   buttonList: buttonList.filter(value => !Doc.noviceMode || !noviceList.length || noviceList.includes(value as CollectionViewType)),
                   getStyle: undefined,
                   selectedVal: StrCast(selected[0]._type_collection),
+                  toolTip: 'change view type (press Shift to add as a new view)',
               }
             : {
                   jsx: selected.length ? (
@@ -205,11 +207,11 @@ export class FontIconBox extends ViewBoxBaseComponent<ButtonProps>() {
     @computed get dropdownListButton() {
         const script = ScriptCast(this.Document.script);
         const selectedFunc = () => script?.script.run({ this: this.Document, value: '', _readOnly_: true }).result as string;
-        const { buttonList, selectedVal, getStyle, jsx } = (() => {
+        const { buttonList, selectedVal, getStyle, jsx, toolTip } = (() => {
             switch (this.Document.title) {
                 case 'Font':         return this.handleFontDropdown(selectedFunc, this.buttonList);
                 case 'Perspective':  return this.handleViewDropdown(script, this.buttonList);
-                default:             return { buttonList: this.buttonList, selectedVal: selectedFunc(), jsx: undefined, getStyle: undefined };
+                default:             return { buttonList: this.buttonList, selectedVal: selectedFunc(), toolTip: undefined, jsx: undefined, getStyle: undefined };
             } // prettier-ignore
         })();
         if (jsx) return jsx;
@@ -225,9 +227,10 @@ export class FontIconBox extends ViewBoxBaseComponent<ButtonProps>() {
         return (
             <Dropdown
                 selectedVal={selectedVal}
-                setSelectedVal={undoable(value => script.script.run({ this: this.Document, value }), `dropdown select ${this.label}`)}
+                setSelectedVal={undoable((value, e) => script.script.run({ this: this.Document, value, shiftKey: e.shiftKey }), `dropdown select ${this.label}`)}
                 color={SnappingManager.userColor}
                 background={SnappingManager.userVariantColor}
+                toolTip={toolTip}
                 type={Type.TERT}
                 closeOnSelect={false}
                 dropdownType={DropdownType.SELECT}
-- 
cgit v1.2.3-70-g09d2


From a496c583f375fe5e118f786b1244c42bc8a34fec Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 28 Jan 2025 14:20:40 -0500
Subject: fixed flashcard practice mode.  moved doc sorting from cardDeck into
 collectionSubView to apply it to all collections.

---
 .../src/components/Dropdown/Dropdown.tsx           |  30 ++----
 src/client/util/CurrentUserUtils.ts                |  10 +-
 .../views/collections/CollectionCardDeckView.tsx   | 119 ++++-----------------
 src/client/views/collections/CollectionSubView.tsx |  49 ++++++++-
 .../views/collections/FlashcardPracticeUI.tsx      |   4 +-
 src/client/views/global/globalScripts.ts           |  35 +++---
 src/client/views/nodes/ComparisonBox.tsx           |   2 +-
 src/client/views/nodes/imageEditor/ImageEditor.tsx |  69 ++----------
 .../views/nodes/imageEditor/ImageEditorButtons.tsx |   1 +
 9 files changed, 108 insertions(+), 211 deletions(-)

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

diff --git a/packages/components/src/components/Dropdown/Dropdown.tsx b/packages/components/src/components/Dropdown/Dropdown.tsx
index 0953f412c..b9b6f01b8 100644
--- a/packages/components/src/components/Dropdown/Dropdown.tsx
+++ b/packages/components/src/components/Dropdown/Dropdown.tsx
@@ -99,22 +99,14 @@ export const Dropdown = (props: IDropdownProps) => {
         background: background ?? color,
     };
 
-    const getCaretDirection = (active: boolean, placement: Placement = 'left'): JSX.Element => {
-        if (iconProvider) return iconProvider(active, placement);
-        switch (placement) {
-            case 'bottom':
-                if (active) return <FaCaretUp />;
-                return <FaCaretDown />;
-            case 'right':
-                if (active) return <FaCaretLeft />;
-                return <FaCaretRight />;
-            case 'top':
-                if (active) return <FaCaretDown />;
-                return <FaCaretUp />;
-            default:
-                if (active) return <FaCaretUp />;
-                return <FaCaretDown />;
-        }
+    const getCaretDirection = (isActive: boolean, caretPlacement: Placement = 'left'): JSX.Element => {
+        if (iconProvider) return iconProvider(isActive, caretPlacement);
+        switch (caretPlacement) {
+            default:      
+            case 'bottom':return isActive ?  <FaCaretUp />: <FaCaretDown />;
+            case 'right': return isActive ? <FaCaretLeft /> : <FaCaretRight />;
+            case 'top':   return isActive ? <FaCaretDown />: <FaCaretUp />;
+        } // prettier-ignore
     };
 
     const getToggle = () => {
@@ -143,9 +135,9 @@ export const Dropdown = (props: IDropdownProps) => {
         }
     };
 
-    const setActiveChanged = (active: boolean) => {
-        setActive(active);
-        props.activeChanged?.(active);
+    const setActiveChanged = (isActive: boolean) => {
+        setActive(isActive);
+        props.activeChanged?.(isActive);
     };
 
     const dropdown: JSX.Element = (
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 7c36a82f2..e806675aa 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -688,7 +688,7 @@ pie title Minerals in my tap water
             { title: "Center",    icon: "align-center",  toolTip: "Center Align Stack",  btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"hcenter",    funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform
         ]
     }
-    static cardTools(): Button[] {
+    static sortTools(): 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_);}'}},
@@ -704,7 +704,7 @@ pie title Minerals in my tap water
          ]
     }
   
-    static tagGroupTools(): Button[] {
+    static filterTools(): Button[] {
         const defaultTagButtonDescs = [
             { title: "Star", isSystem: false,icon: "star",  toolTip:"Click to toggle visibility of Star tagged Docs",  btnType: ButtonType.ToggleButton,  expertMode: false, toolType:"#star", funcs: {}, scripts: { onClick: '{ return setTagFilter(this.toolType, _added_, _readOnly_);}'}},
             { title: "Like", isSystem: false,icon: "heart", toolTip:"Click to toggle visibility of Like tagged Docs",  btnType: ButtonType.ToggleButton,  expertMode: false, toolType:"#like", funcs: {}, scripts: { onClick: '{ return setTagFilter(this.toolType, _added_, _readOnly_);}'}},
@@ -723,7 +723,6 @@ pie title Minerals in my tap water
             { 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: "Cards",     icon: "brain",               toolTip: "Flashcards",          btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"flashcards", funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform
         ]
     }
     static textTools():Button[] {
@@ -822,15 +821,14 @@ 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: "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: 30, backgroundColor: doc.userVariantColor as string},
+            { 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: "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
diff --git a/src/client/views/collections/CollectionCardDeckView.tsx b/src/client/views/collections/CollectionCardDeckView.tsx
index 1bca68846..aaf33c3a1 100644
--- a/src/client/views/collections/CollectionCardDeckView.tsx
+++ b/src/client/views/collections/CollectionCardDeckView.tsx
@@ -1,16 +1,16 @@
-import { IReactionDisposer, ObservableMap, action, computed, makeObservable, observable, reaction, runInAction, trace } from 'mobx';
+import { IReactionDisposer, ObservableMap, action, computed, makeObservable, observable, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import { computedFn } from 'mobx-utils';
 import * as React from 'react';
 import * as CSS from 'csstype';
-import { ClientUtils, DashColor, imageUrlToBase64, returnFalse, returnNever, returnZero, setupMoveUpEvents } from '../../../ClientUtils';
+import { ClientUtils, imageUrlToBase64, returnFalse, returnNever, returnZero, setupMoveUpEvents } from '../../../ClientUtils';
 import { emptyFunction } from '../../../Utils';
-import { Doc, DocListCast, Opt, StrListCast } from '../../../fields/Doc';
+import { Doc, DocListCast, Opt } from '../../../fields/Doc';
 import { Animation, DocData } from '../../../fields/DocSymbols';
 import { Id } from '../../../fields/FieldSymbols';
 import { List } from '../../../fields/List';
 import { ScriptField } from '../../../fields/ScriptField';
-import { BoolCast, DateCast, DocCast, NumCast, RTFCast, ScriptCast, StrCast } from '../../../fields/Types';
+import { BoolCast, DocCast, NumCast, RTFCast, ScriptCast, StrCast } from '../../../fields/Types';
 import { URLField } from '../../../fields/URLField';
 import { gptImageLabel } from '../../apis/gpt/GPT';
 import { DocumentType } from '../../documents/DocumentTypes';
@@ -25,21 +25,12 @@ import { StyleProp } from '../StyleProp';
 import { TagItem } from '../TagsView';
 import { DocumentView, DocumentViewProps } from '../nodes/DocumentView';
 import { FocusViewOptions } from '../nodes/FocusViewOptions';
-import { GPTPopup, GPTPopupMode } from '../pdf/GPTPopup/GPTPopup';
+import { GPTPopup } from '../pdf/GPTPopup/GPTPopup';
 import './CollectionCardDeckView.scss';
 import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { SettingsManager } from '../../util/SettingsManager';
 
-enum cardSortings {
-    Time = 'time',
-    Type = 'type',
-    Color = 'color',
-    Chat = 'chat',
-    Tag = 'tag',
-    None = '',
-}
-
 /**
  * New view type specifically for studying more dynamically. Allows you to reorder docs however you see fit, easily
  * sort and filter using presets, and customize your experience with chat gpt.
@@ -60,7 +51,6 @@ export class CollectionCardView extends CollectionSubView() {
     @observable _forceChildXf = 0;
     @observable _hoveredNodeIndex = -1;
     @observable _docRefs = new ObservableMap<Doc, DocumentView>();
-    @observable _docDraggedIndex: number = -1;
     @observable _cursor: CSS.Property.Cursor = 'ew-resize';
 
     constructor(props: SubCollectionViewProps) {
@@ -102,18 +92,8 @@ export class CollectionCardView extends CollectionSubView() {
 
     componentDidMount() {
         this._props.setContentViewBox?.(this);
-        this._disposers.sort = reaction(
-            () => GPTPopup.Instance.visible,
-            isVis => {
-                if (isVis) {
-                    this.openChatPopup();
-                } else {
-                    this.Document.card_sort = this.cardSort === cardSortings.Chat ? '' : this.Document.card_sort;
-                }
-            }
-        );
         // 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 renders, we need to act like the
+        // when inquired from the dom (below in childScreenToLocal).  When the doc is actually rendered, we need to act like the
         // dash data just changed and trigger a React involidation with the correct data (read from the dom).
         this._disposers.child = reaction(
             () => [this.Document.x, this.Document.y],
@@ -136,14 +116,11 @@ export class CollectionCardView extends CollectionSubView() {
         this._dropDisposer?.();
     }
 
-    @computed get cardSort() {
-        return StrCast(this.Document.card_sort) as cardSortings;
-    }
     /**
      * Number of rows of cards to be rendered
      */
     @computed get numRows() {
-        return Math.ceil(this.sortedDocs.length / this._maxRowCount);
+        return Math.ceil(this.childDocs.length / this._maxRowCount);
     }
     /**
      * Circle arc size, in radians, to layout cards
@@ -211,7 +188,7 @@ export class CollectionCardView extends CollectionSubView() {
      * @returns the card's new index
      */
     findCardDropIndex = (mouseX: number, mouseY: number) => {
-        const cardCount = this.sortedDocs.length;
+        const cardCount = this.childDocs.length;
         let index = 0;
         const cardWidth = cardCount < this._maxRowCount ? this._props.PanelWidth() / cardCount : this._props.PanelWidth() / this._maxRowCount;
 
@@ -245,8 +222,8 @@ export class CollectionCardView extends CollectionSubView() {
      */
     @action
     onPointerMove = (x: number, y: number) => {
-        if (DragManager.docsBeingDragged.some(doc => this.sortedDocs.includes(doc)) || SnappingManager.CanEmbed) {
-            this._docDraggedIndex = this.findCardDropIndex(x, y);
+        if (DragManager.docsBeingDragged.some(doc => this.childDocs.includes(doc)) || SnappingManager.CanEmbed) {
+            this.docDraggedIndex = this.findCardDropIndex(x, y);
         }
     };
 
@@ -259,11 +236,11 @@ export class CollectionCardView extends CollectionSubView() {
     onInternalDrop = undoable(
         action((e: Event, de: DragManager.DropEvent) => {
             if (de.complete.docDragData) {
-                const dragIndex = this._docDraggedIndex;
+                const dragIndex = this.docDraggedIndex;
                 const draggedDoc = DragManager.docsBeingDragged[0];
                 if (dragIndex > -1 && draggedDoc) {
-                    this._docDraggedIndex = -1;
-                    const sorted = this.sortedDocs;
+                    this.docDraggedIndex = -1;
+                    const sorted = this.childDocs;
                     const originalIndex = sorted.findIndex(doc => doc === draggedDoc);
 
                     this.Document.card_sort = '';
@@ -295,50 +272,6 @@ export class CollectionCardView extends CollectionSubView() {
             .map(({ i }) => i)
             .join('.');
 
-    /**
-     * Called in the sortedDocsType method. Compares the cards' value in regards to the desired sort type-- earlier cards are move to the
-     * front, latter cards to the back
-     * @param docs
-     * @param sortType
-     * @param isDesc
-     * @returns
-     */
-    sort = (docsIn: Doc[], sortType: cardSortings, isDesc: boolean, dragIndex: number) => {
-        const docs = docsIn.slice(); // need make new object list since sort() modifies the incoming list which confuses mobx caching
-        sortType &&
-            docs.sort((docA, docB) => {
-                const [typeA, typeB] = (() => {
-                    switch (sortType) {
-                        default:                
-                        case cardSortings.Type: return [StrCast(docA.type),                                   StrCast(docB.type)];
-                        case cardSortings.Chat: return [NumCast(docA.chatIndex, 9999),                        NumCast(docB.chatIndex,9999)];
-                        case cardSortings.Time: return [DateCast(docA.author_date)?.date ?? Date.now(),       DateCast(docB.author_date)?.date ?? Date.now()];
-                        case cardSortings.Color:return [DashColor(StrCast(docA.backgroundColor)).hsv().hue(), DashColor(StrCast(docB.backgroundColor)).hsv().hue()];
-                        case cardSortings.Tag:  return [StrListCast(docA.tags).join(""),                      StrListCast(docB.tags).join("")];
-                    }
-                })(); //prettier-ignore
-                return (typeA < typeB ? -1 : typeA > typeB ? 1 : 0) * (isDesc ? 1 : -1);
-            });
-        if (dragIndex !== -1) {
-            const draggedDoc = DragManager.docsBeingDragged[0];
-            const originalIndex = docs.findIndex(doc => doc === draggedDoc);
-
-            originalIndex !== -1 && docs.splice(originalIndex, 1);
-            draggedDoc && docs.splice(dragIndex, 0, draggedDoc);
-        }
-
-        return docs;
-    };
-
-    @computed get sortedDocs() {
-        return this.sort(
-            this.childCards.map(card => card.layout),
-            this.cardSort,
-            BoolCast(this.Document.card_sort_isDesc),
-            this._docDraggedIndex
-        );
-    }
-
     isChildContentActive = computedFn(
         (doc: Doc) => () =>
             this._props.isContentActive?.() === false
@@ -560,16 +493,6 @@ export class CollectionCardView extends CollectionSubView() {
         });
     }, '');
 
-    /**
-     * Opens up the chat popup and starts the process for smart sorting.
-     */
-    openChatPopup = async () => {
-        GPTPopup.Instance.setVisible(true);
-        GPTPopup.Instance.setMode(GPTPopupMode.CARD);
-        GPTPopup.Instance.setCardsDoneLoading(true); // Set dataDoneLoading to true after data is loaded
-        await this.childPairStringListAndUpdateSortDesc();
-    };
-
     childScreenToLocal = computedFn((doc: Doc, index: number, isSelected: boolean) => () => {
         // need to explicitly trigger an invalidation since we're reading everything from the Dom
         this._forceChildXf;
@@ -615,7 +538,7 @@ export class CollectionCardView extends CollectionSubView() {
         setupMoveUpEvents(
             this,
             e,
-            (e: PointerEvent, down: number[], delta: number[]) => {
+            (emove: PointerEvent, down: number[], delta: number[]) => {
                 this.layoutDoc._cardWidth = Math.max(10, delta[0] < 0 ? Math.floor(this.cardWidth + delta[0]) : Math.ceil(this.cardWidth + delta[0]));
                 return false;
             },
@@ -662,9 +585,9 @@ export class CollectionCardView extends CollectionSubView() {
      * Actually renders all the cards
      */
     @computed get renderCards() {
-        trace();
+        console.log(this.docDraggedIndex, this.childDocs[0].title, this.childDocs[1].title);
         // Map sorted documents to their rendered components
-        return this.sortedDocs.map((doc, index) => {
+        return this.childDocs.map((doc, index) => {
             const cardsInRow = this.cardsInRowThatIncludesCardIndex(index);
 
             const childScreenToLocal = this.childScreenToLocal(doc, index, doc === this.curDoc());
@@ -674,7 +597,7 @@ export class CollectionCardView extends CollectionSubView() {
             const aspect = NumCast(doc.height) / NumCast(doc.width, 1);
             const vscale = Math.max(1,Math.min((this._props.PanelHeight() * 0.95 * this.fitContentScale * this.nativeScaling) / (aspect * this.childPanelWidth()), 
                                     (this._props.PanelHeight() - 80) / (aspect * (this._props.PanelWidth() / 10)))); // prettier-ignore
-            const hscale = Math.min(this.sortedDocs.length, this._maxRowCount) / 2; // bcz: hack - the grid is divided evenly into maxRowCount cells, so the max scaling would be maxRowCount -- but making things that wide is ugly, so cap it off at half the window size
+            const hscale = Math.min(this.childDocs.length, this._maxRowCount) / 2; // bcz: hack - the grid is divided evenly into maxRowCount cells, so the max scaling would be maxRowCount -- but making things that wide is ugly, so cap it off at half the window size
             return (
                 <div
                     key={doc[Id]}
@@ -710,17 +633,13 @@ export class CollectionCardView extends CollectionSubView() {
     curDoc = () => DocCast(this.layoutDoc._card_curDoc);
 
     render() {
-        trace();
         const fitContentScale = this.childCards.length === 0 ? 1 : this.fitContentScale;
         return (
             <div
                 className="collectionCardView-outer"
                 ref={(ele: HTMLDivElement | null) => this.createDashEventsTarget(ele)}
-                onPointerDown={action(e => {
-                    if (e.button === 2 || e.ctrlKey) return;
-                    this.releaseCurDoc();
-                })}
-                onPointerLeave={action(() => (this._docDraggedIndex = -1))}
+                onPointerDown={e => e.button !== 2 && !e.ctrlKey && this.releaseCurDoc()}
+                onPointerLeave={action(() => (this.docDraggedIndex = -1))}
                 onPointerMove={e => this.onPointerMove(...this._props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY))}
                 onDrop={this.onExternalDrop.bind(this)}
                 style={{
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 0c059f729..5e99bec39 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -1,7 +1,7 @@
 import { action, computed, makeObservable, observable } from 'mobx';
 import * as React from 'react';
 import * as rp from 'request-promise';
-import { ClientUtils, returnFalse } from '../../../ClientUtils';
+import { ClientUtils, DashColor, returnFalse } from '../../../ClientUtils';
 import CursorField from '../../../fields/CursorField';
 import { Doc, DocListCast, GetDocFromUrl, GetHrefFromHTML, Opt, RTFIsFragment, StrListCast } from '../../../fields/Doc';
 import { AclPrivate, DocData } from '../../../fields/DocSymbols';
@@ -9,7 +9,7 @@ import { Id } from '../../../fields/FieldSymbols';
 import { List } from '../../../fields/List';
 import { listSpec } from '../../../fields/Schema';
 import { ScriptField } from '../../../fields/ScriptField';
-import { BoolCast, Cast, NumCast, ScriptCast, StrCast, toList } from '../../../fields/Types';
+import { BoolCast, Cast, DateCast, NumCast, ScriptCast, StrCast, toList } from '../../../fields/Types';
 import { WebField } from '../../../fields/URLField';
 import { GetEffectiveAcl, TraceMobx } from '../../../fields/util';
 import { GestureUtils } from '../../../pen-gestures/GestureUtils';
@@ -29,6 +29,14 @@ import { DocumentView, DocumentViewProps } from '../nodes/DocumentView';
 import { FlashcardPracticeUI } from './FlashcardPracticeUI';
 import { OpenWhere, OpenWhereMod } from '../nodes/OpenWhere';
 
+export enum docSortings {
+    Time = 'time',
+    Type = 'type',
+    Color = 'color',
+    Chat = 'chat',
+    Tag = 'tag',
+    None = '',
+}
 export interface CollectionViewProps extends React.PropsWithChildren<FieldViewProps> {
     isAnnotationOverlay?: boolean; // is the collection an annotation overlay (eg an overlay on an image/video/etc)
     isAnnotationOverlayScrollable?: boolean; // whether the annotation overlay can be vertically scrolled (just for tree views, currently)
@@ -150,6 +158,8 @@ export function CollectionSubView<X>() {
         unrecursiveDocFilters = () => [...(this._props.childFilters?.().filter(f => !ClientUtils.IsRecursiveFilter(f)) || [])];
         childDocRangeFilters = () => [...(this._props.childFiltersByRanges?.() || []), ...this.collectionRangeDocFilters()];
         searchFilterDocs = () => this._props.searchFilterDocs?.() ?? DocListCast(this.Document._searchFilterDocs);
+
+        @observable docDraggedIndex = -1;
         @computed.struct get childDocs() {
             TraceMobx();
             let rawdocs: (Doc | Promise<Doc>)[] = [];
@@ -166,8 +176,10 @@ export function CollectionSubView<X>() {
                 const templateRoot = this._props.TemplateDataDocument;
                 rawdocs = templateRoot && !this._props.isAnnotationOverlay ? [Doc.GetProto(templateRoot)] : [];
             }
-
-            const childDocs = rawdocs.filter(d => !(d instanceof Promise) && GetEffectiveAcl(Doc.GetProto(d)) !== AclPrivate && (this._props.ignoreUnrendered || !d.layout_unrendered)).map(d => d as Doc);
+            const childDocs = this.childSortedDocs(
+                rawdocs.filter(d => !(d instanceof Promise) && GetEffectiveAcl(Doc.GetProto(d)) !== AclPrivate && (this._props.ignoreUnrendered || !d.layout_unrendered)).map(d => d as Doc),
+                this.docDraggedIndex
+            );
 
             const childDocFilters = this.childDocFilters();
             const childFiltersByRanges = this.childDocRangeFilters();
@@ -214,6 +226,35 @@ export function CollectionSubView<X>() {
             return docsforFilter;
         }
 
+        childSortedDocs = (docsIn: Doc[], dragIndex: number) => {
+            const sortType = StrCast(this.Document[this._props.fieldKey + '_sort']) as docSortings;
+            const isDesc = BoolCast(this.Document[this._props.fieldKey + '_sort_desc']);
+            const docs = docsIn.slice();
+            if (sortType) {
+                docs.sort((docA, docB) => {
+                    const [typeA, typeB] = (() => {
+                        switch (sortType) {
+                            default:                
+                            case docSortings.Type: return [StrCast(docA.type),                                   StrCast(docB.type)];
+                            case docSortings.Chat: return [NumCast(docA.chatIndex, 9999),                        NumCast(docB.chatIndex,9999)];
+                            case docSortings.Time: return [DateCast(docA.author_date)?.date ?? Date.now(),       DateCast(docB.author_date)?.date ?? Date.now()];
+                            case docSortings.Color:return [DashColor(StrCast(docA.backgroundColor)).hsv().hue(), DashColor(StrCast(docB.backgroundColor)).hsv().hue()];
+                            case docSortings.Tag:  return [StrListCast(docA.tags).join(""),                      StrListCast(docB.tags).join("")];
+                        }
+                    })(); //prettier-ignore
+                    return (typeA < typeB ? -1 : typeA > typeB ? 1 : 0) * (isDesc ? 1 : -1);
+                });
+            }
+            if (dragIndex !== -1) {
+                const draggedDoc = DragManager.docsBeingDragged[0];
+                const originalIndex = docs.findIndex(doc => doc === draggedDoc);
+
+                originalIndex !== -1 && docs.splice(originalIndex, 1);
+                draggedDoc && docs.splice(dragIndex, 0, draggedDoc);
+            }
+            return docs;
+        };
+
         @action
         protected async setCursorPosition(position: [number, number]) {
             let ind;
diff --git a/src/client/views/collections/FlashcardPracticeUI.tsx b/src/client/views/collections/FlashcardPracticeUI.tsx
index 77f1db9ad..c071c5fb8 100644
--- a/src/client/views/collections/FlashcardPracticeUI.tsx
+++ b/src/client/views/collections/FlashcardPracticeUI.tsx
@@ -58,7 +58,7 @@ export class FlashcardPracticeUI extends ObservableReactComponent<PracticeUIProp
     get practiceField() { return this._props.fieldKey + "_practice"; } // prettier-ignore
 
     @computed get filterDoc() { return DocListCast(Doc.MyContextMenuBtns.data).find(doc => doc.title === 'Filter'); } // prettier-ignore
-    @computed get practiceMode() {  return this._props.allChildDocs().some(doc => doc._flashcardType) ? StrCast(this._props.layoutDoc.practiceMode) : ''; } // prettier-ignore
+    @computed get practiceMode() {  return this._props.allChildDocs().some(doc => doc._layout_flashcardType) ? StrCast(this._props.layoutDoc.practiceMode) : ''; } // prettier-ignore
 
     btnHeight = () => NumCast(this.filterDoc?.height) * Math.min(1, this._props.ScreenToLocalBoxXf().Scale);
     btnWidth = () => (!this.filterDoc ? 1 : (this.btnHeight() * NumCast(this.filterDoc._width)) / NumCast(this.filterDoc._height));
@@ -179,7 +179,7 @@ export class FlashcardPracticeUI extends ObservableReactComponent<PracticeUIProp
             </div>
         );
     }
-    tryFilterOut = (doc: Doc) => (this.practiceMode && BoolCast(doc?._flashcardType) && doc[this.practiceField] === practiceVal.CORRECT ? true : false); // show only cards that aren't marked as correct
+    tryFilterOut = (doc: Doc) => (this.practiceMode && doc?._layout_flashcardType && doc[this.practiceField] === practiceVal.CORRECT ? true : false); // show only cards that aren't marked as correct
     render() {
         return (
             <div className="FlashcardPracticeUI">
diff --git a/src/client/views/global/globalScripts.ts b/src/client/views/global/globalScripts.ts
index 542417531..b02eabab0 100644
--- a/src/client/views/global/globalScripts.ts
+++ b/src/client/views/global/globalScripts.ts
@@ -40,6 +40,7 @@ import { WebBox } from '../nodes/WebBox';
 import { RichTextMenu } from '../nodes/formattedText/RichTextMenu';
 import { GPTPopup, GPTPopupMode } from '../pdf/GPTPopup/GPTPopup';
 import { OpenWhere } from '../nodes/OpenWhere';
+import { docSortings } from '../collections/CollectionSubView';
 
 // eslint-disable-next-line prefer-arrow-callback
 ScriptingGlobals.add(function IsNoneSelected() {
@@ -197,43 +198,35 @@ ScriptingGlobals.add(function showFreeform(
             checkResult: (doc: Doc) => BoolCast(doc?._freeform_useClusters, false),
             setDoc: (doc: Doc) => { doc._freeform_useClusters = !doc._freeform_useClusters; },
         }],
-        ['flashcards', {
-            checkResult: (doc: Doc) => BoolCast(Doc.UserDoc().defaultToFlashcards, false),
-            setDoc: (doc: Doc, dv: DocumentView) => { Doc.UserDoc().defaultToFlashcards = !Doc.UserDoc().defaultToFlashcards}, // prettier-ignore
-        }],
         ['time', {
-            checkResult: (doc: Doc) => StrCast(doc?.card_sort) === "time",
-            setDoc: (doc: Doc, dv: DocumentView) => { doc.card_sort === "time" ? doc.card_sort = '' : doc.card_sort = 'time'}, // prettier-ignore
+            checkResult: (doc: Doc) => StrCast(doc?.[Doc.LayoutFieldKey(doc)+"_sort"]) === "time",
+            setDoc: (doc: Doc, dv: DocumentView) => { doc[Doc.LayoutFieldKey(doc)+"_sort"] === "time" ? doc[Doc.LayoutFieldKey(doc)+"_sort"] = '' : doc[Doc.LayoutFieldKey(doc)+"_sort"] = docSortings.Time}, // prettier-ignore
         }],
         ['docType', {
-            checkResult: (doc: Doc) => StrCast(doc?.card_sort) === "type",
-            setDoc: (doc: Doc, dv: DocumentView) => { doc.card_sort === "type" ? doc.card_sort = '' : doc.card_sort = 'type'}, // prettier-ignore
+            checkResult: (doc: Doc) => StrCast(doc?.[Doc.LayoutFieldKey(doc)+"_sort"]) === "type",
+            setDoc: (doc: Doc, dv: DocumentView) => { doc[Doc.LayoutFieldKey(doc)+"_sort"] === "type" ? doc[Doc.LayoutFieldKey(doc)+"_sort"] = '' : doc[Doc.LayoutFieldKey(doc)+"_sort"] = docSortings.Type}, // prettier-ignore
         }],
         ['color', {
-            checkResult: (doc: Doc) => StrCast(doc?.card_sort) === "color",
-            setDoc: (doc: Doc, dv: DocumentView) => { doc.card_sort === "color" ? doc.card_sort = '' : doc.card_sort = 'color'}, // prettier-ignore
+            checkResult: (doc: Doc) => StrCast(doc?.[Doc.LayoutFieldKey(doc)+"_sort"]) === "color",
+            setDoc: (doc: Doc, dv: DocumentView) => { doc?.[Doc.LayoutFieldKey(doc)+"_sort"] === "color" ? doc[Doc.LayoutFieldKey(doc)+"_sort"] = '' : doc[Doc.LayoutFieldKey(doc)+"_sort"] = docSortings.Color}, // prettier-ignore
         }],
         ['tag', {
-            checkResult: (doc: Doc) => StrCast(doc?.card_sort) === "tag",
-            setDoc: (doc: Doc, dv: DocumentView) => { doc.card_sort === "tag" ? doc.card_sort = '' : doc.card_sort = 'tag'}, // prettier-ignore
+            checkResult: (doc: Doc) => StrCast(doc?.[Doc.LayoutFieldKey(doc)+"_sort"]) === "tag",
+            setDoc: (doc: Doc, dv: DocumentView) => { doc[Doc.LayoutFieldKey(doc)+"_sort"] === "tag" ? doc[Doc.LayoutFieldKey(doc)+"_sort"] = '' : doc[Doc.LayoutFieldKey(doc)+"_sort"] = docSortings.Tag}, // prettier-ignore
         }],
         ['up', {
-            checkResult: (doc: Doc) => BoolCast(!doc?.card_sort_isDesc),
-            setDoc: (doc: Doc, dv: DocumentView) => {
-                doc.card_sort_isDesc = false;
-            },
+            checkResult: (doc: Doc) => BoolCast(!doc?.[Doc.LayoutFieldKey(doc)+"_sort_desc"]),
+            setDoc: (doc: Doc, dv: DocumentView) => { doc[Doc.LayoutFieldKey(doc)+"_sort_desc"] = undefined; },
         }],
         ['down', {
-            checkResult: (doc: Doc) => BoolCast(doc?.card_sort_isDesc),
-            setDoc: (doc: Doc, dv: DocumentView) => {
-                doc.card_sort_isDesc = true;
-            },
+            checkResult: (doc: Doc) => BoolCast(doc?.[Doc.LayoutFieldKey(doc)+"_sort_desc"]),
+            setDoc: (doc: Doc, dv: DocumentView) => { doc[Doc.LayoutFieldKey(doc)+"_sort_desc"] = true; },
         }],
         ['toggle-chat', {
             checkResult: (doc: Doc) => GPTPopup.Instance.visible,
             setDoc: (doc: Doc, dv: DocumentView) => {
                 if (GPTPopup.Instance.visible){
-                    doc.card_sort = ''
+                    doc[Doc.LayoutFieldKey(doc)+"_sort"] = '';
                     GPTPopup.Instance.setVisible(false);
    
                 } else {
diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx
index e0c360132..f5291a4c1 100644
--- a/src/client/views/nodes/ComparisonBox.tsx
+++ b/src/client/views/nodes/ComparisonBox.tsx
@@ -1,7 +1,7 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Tooltip } from '@mui/material';
 import axios from 'axios';
-import { IReactionDisposer, action, computed, makeObservable, observable, reaction, runInAction, trace } from 'mobx';
+import { IReactionDisposer, action, computed, makeObservable, observable, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import ReactLoading from 'react-loading';
diff --git a/src/client/views/nodes/imageEditor/ImageEditor.tsx b/src/client/views/nodes/imageEditor/ImageEditor.tsx
index 2ae6ee1dd..6b1d05031 100644
--- a/src/client/views/nodes/imageEditor/ImageEditor.tsx
+++ b/src/client/views/nodes/imageEditor/ImageEditor.tsx
@@ -551,8 +551,7 @@ const ImageEditor = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addDoc
         img.src = src;
         if (!currImg.current || !originalImg.current || !imageRootDoc) return undefined;
         try {
-            const res = await createNewImgDoc(img, false);
-            return res;
+            return await createNewImgDoc(img, false);
         } catch (err) {
             console.log(err);
         }
@@ -589,9 +588,7 @@ const ImageEditor = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addDoc
                                 // disable once edited has been clicked (doesn't make sense to change after first edit)
                                 disabled={edited}
                                 checked={isNewCollection}
-                                onChange={() => {
-                                    setIsNewCollection(prev => !prev);
-                                }}
+                                onChange={() => setIsNewCollection(prev => !prev)}
                             />
                         }
                         label="Create New Collection"
@@ -610,49 +607,13 @@ const ImageEditor = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addDoc
         return (
             <div className="sideControlsContainer" style={{ backgroundColor: bgColor }}>
                 <div className="sideControls">
-                    <div className="imageToolsContainer">
-                        {imageEditTools.map(tool => {
-                            return ImageToolButton(tool, tool.type === currTool.type, changeTool);
-                        })}
-                    </div>
+                    <div className="imageToolsContainer">{imageEditTools.map(tool => ImageToolButton(tool, tool.type === currTool.type, changeTool))}</div>
                     {currTool.type == ImageToolType.Cut && (
                         <div className="cutToolsContainer">
-                            <Button
-                                style={{ width: '100%' }}
-                                text="Keep in"
-                                type={Type.TERT}
-                                color={cutType == CutMode.IN ? SettingsManager.userColor : bgColor}
-                                onClick={() => {
-                                    setCutType(CutMode.IN);
-                                }}
-                            />
-                            <Button
-                                style={{ width: '100%' }}
-                                text="Keep out"
-                                type={Type.TERT}
-                                color={cutType == CutMode.OUT ? SettingsManager.userColor : bgColor}
-                                onClick={() => {
-                                    setCutType(CutMode.OUT);
-                                }}
-                            />
-                            <Button
-                                style={{ width: '100%' }}
-                                text="Draw in"
-                                type={Type.TERT}
-                                color={cutType == CutMode.DRAW_IN ? SettingsManager.userColor : bgColor}
-                                onClick={() => {
-                                    setCutType(CutMode.DRAW_IN);
-                                }}
-                            />
-                            <Button
-                                style={{ width: '100%' }}
-                                text="Erase"
-                                type={Type.TERT}
-                                color={cutType == CutMode.ERASE ? SettingsManager.userColor : bgColor}
-                                onClick={() => {
-                                    setCutType(CutMode.ERASE);
-                                }}
-                            />
+                            <Button style={{ width: '100%' }} text="Keep in" type={Type.TERT} color={cutType == CutMode.IN ? SettingsManager.userColor : bgColor} onClick={() => setCutType(CutMode.IN)} />
+                            <Button style={{ width: '100%' }} text="Keep out" type={Type.TERT} color={cutType == CutMode.OUT ? SettingsManager.userColor : bgColor} onClick={() => setCutType(CutMode.OUT)} />
+                            <Button style={{ width: '100%' }} text="Draw in" type={Type.TERT} color={cutType == CutMode.DRAW_IN ? SettingsManager.userColor : bgColor} onClick={() => setCutType(CutMode.DRAW_IN)} />
+                            <Button style={{ width: '100%' }} text="Erase" type={Type.TERT} color={cutType == CutMode.ERASE ? SettingsManager.userColor : bgColor} onClick={() => setCutType(CutMode.ERASE)} />
                         </div>
                     )}
                     <div className="sliderContainer" onPointerDown={e => e.stopPropagation()}>
@@ -669,9 +630,7 @@ const ImageEditor = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addDoc
                                 defaultValue={genFillTool.sliderDefault}
                                 size="small"
                                 valueLabelDisplay="auto"
-                                onChange={(e, val) => {
-                                    setCursorData(prev => ({ ...prev, width: val as number }));
-                                }}
+                                onChange={(e, val) => setCursorData(prev => ({ ...prev, width: val as number }))}
                             />
                         )}
                         {currTool.type === ImageToolType.Cut && (
@@ -687,9 +646,7 @@ const ImageEditor = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addDoc
                                 defaultValue={cutTool.sliderDefault}
                                 size="small"
                                 valueLabelDisplay="auto"
-                                onChange={(e, val) => {
-                                    setCursorData(prev => ({ ...prev, width: val as number }));
-                                }}
+                                onChange={(e, val) => setCursorData(prev => ({ ...prev, width: val as number }))}
                             />
                         )}
                     </div>
@@ -701,9 +658,7 @@ const ImageEditor = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addDoc
                                 e.stopPropagation();
                                 handleUndo();
                             }}
-                            onPointerUp={e => {
-                                e.stopPropagation();
-                            }}
+                            onPointerUp={e => e.stopPropagation()}
                             color={activeColor}
                             tooltip="Undo"
                             icon={<IoMdUndo />}
@@ -714,9 +669,7 @@ const ImageEditor = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addDoc
                                 e.stopPropagation();
                                 handleRedo();
                             }}
-                            onPointerUp={e => {
-                                e.stopPropagation();
-                            }}
+                            onPointerUp={e => e.stopPropagation()}
                             color={activeColor}
                             tooltip="Redo"
                             icon={<IoMdRedo />}
diff --git a/src/client/views/nodes/imageEditor/ImageEditorButtons.tsx b/src/client/views/nodes/imageEditor/ImageEditorButtons.tsx
index de2116253..e810881a5 100644
--- a/src/client/views/nodes/imageEditor/ImageEditorButtons.tsx
+++ b/src/client/views/nodes/imageEditor/ImageEditorButtons.tsx
@@ -55,6 +55,7 @@ export function ImageToolButton(tool: ImageEditTool, isActive: boolean, selectTo
     return (
         <div className="imageEditorButtonContainer">
             <Button
+                key={tool.name}
                 style={{ width: '100%' }}
                 text={tool.name}
                 type={Type.TERT}
-- 
cgit v1.2.3-70-g09d2


From f47b7b61749e519d694cff841b4e961fd211c0dd Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 29 Jan 2025 10:06:38 -0500
Subject: fixed chat based sorting and tagging of docs

---
 package-lock.json                                  | 7228 ++++++++++----------
 package.json                                       |   13 +-
 src/client/apis/gpt/GPT.ts                         |   45 +-
 .../views/collections/CollectionCardDeckView.tsx   |   11 +-
 4 files changed, 3611 insertions(+), 3686 deletions(-)

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

diff --git a/package-lock.json b/package-lock.json
index 0a8bf1359..fba0c5d8d 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -58,7 +58,7 @@
                 "@types/reveal": "^4.2.0",
                 "@types/supercluster": "^7.1.3",
                 "@types/textfit": "^2.4.4",
-                "@types/web": "^0.0.188",
+                "@types/web": "^0.0.200",
                 "@types/webpack-hot-middleware": "^2.25.9",
                 "@webscopeio/react-textarea-autocomplete": "^4.9.2",
                 "adm-zip": "^0.5.10",
@@ -78,7 +78,7 @@
                 "brotli": "^1.3.3",
                 "browser-assert": "^1.2.1",
                 "bson": "^6.2.0",
-                "canvas": "^2.11.2",
+                "canvas": "^3.1.0",
                 "chart.js": "^4.4.0",
                 "cheerio": "^1.0.0",
                 "child_process": "^1.0.2",
@@ -152,7 +152,7 @@
                 "jpeg-autorotate": "^9.0.0",
                 "jquery": "^3.7.1",
                 "js-datepicker": "^5.18.2",
-                "jsdom": "^25.0.1",
+                "jsdom": "^26.0.0",
                 "jsonschema": "^1.4.1",
                 "jszip": "^3.10.1",
                 "ldrs": "^1.0.2",
@@ -198,7 +198,7 @@
                 "prosemirror-transform": "^1.8.0",
                 "prosemirror-view": "^1.32.5",
                 "pug": "^3.0.2",
-                "puppeteer": "^23.1.0",
+                "puppeteer": "^24.1.1",
                 "query-string": "^7.1.3",
                 "querystring-es3": "^0.2.1",
                 "raw-loader": "^4.0.2",
@@ -250,7 +250,6 @@
                 "styled-components": "^6.1.1",
                 "supercluster": "^8.0.1",
                 "svgson": "^5.3.1",
-                "tesseract.js": "^5.1.0",
                 "textarea-caret": "^3.1.0",
                 "textfit": "^2.4.0",
                 "tough-cookie": "^5.0.0",
@@ -260,7 +259,7 @@
                 "typescript-collections": "^1.3.3",
                 "typescript-language-server": "^4.1.3",
                 "uninstall": "^0.0.0",
-                "unstructured-client": "^0.18.2",
+                "unstructured-client": "^0.19.0",
                 "url": "^0.11.3",
                 "url-loader": "^4.1.1",
                 "util": "^0.12.5",
@@ -333,7 +332,7 @@
                 "eslint": "^9.12.0",
                 "eslint-plugin-react": "^7.37.1",
                 "globals": "^15.10.0",
-                "mocha": "^10.2.0",
+                "mocha": "^11.1.0",
                 "prettier": "^3.1.0",
                 "scss-loader": "0.0.1",
                 "ts-loader": "^9.5.1",
@@ -354,71 +353,71 @@
             "license": "MIT"
         },
         "node_modules/@adobe/react-spectrum": {
-            "version": "3.38.1",
-            "resolved": "https://registry.npmjs.org/@adobe/react-spectrum/-/react-spectrum-3.38.1.tgz",
-            "integrity": "sha512-+y25e8rkRk97SZl2ezOChdDCAzqgvxTcbNDOQeIMhKuiiBXtql2jOdh6QtrIK5Xq+HXm72+G6VujZzJSq3pzwA==",
+            "version": "3.39.0",
+            "resolved": "https://registry.npmjs.org/@adobe/react-spectrum/-/react-spectrum-3.39.0.tgz",
+            "integrity": "sha512-TibIAWgXbr6pkmJctwu6jBYVOX14W8IAW1GCoe5RIcHu8KI7kzMTklhtLkvZeBuTZ2x47ivRnu8uWAlTksxowg==",
             "license": "Apache-2.0",
             "dependencies": {
                 "@internationalized/string": "^3.2.5",
-                "@react-aria/i18n": "^3.12.4",
+                "@react-aria/i18n": "^3.12.5",
                 "@react-aria/ssr": "^3.9.7",
-                "@react-aria/utils": "^3.26.0",
-                "@react-aria/visually-hidden": "^3.8.18",
-                "@react-spectrum/accordion": "^3.0.1",
-                "@react-spectrum/actionbar": "^3.6.2",
-                "@react-spectrum/actiongroup": "^3.10.10",
-                "@react-spectrum/avatar": "^3.0.17",
-                "@react-spectrum/badge": "^3.1.18",
-                "@react-spectrum/breadcrumbs": "^3.9.12",
-                "@react-spectrum/button": "^3.16.9",
-                "@react-spectrum/buttongroup": "^3.6.17",
-                "@react-spectrum/calendar": "^3.5.0",
-                "@react-spectrum/checkbox": "^3.9.11",
-                "@react-spectrum/color": "^3.0.2",
-                "@react-spectrum/combobox": "^3.14.0",
-                "@react-spectrum/contextualhelp": "^3.6.16",
-                "@react-spectrum/datepicker": "^3.11.0",
-                "@react-spectrum/dialog": "^3.8.16",
-                "@react-spectrum/divider": "^3.5.18",
-                "@react-spectrum/dnd": "^3.5.0",
-                "@react-spectrum/dropzone": "^3.0.6",
-                "@react-spectrum/filetrigger": "^3.0.6",
-                "@react-spectrum/form": "^3.7.10",
-                "@react-spectrum/icon": "^3.8.0",
-                "@react-spectrum/illustratedmessage": "^3.5.5",
-                "@react-spectrum/image": "^3.5.6",
-                "@react-spectrum/inlinealert": "^3.2.10",
-                "@react-spectrum/labeledvalue": "^3.1.18",
-                "@react-spectrum/layout": "^3.6.10",
-                "@react-spectrum/link": "^3.6.12",
-                "@react-spectrum/list": "^3.9.0",
-                "@react-spectrum/listbox": "^3.14.0",
-                "@react-spectrum/menu": "^3.21.0",
-                "@react-spectrum/meter": "^3.5.5",
-                "@react-spectrum/numberfield": "^3.9.8",
-                "@react-spectrum/overlays": "^5.7.0",
-                "@react-spectrum/picker": "^3.15.4",
-                "@react-spectrum/progress": "^3.7.11",
-                "@react-spectrum/provider": "^3.10.0",
-                "@react-spectrum/radio": "^3.7.11",
-                "@react-spectrum/searchfield": "^3.8.11",
-                "@react-spectrum/slider": "^3.7.0",
-                "@react-spectrum/statuslight": "^3.5.17",
-                "@react-spectrum/switch": "^3.5.10",
-                "@react-spectrum/table": "^3.15.0",
-                "@react-spectrum/tabs": "^3.8.15",
-                "@react-spectrum/tag": "^3.2.11",
-                "@react-spectrum/text": "^3.5.10",
-                "@react-spectrum/textfield": "^3.12.7",
-                "@react-spectrum/theme-dark": "^3.5.14",
-                "@react-spectrum/theme-default": "^3.5.14",
-                "@react-spectrum/theme-light": "^3.4.14",
-                "@react-spectrum/tooltip": "^3.7.0",
-                "@react-spectrum/view": "^3.6.14",
-                "@react-spectrum/well": "^3.4.18",
-                "@react-stately/collections": "^3.12.0",
-                "@react-stately/data": "^3.12.0",
-                "@react-types/shared": "^3.26.0",
+                "@react-aria/utils": "^3.27.0",
+                "@react-aria/visually-hidden": "^3.8.19",
+                "@react-spectrum/accordion": "^3.0.2",
+                "@react-spectrum/actionbar": "^3.6.3",
+                "@react-spectrum/actiongroup": "^3.10.11",
+                "@react-spectrum/avatar": "^3.0.18",
+                "@react-spectrum/badge": "^3.1.19",
+                "@react-spectrum/breadcrumbs": "^3.9.13",
+                "@react-spectrum/button": "^3.16.10",
+                "@react-spectrum/buttongroup": "^3.6.18",
+                "@react-spectrum/calendar": "^3.6.0",
+                "@react-spectrum/checkbox": "^3.9.12",
+                "@react-spectrum/color": "^3.0.3",
+                "@react-spectrum/combobox": "^3.14.1",
+                "@react-spectrum/contextualhelp": "^3.6.17",
+                "@react-spectrum/datepicker": "^3.12.0",
+                "@react-spectrum/dialog": "^3.8.17",
+                "@react-spectrum/divider": "^3.5.19",
+                "@react-spectrum/dnd": "^3.5.1",
+                "@react-spectrum/dropzone": "^3.0.7",
+                "@react-spectrum/filetrigger": "^3.0.7",
+                "@react-spectrum/form": "^3.7.11",
+                "@react-spectrum/icon": "^3.8.1",
+                "@react-spectrum/illustratedmessage": "^3.5.6",
+                "@react-spectrum/image": "^3.5.7",
+                "@react-spectrum/inlinealert": "^3.2.11",
+                "@react-spectrum/labeledvalue": "^3.1.19",
+                "@react-spectrum/layout": "^3.6.11",
+                "@react-spectrum/link": "^3.6.13",
+                "@react-spectrum/list": "^3.9.1",
+                "@react-spectrum/listbox": "^3.14.1",
+                "@react-spectrum/menu": "^3.21.1",
+                "@react-spectrum/meter": "^3.5.6",
+                "@react-spectrum/numberfield": "^3.9.9",
+                "@react-spectrum/overlays": "^5.7.1",
+                "@react-spectrum/picker": "^3.15.5",
+                "@react-spectrum/progress": "^3.7.12",
+                "@react-spectrum/provider": "^3.10.1",
+                "@react-spectrum/radio": "^3.7.12",
+                "@react-spectrum/searchfield": "^3.8.12",
+                "@react-spectrum/slider": "^3.7.1",
+                "@react-spectrum/statuslight": "^3.5.18",
+                "@react-spectrum/switch": "^3.5.11",
+                "@react-spectrum/table": "^3.15.1",
+                "@react-spectrum/tabs": "^3.8.16",
+                "@react-spectrum/tag": "^3.2.12",
+                "@react-spectrum/text": "^3.5.11",
+                "@react-spectrum/textfield": "^3.12.8",
+                "@react-spectrum/theme-dark": "^3.5.15",
+                "@react-spectrum/theme-default": "^3.5.15",
+                "@react-spectrum/theme-light": "^3.4.15",
+                "@react-spectrum/tooltip": "^3.7.1",
+                "@react-spectrum/view": "^3.6.15",
+                "@react-spectrum/well": "^3.4.19",
+                "@react-stately/collections": "^3.12.1",
+                "@react-stately/data": "^3.12.1",
+                "@react-types/shared": "^3.27.0",
                 "client-only": "^0.0.1"
             },
             "peerDependencies": {
@@ -461,6 +460,25 @@
                 "url": "https://github.com/sponsors/antfu"
             }
         },
+        "node_modules/@asamuzakjp/css-color": {
+            "version": "2.8.3",
+            "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-2.8.3.tgz",
+            "integrity": "sha512-GIc76d9UI1hCvOATjZPyHFmE5qhRccp3/zGfMPapK3jBi+yocEzp6BBB0UnfRYP9NP4FANqUZYb0hnfs3TM3hw==",
+            "license": "MIT",
+            "dependencies": {
+                "@csstools/css-calc": "^2.1.1",
+                "@csstools/css-color-parser": "^3.0.7",
+                "@csstools/css-parser-algorithms": "^3.0.4",
+                "@csstools/css-tokenizer": "^3.0.3",
+                "lru-cache": "^10.4.3"
+            }
+        },
+        "node_modules/@asamuzakjp/css-color/node_modules/lru-cache": {
+            "version": "10.4.3",
+            "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
+            "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
+            "license": "ISC"
+        },
         "node_modules/@aws-crypto/crc32": {
             "version": "3.0.0",
             "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-3.0.0.tgz",
@@ -615,50 +633,48 @@
             }
         },
         "node_modules/@aws-sdk/client-cognito-identity": {
-            "version": "3.723.0",
-            "resolved": "https://registry.npmjs.org/@aws-sdk/client-cognito-identity/-/client-cognito-identity-3.723.0.tgz",
-            "integrity": "sha512-SEJGNcbfpLM7fqS5s0dLgj7jFCm2IBc1Qluehvpf4+lL1dymGwZAK7S4Ii6gPZhliEj0n48YE6Dg0UkxR2iRag==",
+            "version": "3.734.0",
+            "resolved": "https://registry.npmjs.org/@aws-sdk/client-cognito-identity/-/client-cognito-identity-3.734.0.tgz",
+            "integrity": "sha512-qfieHPeLga8MXH1PabQtb9+5etkViUcI4t33dndLHNn46vMhzfTztzkNtjUgN3fg4SrOjnxZd33pR9lRquMRvA==",
             "license": "Apache-2.0",
             "dependencies": {
                 "@aws-crypto/sha256-browser": "5.2.0",
                 "@aws-crypto/sha256-js": "5.2.0",
-                "@aws-sdk/client-sso-oidc": "3.723.0",
-                "@aws-sdk/client-sts": "3.723.0",
-                "@aws-sdk/core": "3.723.0",
-                "@aws-sdk/credential-provider-node": "3.723.0",
-                "@aws-sdk/middleware-host-header": "3.723.0",
-                "@aws-sdk/middleware-logger": "3.723.0",
-                "@aws-sdk/middleware-recursion-detection": "3.723.0",
-                "@aws-sdk/middleware-user-agent": "3.723.0",
-                "@aws-sdk/region-config-resolver": "3.723.0",
-                "@aws-sdk/types": "3.723.0",
-                "@aws-sdk/util-endpoints": "3.723.0",
-                "@aws-sdk/util-user-agent-browser": "3.723.0",
-                "@aws-sdk/util-user-agent-node": "3.723.0",
-                "@smithy/config-resolver": "^4.0.0",
-                "@smithy/core": "^3.0.0",
-                "@smithy/fetch-http-handler": "^5.0.0",
-                "@smithy/hash-node": "^4.0.0",
-                "@smithy/invalid-dependency": "^4.0.0",
-                "@smithy/middleware-content-length": "^4.0.0",
-                "@smithy/middleware-endpoint": "^4.0.0",
-                "@smithy/middleware-retry": "^4.0.0",
-                "@smithy/middleware-serde": "^4.0.0",
-                "@smithy/middleware-stack": "^4.0.0",
-                "@smithy/node-config-provider": "^4.0.0",
-                "@smithy/node-http-handler": "^4.0.0",
-                "@smithy/protocol-http": "^5.0.0",
-                "@smithy/smithy-client": "^4.0.0",
-                "@smithy/types": "^4.0.0",
-                "@smithy/url-parser": "^4.0.0",
+                "@aws-sdk/core": "3.734.0",
+                "@aws-sdk/credential-provider-node": "3.734.0",
+                "@aws-sdk/middleware-host-header": "3.734.0",
+                "@aws-sdk/middleware-logger": "3.734.0",
+                "@aws-sdk/middleware-recursion-detection": "3.734.0",
+                "@aws-sdk/middleware-user-agent": "3.734.0",
+                "@aws-sdk/region-config-resolver": "3.734.0",
+                "@aws-sdk/types": "3.734.0",
+                "@aws-sdk/util-endpoints": "3.734.0",
+                "@aws-sdk/util-user-agent-browser": "3.734.0",
+                "@aws-sdk/util-user-agent-node": "3.734.0",
+                "@smithy/config-resolver": "^4.0.1",
+                "@smithy/core": "^3.1.1",
+                "@smithy/fetch-http-handler": "^5.0.1",
+                "@smithy/hash-node": "^4.0.1",
+                "@smithy/invalid-dependency": "^4.0.1",
+                "@smithy/middleware-content-length": "^4.0.1",
+                "@smithy/middleware-endpoint": "^4.0.2",
+                "@smithy/middleware-retry": "^4.0.3",
+                "@smithy/middleware-serde": "^4.0.1",
+                "@smithy/middleware-stack": "^4.0.1",
+                "@smithy/node-config-provider": "^4.0.1",
+                "@smithy/node-http-handler": "^4.0.2",
+                "@smithy/protocol-http": "^5.0.1",
+                "@smithy/smithy-client": "^4.1.2",
+                "@smithy/types": "^4.1.0",
+                "@smithy/url-parser": "^4.0.1",
                 "@smithy/util-base64": "^4.0.0",
                 "@smithy/util-body-length-browser": "^4.0.0",
                 "@smithy/util-body-length-node": "^4.0.0",
-                "@smithy/util-defaults-mode-browser": "^4.0.0",
-                "@smithy/util-defaults-mode-node": "^4.0.0",
-                "@smithy/util-endpoints": "^3.0.0",
-                "@smithy/util-middleware": "^4.0.0",
-                "@smithy/util-retry": "^4.0.0",
+                "@smithy/util-defaults-mode-browser": "^4.0.3",
+                "@smithy/util-defaults-mode-node": "^4.0.3",
+                "@smithy/util-endpoints": "^3.0.1",
+                "@smithy/util-middleware": "^4.0.1",
+                "@smithy/util-retry": "^4.0.1",
                 "@smithy/util-utf8": "^4.0.0",
                 "tslib": "^2.6.2"
             },
@@ -667,52 +683,50 @@
             }
         },
         "node_modules/@aws-sdk/client-sagemaker": {
-            "version": "3.723.0",
-            "resolved": "https://registry.npmjs.org/@aws-sdk/client-sagemaker/-/client-sagemaker-3.723.0.tgz",
-            "integrity": "sha512-iAr2vKeIufgGBfi8ym3p0H/2TmNkvTXnPodknTbe/unnNgi/5sV+swwkQpnUMuTrxaJ617cAlYhBqpl5dkok4A==",
+            "version": "3.734.0",
+            "resolved": "https://registry.npmjs.org/@aws-sdk/client-sagemaker/-/client-sagemaker-3.734.0.tgz",
+            "integrity": "sha512-K9rUtAT3haVxFflCi/E1XMZHG9YK0txVeuoInDYlS3LQSd8zroIvxZoWrCMEvN1QWulC9qVCJZhPOCnPuo8LLA==",
             "license": "Apache-2.0",
             "dependencies": {
                 "@aws-crypto/sha256-browser": "5.2.0",
                 "@aws-crypto/sha256-js": "5.2.0",
-                "@aws-sdk/client-sso-oidc": "3.723.0",
-                "@aws-sdk/client-sts": "3.723.0",
-                "@aws-sdk/core": "3.723.0",
-                "@aws-sdk/credential-provider-node": "3.723.0",
-                "@aws-sdk/middleware-host-header": "3.723.0",
-                "@aws-sdk/middleware-logger": "3.723.0",
-                "@aws-sdk/middleware-recursion-detection": "3.723.0",
-                "@aws-sdk/middleware-user-agent": "3.723.0",
-                "@aws-sdk/region-config-resolver": "3.723.0",
-                "@aws-sdk/types": "3.723.0",
-                "@aws-sdk/util-endpoints": "3.723.0",
-                "@aws-sdk/util-user-agent-browser": "3.723.0",
-                "@aws-sdk/util-user-agent-node": "3.723.0",
-                "@smithy/config-resolver": "^4.0.0",
-                "@smithy/core": "^3.0.0",
-                "@smithy/fetch-http-handler": "^5.0.0",
-                "@smithy/hash-node": "^4.0.0",
-                "@smithy/invalid-dependency": "^4.0.0",
-                "@smithy/middleware-content-length": "^4.0.0",
-                "@smithy/middleware-endpoint": "^4.0.0",
-                "@smithy/middleware-retry": "^4.0.0",
-                "@smithy/middleware-serde": "^4.0.0",
-                "@smithy/middleware-stack": "^4.0.0",
-                "@smithy/node-config-provider": "^4.0.0",
-                "@smithy/node-http-handler": "^4.0.0",
-                "@smithy/protocol-http": "^5.0.0",
-                "@smithy/smithy-client": "^4.0.0",
-                "@smithy/types": "^4.0.0",
-                "@smithy/url-parser": "^4.0.0",
+                "@aws-sdk/core": "3.734.0",
+                "@aws-sdk/credential-provider-node": "3.734.0",
+                "@aws-sdk/middleware-host-header": "3.734.0",
+                "@aws-sdk/middleware-logger": "3.734.0",
+                "@aws-sdk/middleware-recursion-detection": "3.734.0",
+                "@aws-sdk/middleware-user-agent": "3.734.0",
+                "@aws-sdk/region-config-resolver": "3.734.0",
+                "@aws-sdk/types": "3.734.0",
+                "@aws-sdk/util-endpoints": "3.734.0",
+                "@aws-sdk/util-user-agent-browser": "3.734.0",
+                "@aws-sdk/util-user-agent-node": "3.734.0",
+                "@smithy/config-resolver": "^4.0.1",
+                "@smithy/core": "^3.1.1",
+                "@smithy/fetch-http-handler": "^5.0.1",
+                "@smithy/hash-node": "^4.0.1",
+                "@smithy/invalid-dependency": "^4.0.1",
+                "@smithy/middleware-content-length": "^4.0.1",
+                "@smithy/middleware-endpoint": "^4.0.2",
+                "@smithy/middleware-retry": "^4.0.3",
+                "@smithy/middleware-serde": "^4.0.1",
+                "@smithy/middleware-stack": "^4.0.1",
+                "@smithy/node-config-provider": "^4.0.1",
+                "@smithy/node-http-handler": "^4.0.2",
+                "@smithy/protocol-http": "^5.0.1",
+                "@smithy/smithy-client": "^4.1.2",
+                "@smithy/types": "^4.1.0",
+                "@smithy/url-parser": "^4.0.1",
                 "@smithy/util-base64": "^4.0.0",
                 "@smithy/util-body-length-browser": "^4.0.0",
                 "@smithy/util-body-length-node": "^4.0.0",
-                "@smithy/util-defaults-mode-browser": "^4.0.0",
-                "@smithy/util-defaults-mode-node": "^4.0.0",
-                "@smithy/util-endpoints": "^3.0.0",
-                "@smithy/util-middleware": "^4.0.0",
-                "@smithy/util-retry": "^4.0.0",
+                "@smithy/util-defaults-mode-browser": "^4.0.3",
+                "@smithy/util-defaults-mode-node": "^4.0.3",
+                "@smithy/util-endpoints": "^3.0.1",
+                "@smithy/util-middleware": "^4.0.1",
+                "@smithy/util-retry": "^4.0.1",
                 "@smithy/util-utf8": "^4.0.0",
-                "@smithy/util-waiter": "^4.0.0",
+                "@smithy/util-waiter": "^4.0.2",
                 "@types/uuid": "^9.0.1",
                 "tslib": "^2.6.2",
                 "uuid": "^9.0.1"
@@ -741,151 +755,47 @@
             }
         },
         "node_modules/@aws-sdk/client-sso": {
-            "version": "3.723.0",
-            "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.723.0.tgz",
-            "integrity": "sha512-r1ddZDb8yPmdofX1gQ4m8oqKozgkgVONLlAuSprGObbyMy8bYt1Psxu+GjnwMmgVu3vlF069PHyW1ndrBiL1zA==",
-            "license": "Apache-2.0",
-            "dependencies": {
-                "@aws-crypto/sha256-browser": "5.2.0",
-                "@aws-crypto/sha256-js": "5.2.0",
-                "@aws-sdk/core": "3.723.0",
-                "@aws-sdk/middleware-host-header": "3.723.0",
-                "@aws-sdk/middleware-logger": "3.723.0",
-                "@aws-sdk/middleware-recursion-detection": "3.723.0",
-                "@aws-sdk/middleware-user-agent": "3.723.0",
-                "@aws-sdk/region-config-resolver": "3.723.0",
-                "@aws-sdk/types": "3.723.0",
-                "@aws-sdk/util-endpoints": "3.723.0",
-                "@aws-sdk/util-user-agent-browser": "3.723.0",
-                "@aws-sdk/util-user-agent-node": "3.723.0",
-                "@smithy/config-resolver": "^4.0.0",
-                "@smithy/core": "^3.0.0",
-                "@smithy/fetch-http-handler": "^5.0.0",
-                "@smithy/hash-node": "^4.0.0",
-                "@smithy/invalid-dependency": "^4.0.0",
-                "@smithy/middleware-content-length": "^4.0.0",
-                "@smithy/middleware-endpoint": "^4.0.0",
-                "@smithy/middleware-retry": "^4.0.0",
-                "@smithy/middleware-serde": "^4.0.0",
-                "@smithy/middleware-stack": "^4.0.0",
-                "@smithy/node-config-provider": "^4.0.0",
-                "@smithy/node-http-handler": "^4.0.0",
-                "@smithy/protocol-http": "^5.0.0",
-                "@smithy/smithy-client": "^4.0.0",
-                "@smithy/types": "^4.0.0",
-                "@smithy/url-parser": "^4.0.0",
-                "@smithy/util-base64": "^4.0.0",
-                "@smithy/util-body-length-browser": "^4.0.0",
-                "@smithy/util-body-length-node": "^4.0.0",
-                "@smithy/util-defaults-mode-browser": "^4.0.0",
-                "@smithy/util-defaults-mode-node": "^4.0.0",
-                "@smithy/util-endpoints": "^3.0.0",
-                "@smithy/util-middleware": "^4.0.0",
-                "@smithy/util-retry": "^4.0.0",
-                "@smithy/util-utf8": "^4.0.0",
-                "tslib": "^2.6.2"
-            },
-            "engines": {
-                "node": ">=18.0.0"
-            }
-        },
-        "node_modules/@aws-sdk/client-sso-oidc": {
-            "version": "3.723.0",
-            "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.723.0.tgz",
-            "integrity": "sha512-9IH90m4bnHogBctVna2FnXaIGVORncfdxcqeEIovOxjIJJyHDmEAtA7B91dAM4sruddTbVzOYnqfPVst3odCbA==",
-            "license": "Apache-2.0",
-            "dependencies": {
-                "@aws-crypto/sha256-browser": "5.2.0",
-                "@aws-crypto/sha256-js": "5.2.0",
-                "@aws-sdk/core": "3.723.0",
-                "@aws-sdk/credential-provider-node": "3.723.0",
-                "@aws-sdk/middleware-host-header": "3.723.0",
-                "@aws-sdk/middleware-logger": "3.723.0",
-                "@aws-sdk/middleware-recursion-detection": "3.723.0",
-                "@aws-sdk/middleware-user-agent": "3.723.0",
-                "@aws-sdk/region-config-resolver": "3.723.0",
-                "@aws-sdk/types": "3.723.0",
-                "@aws-sdk/util-endpoints": "3.723.0",
-                "@aws-sdk/util-user-agent-browser": "3.723.0",
-                "@aws-sdk/util-user-agent-node": "3.723.0",
-                "@smithy/config-resolver": "^4.0.0",
-                "@smithy/core": "^3.0.0",
-                "@smithy/fetch-http-handler": "^5.0.0",
-                "@smithy/hash-node": "^4.0.0",
-                "@smithy/invalid-dependency": "^4.0.0",
-                "@smithy/middleware-content-length": "^4.0.0",
-                "@smithy/middleware-endpoint": "^4.0.0",
-                "@smithy/middleware-retry": "^4.0.0",
-                "@smithy/middleware-serde": "^4.0.0",
-                "@smithy/middleware-stack": "^4.0.0",
-                "@smithy/node-config-provider": "^4.0.0",
-                "@smithy/node-http-handler": "^4.0.0",
-                "@smithy/protocol-http": "^5.0.0",
-                "@smithy/smithy-client": "^4.0.0",
-                "@smithy/types": "^4.0.0",
-                "@smithy/url-parser": "^4.0.0",
-                "@smithy/util-base64": "^4.0.0",
-                "@smithy/util-body-length-browser": "^4.0.0",
-                "@smithy/util-body-length-node": "^4.0.0",
-                "@smithy/util-defaults-mode-browser": "^4.0.0",
-                "@smithy/util-defaults-mode-node": "^4.0.0",
-                "@smithy/util-endpoints": "^3.0.0",
-                "@smithy/util-middleware": "^4.0.0",
-                "@smithy/util-retry": "^4.0.0",
-                "@smithy/util-utf8": "^4.0.0",
-                "tslib": "^2.6.2"
-            },
-            "engines": {
-                "node": ">=18.0.0"
-            },
-            "peerDependencies": {
-                "@aws-sdk/client-sts": "^3.723.0"
-            }
-        },
-        "node_modules/@aws-sdk/client-sts": {
-            "version": "3.723.0",
-            "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.723.0.tgz",
-            "integrity": "sha512-YyN8x4MI/jMb4LpHsLf+VYqvbColMK8aZeGWVk2fTFsmt8lpTYGaGC1yybSwGX42mZ4W8ucu8SAYSbUraJZEjA==",
+            "version": "3.734.0",
+            "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.734.0.tgz",
+            "integrity": "sha512-oerepp0mut9VlgTwnG5Ds/lb0C0b2/rQ+hL/rF6q+HGKPfGsCuPvFx1GtwGKCXd49ase88/jVgrhcA9OQbz3kg==",
             "license": "Apache-2.0",
             "dependencies": {
                 "@aws-crypto/sha256-browser": "5.2.0",
                 "@aws-crypto/sha256-js": "5.2.0",
-                "@aws-sdk/client-sso-oidc": "3.723.0",
-                "@aws-sdk/core": "3.723.0",
-                "@aws-sdk/credential-provider-node": "3.723.0",
-                "@aws-sdk/middleware-host-header": "3.723.0",
-                "@aws-sdk/middleware-logger": "3.723.0",
-                "@aws-sdk/middleware-recursion-detection": "3.723.0",
-                "@aws-sdk/middleware-user-agent": "3.723.0",
-                "@aws-sdk/region-config-resolver": "3.723.0",
-                "@aws-sdk/types": "3.723.0",
-                "@aws-sdk/util-endpoints": "3.723.0",
-                "@aws-sdk/util-user-agent-browser": "3.723.0",
-                "@aws-sdk/util-user-agent-node": "3.723.0",
-                "@smithy/config-resolver": "^4.0.0",
-                "@smithy/core": "^3.0.0",
-                "@smithy/fetch-http-handler": "^5.0.0",
-                "@smithy/hash-node": "^4.0.0",
-                "@smithy/invalid-dependency": "^4.0.0",
-                "@smithy/middleware-content-length": "^4.0.0",
-                "@smithy/middleware-endpoint": "^4.0.0",
-                "@smithy/middleware-retry": "^4.0.0",
-                "@smithy/middleware-serde": "^4.0.0",
-                "@smithy/middleware-stack": "^4.0.0",
-                "@smithy/node-config-provider": "^4.0.0",
-                "@smithy/node-http-handler": "^4.0.0",
-                "@smithy/protocol-http": "^5.0.0",
-                "@smithy/smithy-client": "^4.0.0",
-                "@smithy/types": "^4.0.0",
-                "@smithy/url-parser": "^4.0.0",
+                "@aws-sdk/core": "3.734.0",
+                "@aws-sdk/middleware-host-header": "3.734.0",
+                "@aws-sdk/middleware-logger": "3.734.0",
+                "@aws-sdk/middleware-recursion-detection": "3.734.0",
+                "@aws-sdk/middleware-user-agent": "3.734.0",
+                "@aws-sdk/region-config-resolver": "3.734.0",
+                "@aws-sdk/types": "3.734.0",
+                "@aws-sdk/util-endpoints": "3.734.0",
+                "@aws-sdk/util-user-agent-browser": "3.734.0",
+                "@aws-sdk/util-user-agent-node": "3.734.0",
+                "@smithy/config-resolver": "^4.0.1",
+                "@smithy/core": "^3.1.1",
+                "@smithy/fetch-http-handler": "^5.0.1",
+                "@smithy/hash-node": "^4.0.1",
+                "@smithy/invalid-dependency": "^4.0.1",
+                "@smithy/middleware-content-length": "^4.0.1",
+                "@smithy/middleware-endpoint": "^4.0.2",
+                "@smithy/middleware-retry": "^4.0.3",
+                "@smithy/middleware-serde": "^4.0.1",
+                "@smithy/middleware-stack": "^4.0.1",
+                "@smithy/node-config-provider": "^4.0.1",
+                "@smithy/node-http-handler": "^4.0.2",
+                "@smithy/protocol-http": "^5.0.1",
+                "@smithy/smithy-client": "^4.1.2",
+                "@smithy/types": "^4.1.0",
+                "@smithy/url-parser": "^4.0.1",
                 "@smithy/util-base64": "^4.0.0",
                 "@smithy/util-body-length-browser": "^4.0.0",
                 "@smithy/util-body-length-node": "^4.0.0",
-                "@smithy/util-defaults-mode-browser": "^4.0.0",
-                "@smithy/util-defaults-mode-node": "^4.0.0",
-                "@smithy/util-endpoints": "^3.0.0",
-                "@smithy/util-middleware": "^4.0.0",
-                "@smithy/util-retry": "^4.0.0",
+                "@smithy/util-defaults-mode-browser": "^4.0.3",
+                "@smithy/util-defaults-mode-node": "^4.0.3",
+                "@smithy/util-endpoints": "^3.0.1",
+                "@smithy/util-middleware": "^4.0.1",
+                "@smithy/util-retry": "^4.0.1",
                 "@smithy/util-utf8": "^4.0.0",
                 "tslib": "^2.6.2"
             },
@@ -894,20 +804,20 @@
             }
         },
         "node_modules/@aws-sdk/core": {
-            "version": "3.723.0",
-            "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.723.0.tgz",
-            "integrity": "sha512-UraXNmvqj3vScSsTkjMwQkhei30BhXlW5WxX6JacMKVtl95c7z0qOXquTWeTalYkFfulfdirUhvSZrl+hcyqTw==",
-            "license": "Apache-2.0",
-            "dependencies": {
-                "@aws-sdk/types": "3.723.0",
-                "@smithy/core": "^3.0.0",
-                "@smithy/node-config-provider": "^4.0.0",
-                "@smithy/property-provider": "^4.0.0",
-                "@smithy/protocol-http": "^5.0.0",
-                "@smithy/signature-v4": "^5.0.0",
-                "@smithy/smithy-client": "^4.0.0",
-                "@smithy/types": "^4.0.0",
-                "@smithy/util-middleware": "^4.0.0",
+            "version": "3.734.0",
+            "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.734.0.tgz",
+            "integrity": "sha512-SxnDqf3vobdm50OLyAKfqZetv6zzwnSqwIwd3jrbopxxHKqNIM/I0xcYjD6Tn+mPig+u7iRKb9q3QnEooFTlmg==",
+            "license": "Apache-2.0",
+            "dependencies": {
+                "@aws-sdk/types": "3.734.0",
+                "@smithy/core": "^3.1.1",
+                "@smithy/node-config-provider": "^4.0.1",
+                "@smithy/property-provider": "^4.0.1",
+                "@smithy/protocol-http": "^5.0.1",
+                "@smithy/signature-v4": "^5.0.1",
+                "@smithy/smithy-client": "^4.1.2",
+                "@smithy/types": "^4.1.0",
+                "@smithy/util-middleware": "^4.0.1",
                 "fast-xml-parser": "4.4.1",
                 "tslib": "^2.6.2"
             },
@@ -938,15 +848,15 @@
             }
         },
         "node_modules/@aws-sdk/credential-provider-cognito-identity": {
-            "version": "3.723.0",
-            "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-cognito-identity/-/credential-provider-cognito-identity-3.723.0.tgz",
-            "integrity": "sha512-QsI3Y9isqJAttxtGiPgm/moa/SWJl4EaCfwBLDTI/gZpYdzYkqeYf6r52mhCZ0tQm2CHvXrt1eHiTqD9jINhEA==",
+            "version": "3.734.0",
+            "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-cognito-identity/-/credential-provider-cognito-identity-3.734.0.tgz",
+            "integrity": "sha512-9/5SZsg7aZVssWFPWedRv9UNFMI3Vjf83DqVQUCzsSgpIQNtqlxC30WeFXtC/rP5ulOqmF5xHs9zv3bcETAzsg==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@aws-sdk/client-cognito-identity": "3.723.0",
-                "@aws-sdk/types": "3.723.0",
-                "@smithy/property-provider": "^4.0.0",
-                "@smithy/types": "^4.0.0",
+                "@aws-sdk/client-cognito-identity": "3.734.0",
+                "@aws-sdk/types": "3.734.0",
+                "@smithy/property-provider": "^4.0.1",
+                "@smithy/types": "^4.1.0",
                 "tslib": "^2.6.2"
             },
             "engines": {
@@ -954,15 +864,15 @@
             }
         },
         "node_modules/@aws-sdk/credential-provider-env": {
-            "version": "3.723.0",
-            "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.723.0.tgz",
-            "integrity": "sha512-OuH2yULYUHTVDUotBoP/9AEUIJPn81GQ/YBtZLoo2QyezRJ2QiO/1epVtbJlhNZRwXrToLEDmQGA2QfC8c7pbA==",
+            "version": "3.734.0",
+            "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.734.0.tgz",
+            "integrity": "sha512-gtRkzYTGafnm1FPpiNO8VBmJrYMoxhDlGPYDVcijzx3DlF8dhWnowuSBCxLSi+MJMx5hvwrX2A+e/q0QAeHqmw==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@aws-sdk/core": "3.723.0",
-                "@aws-sdk/types": "3.723.0",
-                "@smithy/property-provider": "^4.0.0",
-                "@smithy/types": "^4.0.0",
+                "@aws-sdk/core": "3.734.0",
+                "@aws-sdk/types": "3.734.0",
+                "@smithy/property-provider": "^4.0.1",
+                "@smithy/types": "^4.1.0",
                 "tslib": "^2.6.2"
             },
             "engines": {
@@ -970,20 +880,20 @@
             }
         },
         "node_modules/@aws-sdk/credential-provider-http": {
-            "version": "3.723.0",
-            "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.723.0.tgz",
-            "integrity": "sha512-DTsKC6xo/kz/ZSs1IcdbQMTgiYbpGTGEd83kngFc1bzmw7AmK92DBZKNZpumf8R/UfSpTcj9zzUUmrWz1kD0eQ==",
-            "license": "Apache-2.0",
-            "dependencies": {
-                "@aws-sdk/core": "3.723.0",
-                "@aws-sdk/types": "3.723.0",
-                "@smithy/fetch-http-handler": "^5.0.0",
-                "@smithy/node-http-handler": "^4.0.0",
-                "@smithy/property-provider": "^4.0.0",
-                "@smithy/protocol-http": "^5.0.0",
-                "@smithy/smithy-client": "^4.0.0",
-                "@smithy/types": "^4.0.0",
-                "@smithy/util-stream": "^4.0.0",
+            "version": "3.734.0",
+            "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.734.0.tgz",
+            "integrity": "sha512-JFSL6xhONsq+hKM8xroIPhM5/FOhiQ1cov0lZxhzZWj6Ai3UAjucy3zyIFDr9MgP1KfCYNdvyaUq9/o+HWvEDg==",
+            "license": "Apache-2.0",
+            "dependencies": {
+                "@aws-sdk/core": "3.734.0",
+                "@aws-sdk/types": "3.734.0",
+                "@smithy/fetch-http-handler": "^5.0.1",
+                "@smithy/node-http-handler": "^4.0.2",
+                "@smithy/property-provider": "^4.0.1",
+                "@smithy/protocol-http": "^5.0.1",
+                "@smithy/smithy-client": "^4.1.2",
+                "@smithy/types": "^4.1.0",
+                "@smithy/util-stream": "^4.0.2",
                 "tslib": "^2.6.2"
             },
             "engines": {
@@ -991,48 +901,46 @@
             }
         },
         "node_modules/@aws-sdk/credential-provider-ini": {
-            "version": "3.723.0",
-            "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.723.0.tgz",
-            "integrity": "sha512-fWRLksuSG851e7Iu+ltMrQTM7C/5iI9OkxAmCYblcCetAzjTRmMB2arku0Z83D8edIZEQtOJMt5oQ9KNg43pzg==",
-            "license": "Apache-2.0",
-            "dependencies": {
-                "@aws-sdk/core": "3.723.0",
-                "@aws-sdk/credential-provider-env": "3.723.0",
-                "@aws-sdk/credential-provider-http": "3.723.0",
-                "@aws-sdk/credential-provider-process": "3.723.0",
-                "@aws-sdk/credential-provider-sso": "3.723.0",
-                "@aws-sdk/credential-provider-web-identity": "3.723.0",
-                "@aws-sdk/types": "3.723.0",
-                "@smithy/credential-provider-imds": "^4.0.0",
-                "@smithy/property-provider": "^4.0.0",
-                "@smithy/shared-ini-file-loader": "^4.0.0",
-                "@smithy/types": "^4.0.0",
+            "version": "3.734.0",
+            "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.734.0.tgz",
+            "integrity": "sha512-HEyaM/hWI7dNmb4NhdlcDLcgJvrilk8G4DQX6qz0i4pBZGC2l4iffuqP8K6ZQjUfz5/6894PzeFuhTORAMd+cg==",
+            "license": "Apache-2.0",
+            "dependencies": {
+                "@aws-sdk/core": "3.734.0",
+                "@aws-sdk/credential-provider-env": "3.734.0",
+                "@aws-sdk/credential-provider-http": "3.734.0",
+                "@aws-sdk/credential-provider-process": "3.734.0",
+                "@aws-sdk/credential-provider-sso": "3.734.0",
+                "@aws-sdk/credential-provider-web-identity": "3.734.0",
+                "@aws-sdk/nested-clients": "3.734.0",
+                "@aws-sdk/types": "3.734.0",
+                "@smithy/credential-provider-imds": "^4.0.1",
+                "@smithy/property-provider": "^4.0.1",
+                "@smithy/shared-ini-file-loader": "^4.0.1",
+                "@smithy/types": "^4.1.0",
                 "tslib": "^2.6.2"
             },
             "engines": {
                 "node": ">=18.0.0"
-            },
-            "peerDependencies": {
-                "@aws-sdk/client-sts": "^3.723.0"
             }
         },
         "node_modules/@aws-sdk/credential-provider-node": {
-            "version": "3.723.0",
-            "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.723.0.tgz",
-            "integrity": "sha512-OyLHt+aY+rkuRejigcxviS5RLUBcqbxhDTSNfP8dp9I+1SP610qRLpTIROvtKwXZssFcATpPfgikFtVYRrihXQ==",
-            "license": "Apache-2.0",
-            "dependencies": {
-                "@aws-sdk/credential-provider-env": "3.723.0",
-                "@aws-sdk/credential-provider-http": "3.723.0",
-                "@aws-sdk/credential-provider-ini": "3.723.0",
-                "@aws-sdk/credential-provider-process": "3.723.0",
-                "@aws-sdk/credential-provider-sso": "3.723.0",
-                "@aws-sdk/credential-provider-web-identity": "3.723.0",
-                "@aws-sdk/types": "3.723.0",
-                "@smithy/credential-provider-imds": "^4.0.0",
-                "@smithy/property-provider": "^4.0.0",
-                "@smithy/shared-ini-file-loader": "^4.0.0",
-                "@smithy/types": "^4.0.0",
+            "version": "3.734.0",
+            "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.734.0.tgz",
+            "integrity": "sha512-9NOSNbkPVb91JwaXOhyfahkzAwWdMsbWHL6fh5/PHlXYpsDjfIfT23I++toepNF2nODAJNLnOEHGYIxgNgf6jQ==",
+            "license": "Apache-2.0",
+            "dependencies": {
+                "@aws-sdk/credential-provider-env": "3.734.0",
+                "@aws-sdk/credential-provider-http": "3.734.0",
+                "@aws-sdk/credential-provider-ini": "3.734.0",
+                "@aws-sdk/credential-provider-process": "3.734.0",
+                "@aws-sdk/credential-provider-sso": "3.734.0",
+                "@aws-sdk/credential-provider-web-identity": "3.734.0",
+                "@aws-sdk/types": "3.734.0",
+                "@smithy/credential-provider-imds": "^4.0.1",
+                "@smithy/property-provider": "^4.0.1",
+                "@smithy/shared-ini-file-loader": "^4.0.1",
+                "@smithy/types": "^4.1.0",
                 "tslib": "^2.6.2"
             },
             "engines": {
@@ -1040,16 +948,16 @@
             }
         },
         "node_modules/@aws-sdk/credential-provider-process": {
-            "version": "3.723.0",
-            "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.723.0.tgz",
-            "integrity": "sha512-fgupvUjz1+jeoCBA7GMv0L6xEk92IN6VdF4YcFhsgRHlHvNgm7ayaoKQg7pz2JAAhG/3jPX6fp0ASNy+xOhmPA==",
+            "version": "3.734.0",
+            "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.734.0.tgz",
+            "integrity": "sha512-zvjsUo+bkYn2vjT+EtLWu3eD6me+uun+Hws1IyWej/fKFAqiBPwyeyCgU7qjkiPQSXqk1U9+/HG9IQ6Iiz+eBw==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@aws-sdk/core": "3.723.0",
-                "@aws-sdk/types": "3.723.0",
-                "@smithy/property-provider": "^4.0.0",
-                "@smithy/shared-ini-file-loader": "^4.0.0",
-                "@smithy/types": "^4.0.0",
+                "@aws-sdk/core": "3.734.0",
+                "@aws-sdk/types": "3.734.0",
+                "@smithy/property-provider": "^4.0.1",
+                "@smithy/shared-ini-file-loader": "^4.0.1",
+                "@smithy/types": "^4.1.0",
                 "tslib": "^2.6.2"
             },
             "engines": {
@@ -1057,18 +965,18 @@
             }
         },
         "node_modules/@aws-sdk/credential-provider-sso": {
-            "version": "3.723.0",
-            "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.723.0.tgz",
-            "integrity": "sha512-laCnxrk0pgUegU+ib6rj1/Uv51wei+cH8crvBJddybc8EDn7Qht61tCvBwf3o33qUDC+ZWZZewlpSebf+J+tBw==",
-            "license": "Apache-2.0",
-            "dependencies": {
-                "@aws-sdk/client-sso": "3.723.0",
-                "@aws-sdk/core": "3.723.0",
-                "@aws-sdk/token-providers": "3.723.0",
-                "@aws-sdk/types": "3.723.0",
-                "@smithy/property-provider": "^4.0.0",
-                "@smithy/shared-ini-file-loader": "^4.0.0",
-                "@smithy/types": "^4.0.0",
+            "version": "3.734.0",
+            "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.734.0.tgz",
+            "integrity": "sha512-cCwwcgUBJOsV/ddyh1OGb4gKYWEaTeTsqaAK19hiNINfYV/DO9r4RMlnWAo84sSBfJuj9shUNsxzyoe6K7R92Q==",
+            "license": "Apache-2.0",
+            "dependencies": {
+                "@aws-sdk/client-sso": "3.734.0",
+                "@aws-sdk/core": "3.734.0",
+                "@aws-sdk/token-providers": "3.734.0",
+                "@aws-sdk/types": "3.734.0",
+                "@smithy/property-provider": "^4.0.1",
+                "@smithy/shared-ini-file-loader": "^4.0.1",
+                "@smithy/types": "^4.1.0",
                 "tslib": "^2.6.2"
             },
             "engines": {
@@ -1076,46 +984,44 @@
             }
         },
         "node_modules/@aws-sdk/credential-provider-web-identity": {
-            "version": "3.723.0",
-            "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.723.0.tgz",
-            "integrity": "sha512-tl7pojbFbr3qLcOE6xWaNCf1zEfZrIdSJtOPeSXfV/thFMMAvIjgf3YN6Zo1a6cxGee8zrV/C8PgOH33n+Ev/A==",
+            "version": "3.734.0",
+            "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.734.0.tgz",
+            "integrity": "sha512-t4OSOerc+ppK541/Iyn1AS40+2vT/qE+MFMotFkhCgCJbApeRF2ozEdnDN6tGmnl4ybcUuxnp9JWLjwDVlR/4g==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@aws-sdk/core": "3.723.0",
-                "@aws-sdk/types": "3.723.0",
-                "@smithy/property-provider": "^4.0.0",
-                "@smithy/types": "^4.0.0",
+                "@aws-sdk/core": "3.734.0",
+                "@aws-sdk/nested-clients": "3.734.0",
+                "@aws-sdk/types": "3.734.0",
+                "@smithy/property-provider": "^4.0.1",
+                "@smithy/types": "^4.1.0",
                 "tslib": "^2.6.2"
             },
             "engines": {
                 "node": ">=18.0.0"
-            },
-            "peerDependencies": {
-                "@aws-sdk/client-sts": "^3.723.0"
             }
         },
         "node_modules/@aws-sdk/credential-providers": {
-            "version": "3.723.0",
-            "resolved": "https://registry.npmjs.org/@aws-sdk/credential-providers/-/credential-providers-3.723.0.tgz",
-            "integrity": "sha512-poDyQ5/O8fdhVP+Nr3nNYS9jBpmrwy9G7ipMYceHbztiBrAmFBsB3dyFKytRM1+z5Gz3icOHamv5ZinDhyBWsA==",
-            "license": "Apache-2.0",
-            "dependencies": {
-                "@aws-sdk/client-cognito-identity": "3.723.0",
-                "@aws-sdk/client-sso": "3.723.0",
-                "@aws-sdk/client-sts": "3.723.0",
-                "@aws-sdk/core": "3.723.0",
-                "@aws-sdk/credential-provider-cognito-identity": "3.723.0",
-                "@aws-sdk/credential-provider-env": "3.723.0",
-                "@aws-sdk/credential-provider-http": "3.723.0",
-                "@aws-sdk/credential-provider-ini": "3.723.0",
-                "@aws-sdk/credential-provider-node": "3.723.0",
-                "@aws-sdk/credential-provider-process": "3.723.0",
-                "@aws-sdk/credential-provider-sso": "3.723.0",
-                "@aws-sdk/credential-provider-web-identity": "3.723.0",
-                "@aws-sdk/types": "3.723.0",
-                "@smithy/credential-provider-imds": "^4.0.0",
-                "@smithy/property-provider": "^4.0.0",
-                "@smithy/types": "^4.0.0",
+            "version": "3.734.0",
+            "resolved": "https://registry.npmjs.org/@aws-sdk/credential-providers/-/credential-providers-3.734.0.tgz",
+            "integrity": "sha512-3q76ngVxwX/kSRA0bjH7hUkIOVf/38aACmYpbwwr7jyRU3Cpbsj57W9YtRd7zS9/A4Jt6fYx7VFEA52ajyoGAQ==",
+            "license": "Apache-2.0",
+            "dependencies": {
+                "@aws-sdk/client-cognito-identity": "3.734.0",
+                "@aws-sdk/core": "3.734.0",
+                "@aws-sdk/credential-provider-cognito-identity": "3.734.0",
+                "@aws-sdk/credential-provider-env": "3.734.0",
+                "@aws-sdk/credential-provider-http": "3.734.0",
+                "@aws-sdk/credential-provider-ini": "3.734.0",
+                "@aws-sdk/credential-provider-node": "3.734.0",
+                "@aws-sdk/credential-provider-process": "3.734.0",
+                "@aws-sdk/credential-provider-sso": "3.734.0",
+                "@aws-sdk/credential-provider-web-identity": "3.734.0",
+                "@aws-sdk/nested-clients": "3.734.0",
+                "@aws-sdk/types": "3.734.0",
+                "@smithy/core": "^3.1.1",
+                "@smithy/credential-provider-imds": "^4.0.1",
+                "@smithy/property-provider": "^4.0.1",
+                "@smithy/types": "^4.1.0",
                 "tslib": "^2.6.2"
             },
             "engines": {
@@ -1123,14 +1029,14 @@
             }
         },
         "node_modules/@aws-sdk/middleware-host-header": {
-            "version": "3.723.0",
-            "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.723.0.tgz",
-            "integrity": "sha512-LLVzLvk299pd7v4jN9yOSaWDZDfH0SnBPb6q+FDPaOCMGBY8kuwQso7e/ozIKSmZHRMGO3IZrflasHM+rI+2YQ==",
+            "version": "3.734.0",
+            "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.734.0.tgz",
+            "integrity": "sha512-LW7RRgSOHHBzWZnigNsDIzu3AiwtjeI2X66v+Wn1P1u+eXssy1+up4ZY/h+t2sU4LU36UvEf+jrZti9c6vRnFw==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@aws-sdk/types": "3.723.0",
-                "@smithy/protocol-http": "^5.0.0",
-                "@smithy/types": "^4.0.0",
+                "@aws-sdk/types": "3.734.0",
+                "@smithy/protocol-http": "^5.0.1",
+                "@smithy/types": "^4.1.0",
                 "tslib": "^2.6.2"
             },
             "engines": {
@@ -1138,13 +1044,13 @@
             }
         },
         "node_modules/@aws-sdk/middleware-logger": {
-            "version": "3.723.0",
-            "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.723.0.tgz",
-            "integrity": "sha512-chASQfDG5NJ8s5smydOEnNK7N0gDMyuPbx7dYYcm1t/PKtnVfvWF+DHCTrRC2Ej76gLJVCVizlAJKM8v8Kg3cg==",
+            "version": "3.734.0",
+            "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.734.0.tgz",
+            "integrity": "sha512-mUMFITpJUW3LcKvFok176eI5zXAUomVtahb9IQBwLzkqFYOrMJvWAvoV4yuxrJ8TlQBG8gyEnkb9SnhZvjg67w==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@aws-sdk/types": "3.723.0",
-                "@smithy/types": "^4.0.0",
+                "@aws-sdk/types": "3.734.0",
+                "@smithy/types": "^4.1.0",
                 "tslib": "^2.6.2"
             },
             "engines": {
@@ -1152,14 +1058,14 @@
             }
         },
         "node_modules/@aws-sdk/middleware-recursion-detection": {
-            "version": "3.723.0",
-            "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.723.0.tgz",
-            "integrity": "sha512-7usZMtoynT9/jxL/rkuDOFQ0C2mhXl4yCm67Rg7GNTstl67u7w5WN1aIRImMeztaKlw8ExjoTyo6WTs1Kceh7A==",
+            "version": "3.734.0",
+            "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.734.0.tgz",
+            "integrity": "sha512-CUat2d9ITsFc2XsmeiRQO96iWpxSKYFjxvj27Hc7vo87YUHRnfMfnc8jw1EpxEwMcvBD7LsRa6vDNky6AjcrFA==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@aws-sdk/types": "3.723.0",
-                "@smithy/protocol-http": "^5.0.0",
-                "@smithy/types": "^4.0.0",
+                "@aws-sdk/types": "3.734.0",
+                "@smithy/protocol-http": "^5.0.1",
+                "@smithy/types": "^4.1.0",
                 "tslib": "^2.6.2"
             },
             "engines": {
@@ -1167,17 +1073,66 @@
             }
         },
         "node_modules/@aws-sdk/middleware-user-agent": {
-            "version": "3.723.0",
-            "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.723.0.tgz",
-            "integrity": "sha512-AY5H2vD3IRElplBO4DCyRMNnOG/4/cb0tsHyLe1HJy0hdUF6eY5z/VVjKJoKbbDk7ui9euyOBWslXxDyLmyPWg==",
+            "version": "3.734.0",
+            "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.734.0.tgz",
+            "integrity": "sha512-MFVzLWRkfFz02GqGPjqSOteLe5kPfElUrXZft1eElnqulqs6RJfVSpOV7mO90gu293tNAeggMWAVSGRPKIYVMg==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@aws-sdk/core": "3.723.0",
-                "@aws-sdk/types": "3.723.0",
-                "@aws-sdk/util-endpoints": "3.723.0",
-                "@smithy/core": "^3.0.0",
-                "@smithy/protocol-http": "^5.0.0",
-                "@smithy/types": "^4.0.0",
+                "@aws-sdk/core": "3.734.0",
+                "@aws-sdk/types": "3.734.0",
+                "@aws-sdk/util-endpoints": "3.734.0",
+                "@smithy/core": "^3.1.1",
+                "@smithy/protocol-http": "^5.0.1",
+                "@smithy/types": "^4.1.0",
+                "tslib": "^2.6.2"
+            },
+            "engines": {
+                "node": ">=18.0.0"
+            }
+        },
+        "node_modules/@aws-sdk/nested-clients": {
+            "version": "3.734.0",
+            "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.734.0.tgz",
+            "integrity": "sha512-iph2XUy8UzIfdJFWo1r0Zng9uWj3253yvW9gljhtu+y/LNmNvSnJxQk1f3D2BC5WmcoPZqTS3UsycT3mLPSzWA==",
+            "license": "Apache-2.0",
+            "dependencies": {
+                "@aws-crypto/sha256-browser": "5.2.0",
+                "@aws-crypto/sha256-js": "5.2.0",
+                "@aws-sdk/core": "3.734.0",
+                "@aws-sdk/middleware-host-header": "3.734.0",
+                "@aws-sdk/middleware-logger": "3.734.0",
+                "@aws-sdk/middleware-recursion-detection": "3.734.0",
+                "@aws-sdk/middleware-user-agent": "3.734.0",
+                "@aws-sdk/region-config-resolver": "3.734.0",
+                "@aws-sdk/types": "3.734.0",
+                "@aws-sdk/util-endpoints": "3.734.0",
+                "@aws-sdk/util-user-agent-browser": "3.734.0",
+                "@aws-sdk/util-user-agent-node": "3.734.0",
+                "@smithy/config-resolver": "^4.0.1",
+                "@smithy/core": "^3.1.1",
+                "@smithy/fetch-http-handler": "^5.0.1",
+                "@smithy/hash-node": "^4.0.1",
+                "@smithy/invalid-dependency": "^4.0.1",
+                "@smithy/middleware-content-length": "^4.0.1",
+                "@smithy/middleware-endpoint": "^4.0.2",
+                "@smithy/middleware-retry": "^4.0.3",
+                "@smithy/middleware-serde": "^4.0.1",
+                "@smithy/middleware-stack": "^4.0.1",
+                "@smithy/node-config-provider": "^4.0.1",
+                "@smithy/node-http-handler": "^4.0.2",
+                "@smithy/protocol-http": "^5.0.1",
+                "@smithy/smithy-client": "^4.1.2",
+                "@smithy/types": "^4.1.0",
+                "@smithy/url-parser": "^4.0.1",
+                "@smithy/util-base64": "^4.0.0",
+                "@smithy/util-body-length-browser": "^4.0.0",
+                "@smithy/util-body-length-node": "^4.0.0",
+                "@smithy/util-defaults-mode-browser": "^4.0.3",
+                "@smithy/util-defaults-mode-node": "^4.0.3",
+                "@smithy/util-endpoints": "^3.0.1",
+                "@smithy/util-middleware": "^4.0.1",
+                "@smithy/util-retry": "^4.0.1",
+                "@smithy/util-utf8": "^4.0.0",
                 "tslib": "^2.6.2"
             },
             "engines": {
@@ -1224,16 +1179,16 @@
             }
         },
         "node_modules/@aws-sdk/region-config-resolver": {
-            "version": "3.723.0",
-            "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.723.0.tgz",
-            "integrity": "sha512-tGF/Cvch3uQjZIj34LY2mg8M2Dr4kYG8VU8Yd0dFnB1ybOEOveIK/9ypUo9ycZpB9oO6q01KRe5ijBaxNueUQg==",
+            "version": "3.734.0",
+            "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.734.0.tgz",
+            "integrity": "sha512-Lvj1kPRC5IuJBr9DyJ9T9/plkh+EfKLy+12s/mykOy1JaKHDpvj+XGy2YO6YgYVOb8JFtaqloid+5COtje4JTQ==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@aws-sdk/types": "3.723.0",
-                "@smithy/node-config-provider": "^4.0.0",
-                "@smithy/types": "^4.0.0",
+                "@aws-sdk/types": "3.734.0",
+                "@smithy/node-config-provider": "^4.0.1",
+                "@smithy/types": "^4.1.0",
                 "@smithy/util-config-provider": "^4.0.0",
-                "@smithy/util-middleware": "^4.0.0",
+                "@smithy/util-middleware": "^4.0.1",
                 "tslib": "^2.6.2"
             },
             "engines": {
@@ -1360,31 +1315,29 @@
             }
         },
         "node_modules/@aws-sdk/token-providers": {
-            "version": "3.723.0",
-            "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.723.0.tgz",
-            "integrity": "sha512-hniWi1x4JHVwKElANh9afKIMUhAutHVBRD8zo6usr0PAoj+Waf220+1ULS74GXtLXAPCiNXl5Og+PHA7xT8ElQ==",
+            "version": "3.734.0",
+            "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.734.0.tgz",
+            "integrity": "sha512-2U6yWKrjWjZO8Y5SHQxkFvMVWHQWbS0ufqfAIBROqmIZNubOL7jXCiVdEFekz6MZ9LF2tvYGnOW4jX8OKDGfIw==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@aws-sdk/types": "3.723.0",
-                "@smithy/property-provider": "^4.0.0",
-                "@smithy/shared-ini-file-loader": "^4.0.0",
-                "@smithy/types": "^4.0.0",
+                "@aws-sdk/nested-clients": "3.734.0",
+                "@aws-sdk/types": "3.734.0",
+                "@smithy/property-provider": "^4.0.1",
+                "@smithy/shared-ini-file-loader": "^4.0.1",
+                "@smithy/types": "^4.1.0",
                 "tslib": "^2.6.2"
             },
             "engines": {
                 "node": ">=18.0.0"
-            },
-            "peerDependencies": {
-                "@aws-sdk/client-sso-oidc": "^3.723.0"
             }
         },
         "node_modules/@aws-sdk/types": {
-            "version": "3.723.0",
-            "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.723.0.tgz",
-            "integrity": "sha512-LmK3kwiMZG1y5g3LGihT9mNkeNOmwEyPk6HGcJqh0wOSV4QpWoKu2epyKE4MLQNUUlz2kOVbVbOrwmI6ZcteuA==",
+            "version": "3.734.0",
+            "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.734.0.tgz",
+            "integrity": "sha512-o11tSPTT70nAkGV1fN9wm/hAIiLPyWX6SuGf+9JyTp7S/rC2cFWhR26MvA69nplcjNaXVzB0f+QFrLXXjOqCrg==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@smithy/types": "^4.0.0",
+                "@smithy/types": "^4.1.0",
                 "tslib": "^2.6.2"
             },
             "engines": {
@@ -1392,14 +1345,14 @@
             }
         },
         "node_modules/@aws-sdk/util-endpoints": {
-            "version": "3.723.0",
-            "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.723.0.tgz",
-            "integrity": "sha512-vR1ZfAUvrTtdA1Q78QxgR8TFgi2gzk+N4EmNjbyR5hHmeOXuaKRdhbNQAzLPYVe1aNUpoiy9cl8mWkg9SrNHBw==",
+            "version": "3.734.0",
+            "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.734.0.tgz",
+            "integrity": "sha512-w2+/E88NUbqql6uCVAsmMxDQKu7vsKV0KqhlQb0lL+RCq4zy07yXYptVNs13qrnuTfyX7uPXkXrlugvK9R1Ucg==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@aws-sdk/types": "3.723.0",
-                "@smithy/types": "^4.0.0",
-                "@smithy/util-endpoints": "^3.0.0",
+                "@aws-sdk/types": "3.734.0",
+                "@smithy/types": "^4.1.0",
+                "@smithy/util-endpoints": "^3.0.1",
                 "tslib": "^2.6.2"
             },
             "engines": {
@@ -1419,27 +1372,27 @@
             }
         },
         "node_modules/@aws-sdk/util-user-agent-browser": {
-            "version": "3.723.0",
-            "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.723.0.tgz",
-            "integrity": "sha512-Wh9I6j2jLhNFq6fmXydIpqD1WyQLyTfSxjW9B+PXSnPyk3jtQW8AKQur7p97rO8LAUzVI0bv8kb3ZzDEVbquIg==",
+            "version": "3.734.0",
+            "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.734.0.tgz",
+            "integrity": "sha512-xQTCus6Q9LwUuALW+S76OL0jcWtMOVu14q+GoLnWPUM7QeUw963oQcLhF7oq0CtaLLKyl4GOUfcwc773Zmwwng==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@aws-sdk/types": "3.723.0",
-                "@smithy/types": "^4.0.0",
+                "@aws-sdk/types": "3.734.0",
+                "@smithy/types": "^4.1.0",
                 "bowser": "^2.11.0",
                 "tslib": "^2.6.2"
             }
         },
         "node_modules/@aws-sdk/util-user-agent-node": {
-            "version": "3.723.0",
-            "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.723.0.tgz",
-            "integrity": "sha512-uCtW5sGq8jCwA9w57TvVRIwNnPbSDD1lJaTIgotf7Jit2bTrYR64thgMy/drL5yU5aHOdFIQljqn/5aDXLtTJw==",
+            "version": "3.734.0",
+            "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.734.0.tgz",
+            "integrity": "sha512-c6Iinh+RVQKs6jYUFQ64htOU2HUXFQ3TVx+8Tu3EDF19+9vzWi9UukhIMH9rqyyEXIAkk9XL7avt8y2Uyw2dGA==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@aws-sdk/middleware-user-agent": "3.723.0",
-                "@aws-sdk/types": "3.723.0",
-                "@smithy/node-config-provider": "^4.0.0",
-                "@smithy/types": "^4.0.0",
+                "@aws-sdk/middleware-user-agent": "3.734.0",
+                "@aws-sdk/types": "3.734.0",
+                "@smithy/node-config-provider": "^4.0.1",
+                "@smithy/types": "^4.1.0",
                 "tslib": "^2.6.2"
             },
             "engines": {
@@ -1549,9 +1502,9 @@
             }
         },
         "node_modules/@azure/core-rest-pipeline": {
-            "version": "1.18.1",
-            "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.18.1.tgz",
-            "integrity": "sha512-/wS73UEDrxroUEVywEm7J0p2c+IIiVxyfigCGfsKvCxxCET4V/Hef2aURqltrXMRjNmdmt5IuOgIpl8f6xdO5A==",
+            "version": "1.18.2",
+            "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.18.2.tgz",
+            "integrity": "sha512-IkTf/DWKyCklEtN/WYW3lqEsIaUDshlzWRlZNNwSYtFcCBQz++OtOjxNpm8rr1VcbMS6RpjybQa3u6B6nG0zNw==",
             "license": "MIT",
             "dependencies": {
                 "@azure/abort-controller": "^2.0.0",
@@ -1656,30 +1609,30 @@
             }
         },
         "node_modules/@babel/compat-data": {
-            "version": "7.26.3",
-            "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.3.tgz",
-            "integrity": "sha512-nHIxvKPniQXpmQLb0vhY3VaFb3S0YrTAwpOWJZh1wn3oJPjJk9Asva204PsBdmAE8vpzfHudT8DB0scYvy9q0g==",
+            "version": "7.26.5",
+            "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.5.tgz",
+            "integrity": "sha512-XvcZi1KWf88RVbF9wn8MN6tYFloU5qX8KjuF3E1PVBmJ9eypXfs4GRiJwLuTZL0iSnJUKn1BFPa5BPZZJyFzPg==",
             "license": "MIT",
             "engines": {
                 "node": ">=6.9.0"
             }
         },
         "node_modules/@babel/core": {
-            "version": "7.26.0",
-            "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz",
-            "integrity": "sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==",
+            "version": "7.26.7",
+            "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.7.tgz",
+            "integrity": "sha512-SRijHmF0PSPgLIBYlWnG0hyeJLwXE2CgpsXaMOrtt2yp9/86ALw6oUlj9KYuZ0JN07T4eBMVIW4li/9S1j2BGA==",
             "license": "MIT",
             "dependencies": {
                 "@ampproject/remapping": "^2.2.0",
-                "@babel/code-frame": "^7.26.0",
-                "@babel/generator": "^7.26.0",
-                "@babel/helper-compilation-targets": "^7.25.9",
+                "@babel/code-frame": "^7.26.2",
+                "@babel/generator": "^7.26.5",
+                "@babel/helper-compilation-targets": "^7.26.5",
                 "@babel/helper-module-transforms": "^7.26.0",
-                "@babel/helpers": "^7.26.0",
-                "@babel/parser": "^7.26.0",
+                "@babel/helpers": "^7.26.7",
+                "@babel/parser": "^7.26.7",
                 "@babel/template": "^7.25.9",
-                "@babel/traverse": "^7.25.9",
-                "@babel/types": "^7.26.0",
+                "@babel/traverse": "^7.26.7",
+                "@babel/types": "^7.26.7",
                 "convert-source-map": "^2.0.0",
                 "debug": "^4.1.0",
                 "gensync": "^1.0.0-beta.2",
@@ -1695,13 +1648,13 @@
             }
         },
         "node_modules/@babel/generator": {
-            "version": "7.26.3",
-            "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.3.tgz",
-            "integrity": "sha512-6FF/urZvD0sTeO7k6/B15pMLC4CHUv1426lzr3N01aHJTl046uCAh9LXW/fzeXXjPNCJ6iABW5XaWOsIZB93aQ==",
+            "version": "7.26.5",
+            "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.5.tgz",
+            "integrity": "sha512-2caSP6fN9I7HOe6nqhtft7V4g7/V/gfDsC3Ag4W7kEzzvRGKqiv0pu0HogPiZ3KaVSoNDhUws6IJjDjpfmYIXw==",
             "license": "MIT",
             "dependencies": {
-                "@babel/parser": "^7.26.3",
-                "@babel/types": "^7.26.3",
+                "@babel/parser": "^7.26.5",
+                "@babel/types": "^7.26.5",
                 "@jridgewell/gen-mapping": "^0.3.5",
                 "@jridgewell/trace-mapping": "^0.3.25",
                 "jsesc": "^3.0.2"
@@ -1723,12 +1676,12 @@
             }
         },
         "node_modules/@babel/helper-compilation-targets": {
-            "version": "7.25.9",
-            "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz",
-            "integrity": "sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==",
+            "version": "7.26.5",
+            "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.26.5.tgz",
+            "integrity": "sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==",
             "license": "MIT",
             "dependencies": {
-                "@babel/compat-data": "^7.25.9",
+                "@babel/compat-data": "^7.26.5",
                 "@babel/helper-validator-option": "^7.25.9",
                 "browserslist": "^4.24.0",
                 "lru-cache": "^5.1.1",
@@ -1848,9 +1801,9 @@
             }
         },
         "node_modules/@babel/helper-plugin-utils": {
-            "version": "7.25.9",
-            "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz",
-            "integrity": "sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==",
+            "version": "7.26.5",
+            "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz",
+            "integrity": "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==",
             "license": "MIT",
             "engines": {
                 "node": ">=6.9.0"
@@ -1874,14 +1827,14 @@
             }
         },
         "node_modules/@babel/helper-replace-supers": {
-            "version": "7.25.9",
-            "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.25.9.tgz",
-            "integrity": "sha512-IiDqTOTBQy0sWyeXyGSC5TBJpGFXBkRynjBeXsvbhQFKj2viwJC76Epz35YLU1fpe/Am6Vppb7W7zM4fPQzLsQ==",
+            "version": "7.26.5",
+            "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.26.5.tgz",
+            "integrity": "sha512-bJ6iIVdYX1YooY2X7w1q6VITt+LnUILtNk7zT78ykuwStx8BauCzxvFqFaHjOpW1bVnSUM1PN1f0p5P21wHxvg==",
             "license": "MIT",
             "dependencies": {
                 "@babel/helper-member-expression-to-functions": "^7.25.9",
                 "@babel/helper-optimise-call-expression": "^7.25.9",
-                "@babel/traverse": "^7.25.9"
+                "@babel/traverse": "^7.26.5"
             },
             "engines": {
                 "node": ">=6.9.0"
@@ -1945,25 +1898,25 @@
             }
         },
         "node_modules/@babel/helpers": {
-            "version": "7.26.0",
-            "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz",
-            "integrity": "sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==",
+            "version": "7.26.7",
+            "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.7.tgz",
+            "integrity": "sha512-8NHiL98vsi0mbPQmYAGWwfcFaOy4j2HY49fXJCfuDcdE7fMIsH9a7GdaeXpIBsbT7307WU8KCMp5pUVDNL4f9A==",
             "license": "MIT",
             "dependencies": {
                 "@babel/template": "^7.25.9",
-                "@babel/types": "^7.26.0"
+                "@babel/types": "^7.26.7"
             },
             "engines": {
                 "node": ">=6.9.0"
             }
         },
         "node_modules/@babel/parser": {
-            "version": "7.26.3",
-            "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.3.tgz",
-            "integrity": "sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA==",
+            "version": "7.26.7",
+            "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.7.tgz",
+            "integrity": "sha512-kEvgGGgEjRUutvdVvZhbn/BxVt+5VSpwXz1j3WYXQbXDo8KzFOPNG2GQbdAiNq8g6wn1yKk7C/qrke03a84V+w==",
             "license": "MIT",
             "dependencies": {
-                "@babel/types": "^7.26.3"
+                "@babel/types": "^7.26.7"
             },
             "bin": {
                 "parser": "bin/babel-parser.js"
@@ -2174,12 +2127,12 @@
             }
         },
         "node_modules/@babel/plugin-transform-block-scoped-functions": {
-            "version": "7.25.9",
-            "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.25.9.tgz",
-            "integrity": "sha512-toHc9fzab0ZfenFpsyYinOX0J/5dgJVA2fm64xPewu7CoYHWEivIWKxkK2rMi4r3yQqLnVmheMXRdG+k239CgA==",
+            "version": "7.26.5",
+            "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.26.5.tgz",
+            "integrity": "sha512-chuTSY+hq09+/f5lMj8ZSYgCFpppV2CbYrhNFJ1BFoXpiWPnnAb7R0MqrafCpN8E1+YRrtM1MXZHJdIx8B6rMQ==",
             "license": "MIT",
             "dependencies": {
-                "@babel/helper-plugin-utils": "^7.25.9"
+                "@babel/helper-plugin-utils": "^7.26.5"
             },
             "engines": {
                 "node": ">=6.9.0"
@@ -2578,12 +2531,12 @@
             }
         },
         "node_modules/@babel/plugin-transform-nullish-coalescing-operator": {
-            "version": "7.25.9",
-            "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.25.9.tgz",
-            "integrity": "sha512-ENfftpLZw5EItALAD4WsY/KUWvhUlZndm5GC7G3evUsVeSJB6p0pBeLQUnRnBCBx7zV0RKQjR9kCuwrsIrjWog==",
+            "version": "7.26.6",
+            "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.26.6.tgz",
+            "integrity": "sha512-CKW8Vu+uUZneQCPtXmSBUC6NCAUdya26hWCElAWh5mVSlSRsmiCPUUDKb3Z0szng1hiAJa098Hkhg9o4SE35Qw==",
             "license": "MIT",
             "dependencies": {
-                "@babel/helper-plugin-utils": "^7.25.9"
+                "@babel/helper-plugin-utils": "^7.26.5"
             },
             "engines": {
                 "node": ">=6.9.0"
@@ -2908,12 +2861,12 @@
             }
         },
         "node_modules/@babel/plugin-transform-typeof-symbol": {
-            "version": "7.25.9",
-            "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.25.9.tgz",
-            "integrity": "sha512-v61XqUMiueJROUv66BVIOi0Fv/CUuZuZMl5NkRoCVxLAnMexZ0A3kMe7vvZ0nulxMuMp0Mk6S5hNh48yki08ZA==",
+            "version": "7.26.7",
+            "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.26.7.tgz",
+            "integrity": "sha512-jfoTXXZTgGg36BmhqT3cAYK5qkmqvJpvNrPhaK/52Vgjhw4Rq29s9UqpWWV0D6yuRmgiFH/BUVlkl96zJWqnaw==",
             "license": "MIT",
             "dependencies": {
-                "@babel/helper-plugin-utils": "^7.25.9"
+                "@babel/helper-plugin-utils": "^7.26.5"
             },
             "engines": {
                 "node": ">=6.9.0"
@@ -2986,14 +2939,14 @@
             }
         },
         "node_modules/@babel/preset-env": {
-            "version": "7.26.0",
-            "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.26.0.tgz",
-            "integrity": "sha512-H84Fxq0CQJNdPFT2DrfnylZ3cf5K43rGfWK4LJGPpjKHiZlk0/RzwEus3PDDZZg+/Er7lCA03MVacueUuXdzfw==",
+            "version": "7.26.7",
+            "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.26.7.tgz",
+            "integrity": "sha512-Ycg2tnXwixaXOVb29rana8HNPgLVBof8qqtNQ9LE22IoyZboQbGSxI6ZySMdW3K5nAe6gu35IaJefUJflhUFTQ==",
             "license": "MIT",
             "dependencies": {
-                "@babel/compat-data": "^7.26.0",
-                "@babel/helper-compilation-targets": "^7.25.9",
-                "@babel/helper-plugin-utils": "^7.25.9",
+                "@babel/compat-data": "^7.26.5",
+                "@babel/helper-compilation-targets": "^7.26.5",
+                "@babel/helper-plugin-utils": "^7.26.5",
                 "@babel/helper-validator-option": "^7.25.9",
                 "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.9",
                 "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.9",
@@ -3007,7 +2960,7 @@
                 "@babel/plugin-transform-arrow-functions": "^7.25.9",
                 "@babel/plugin-transform-async-generator-functions": "^7.25.9",
                 "@babel/plugin-transform-async-to-generator": "^7.25.9",
-                "@babel/plugin-transform-block-scoped-functions": "^7.25.9",
+                "@babel/plugin-transform-block-scoped-functions": "^7.26.5",
                 "@babel/plugin-transform-block-scoping": "^7.25.9",
                 "@babel/plugin-transform-class-properties": "^7.25.9",
                 "@babel/plugin-transform-class-static-block": "^7.26.0",
@@ -3018,7 +2971,7 @@
                 "@babel/plugin-transform-duplicate-keys": "^7.25.9",
                 "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.9",
                 "@babel/plugin-transform-dynamic-import": "^7.25.9",
-                "@babel/plugin-transform-exponentiation-operator": "^7.25.9",
+                "@babel/plugin-transform-exponentiation-operator": "^7.26.3",
                 "@babel/plugin-transform-export-namespace-from": "^7.25.9",
                 "@babel/plugin-transform-for-of": "^7.25.9",
                 "@babel/plugin-transform-function-name": "^7.25.9",
@@ -3027,12 +2980,12 @@
                 "@babel/plugin-transform-logical-assignment-operators": "^7.25.9",
                 "@babel/plugin-transform-member-expression-literals": "^7.25.9",
                 "@babel/plugin-transform-modules-amd": "^7.25.9",
-                "@babel/plugin-transform-modules-commonjs": "^7.25.9",
+                "@babel/plugin-transform-modules-commonjs": "^7.26.3",
                 "@babel/plugin-transform-modules-systemjs": "^7.25.9",
                 "@babel/plugin-transform-modules-umd": "^7.25.9",
                 "@babel/plugin-transform-named-capturing-groups-regex": "^7.25.9",
                 "@babel/plugin-transform-new-target": "^7.25.9",
-                "@babel/plugin-transform-nullish-coalescing-operator": "^7.25.9",
+                "@babel/plugin-transform-nullish-coalescing-operator": "^7.26.6",
                 "@babel/plugin-transform-numeric-separator": "^7.25.9",
                 "@babel/plugin-transform-object-rest-spread": "^7.25.9",
                 "@babel/plugin-transform-object-super": "^7.25.9",
@@ -3049,7 +3002,7 @@
                 "@babel/plugin-transform-spread": "^7.25.9",
                 "@babel/plugin-transform-sticky-regex": "^7.25.9",
                 "@babel/plugin-transform-template-literals": "^7.25.9",
-                "@babel/plugin-transform-typeof-symbol": "^7.25.9",
+                "@babel/plugin-transform-typeof-symbol": "^7.26.7",
                 "@babel/plugin-transform-unicode-escapes": "^7.25.9",
                 "@babel/plugin-transform-unicode-property-regex": "^7.25.9",
                 "@babel/plugin-transform-unicode-regex": "^7.25.9",
@@ -3103,9 +3056,9 @@
             }
         },
         "node_modules/@babel/runtime": {
-            "version": "7.26.0",
-            "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz",
-            "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==",
+            "version": "7.26.7",
+            "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.7.tgz",
+            "integrity": "sha512-AOPI3D+a8dXnja+iwsUqGRjr1BbZIe771sXdapOtYI531gSqpi92vXivKcq2asu/DFpdl1ceFAKZyRzK2PCVcQ==",
             "license": "MIT",
             "dependencies": {
                 "regenerator-runtime": "^0.14.0"
@@ -3115,9 +3068,9 @@
             }
         },
         "node_modules/@babel/runtime-corejs3": {
-            "version": "7.26.0",
-            "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.26.0.tgz",
-            "integrity": "sha512-YXHu5lN8kJCb1LOb9PgV6pvak43X2h4HvRApcN5SdWeaItQOzfn1hgP6jasD6KWQyJDBxrVmA9o9OivlnNJK/w==",
+            "version": "7.26.7",
+            "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.26.7.tgz",
+            "integrity": "sha512-55gRV8vGrCIYZnaQHQrD92Lo/hYE3Sj5tmbuf0hhHR7sj2CWhEhHU89hbq+UVDXvFG1zUVXJhUkEq1eAfqXtFw==",
             "license": "MIT",
             "dependencies": {
                 "core-js-pure": "^3.30.2",
@@ -3142,16 +3095,16 @@
             }
         },
         "node_modules/@babel/traverse": {
-            "version": "7.26.4",
-            "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.4.tgz",
-            "integrity": "sha512-fH+b7Y4p3yqvApJALCPJcwb0/XaOSgtK4pzV6WVjPR5GLFQBRI7pfoX2V2iM48NXvX07NUxxm1Vw98YjqTcU5w==",
+            "version": "7.26.7",
+            "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.7.tgz",
+            "integrity": "sha512-1x1sgeyRLC3r5fQOM0/xtQKsYjyxmFjaOrLJNtZ81inNjyJHGIolTULPiSc/2qe1/qfpFLisLQYFnnZl7QoedA==",
             "license": "MIT",
             "dependencies": {
                 "@babel/code-frame": "^7.26.2",
-                "@babel/generator": "^7.26.3",
-                "@babel/parser": "^7.26.3",
+                "@babel/generator": "^7.26.5",
+                "@babel/parser": "^7.26.7",
                 "@babel/template": "^7.25.9",
-                "@babel/types": "^7.26.3",
+                "@babel/types": "^7.26.7",
                 "debug": "^4.3.1",
                 "globals": "^11.1.0"
             },
@@ -3169,9 +3122,9 @@
             }
         },
         "node_modules/@babel/types": {
-            "version": "7.26.3",
-            "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz",
-            "integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==",
+            "version": "7.26.7",
+            "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.7.tgz",
+            "integrity": "sha512-t8kDRGrKXyp6+tjUh7hw2RLyclsW4TRoRvRHtSyAX9Bb5ldlFh+90YAYY6awRXrlB4G5G2izNeGySpATlFzmOg==",
             "license": "MIT",
             "dependencies": {
                 "@babel/helper-string-parser": "^7.25.9",
@@ -3233,9 +3186,9 @@
             "license": "Apache-2.0"
         },
         "node_modules/@chromatic-com/storybook": {
-            "version": "3.2.3",
-            "resolved": "https://registry.npmjs.org/@chromatic-com/storybook/-/storybook-3.2.3.tgz",
-            "integrity": "sha512-3+hfANx79kIjP1qrOSLxpoAXOiYUA0S7A0WI0A24kASrv7USFNNW8etR5TjUilMb0LmqKUn3wDwUK2h6aceQ9g==",
+            "version": "3.2.4",
+            "resolved": "https://registry.npmjs.org/@chromatic-com/storybook/-/storybook-3.2.4.tgz",
+            "integrity": "sha512-5/bOOYxfwZ2BktXeqcCpOVAoR6UCoeART5t9FVy22hoo8F291zOuX4y3SDgm10B1GVU/ZTtJWPT2X9wZFlxYLg==",
             "dev": true,
             "license": "MIT",
             "dependencies": {
@@ -3277,6 +3230,116 @@
                 "@jridgewell/sourcemap-codec": "^1.4.10"
             }
         },
+        "node_modules/@csstools/color-helpers": {
+            "version": "5.0.1",
+            "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.0.1.tgz",
+            "integrity": "sha512-MKtmkA0BX87PKaO1NFRTFH+UnkgnmySQOvNxJubsadusqPEC2aJ9MOQiMceZJJ6oitUl/i0L6u0M1IrmAOmgBA==",
+            "funding": [
+                {
+                    "type": "github",
+                    "url": "https://github.com/sponsors/csstools"
+                },
+                {
+                    "type": "opencollective",
+                    "url": "https://opencollective.com/csstools"
+                }
+            ],
+            "license": "MIT-0",
+            "engines": {
+                "node": ">=18"
+            }
+        },
+        "node_modules/@csstools/css-calc": {
+            "version": "2.1.1",
+            "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.1.tgz",
+            "integrity": "sha512-rL7kaUnTkL9K+Cvo2pnCieqNpTKgQzy5f+N+5Iuko9HAoasP+xgprVh7KN/MaJVvVL1l0EzQq2MoqBHKSrDrag==",
+            "funding": [
+                {
+                    "type": "github",
+                    "url": "https://github.com/sponsors/csstools"
+                },
+                {
+                    "type": "opencollective",
+                    "url": "https://opencollective.com/csstools"
+                }
+            ],
+            "license": "MIT",
+            "engines": {
+                "node": ">=18"
+            },
+            "peerDependencies": {
+                "@csstools/css-parser-algorithms": "^3.0.4",
+                "@csstools/css-tokenizer": "^3.0.3"
+            }
+        },
+        "node_modules/@csstools/css-color-parser": {
+            "version": "3.0.7",
+            "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.0.7.tgz",
+            "integrity": "sha512-nkMp2mTICw32uE5NN+EsJ4f5N+IGFeCFu4bGpiKgb2Pq/7J/MpyLBeQ5ry4KKtRFZaYs6sTmcMYrSRIyj5DFKA==",
+            "funding": [
+                {
+                    "type": "github",
+                    "url": "https://github.com/sponsors/csstools"
+                },
+                {
+                    "type": "opencollective",
+                    "url": "https://opencollective.com/csstools"
+                }
+            ],
+            "license": "MIT",
+            "dependencies": {
+                "@csstools/color-helpers": "^5.0.1",
+                "@csstools/css-calc": "^2.1.1"
+            },
+            "engines": {
+                "node": ">=18"
+            },
+            "peerDependencies": {
+                "@csstools/css-parser-algorithms": "^3.0.4",
+                "@csstools/css-tokenizer": "^3.0.3"
+            }
+        },
+        "node_modules/@csstools/css-parser-algorithms": {
+            "version": "3.0.4",
+            "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.4.tgz",
+            "integrity": "sha512-Up7rBoV77rv29d3uKHUIVubz1BTcgyUK72IvCQAbfbMv584xHcGKCKbWh7i8hPrRJ7qU4Y8IO3IY9m+iTB7P3A==",
+            "funding": [
+                {
+                    "type": "github",
+                    "url": "https://github.com/sponsors/csstools"
+                },
+                {
+                    "type": "opencollective",
+                    "url": "https://opencollective.com/csstools"
+                }
+            ],
+            "license": "MIT",
+            "engines": {
+                "node": ">=18"
+            },
+            "peerDependencies": {
+                "@csstools/css-tokenizer": "^3.0.3"
+            }
+        },
+        "node_modules/@csstools/css-tokenizer": {
+            "version": "3.0.3",
+            "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.3.tgz",
+            "integrity": "sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw==",
+            "funding": [
+                {
+                    "type": "github",
+                    "url": "https://github.com/sponsors/csstools"
+                },
+                {
+                    "type": "opencollective",
+                    "url": "https://opencollective.com/csstools"
+                }
+            ],
+            "license": "MIT",
+            "engines": {
+                "node": ">=18"
+            }
+        },
         "node_modules/@dash/components": {
             "resolved": "packages/components",
             "link": true
@@ -3983,9 +4046,9 @@
             }
         },
         "node_modules/@eslint/core": {
-            "version": "0.9.1",
-            "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.9.1.tgz",
-            "integrity": "sha512-GuUdqkyyzQI5RMIWkHhvTWLCyLo1jNK3vzkSyaExH5kHPDHcuL2VOpHjmMY+y3+NC69qAKToBqldTBgYeLSr9Q==",
+            "version": "0.10.0",
+            "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.10.0.tgz",
+            "integrity": "sha512-gFHJ+xBOo4G3WRlR1e/3G8A6/KZAH6zcE/hkLRCZTi/B9avAG365QhFA8uOGzTMqgTghpn7/fSnscW++dpMSAw==",
             "license": "Apache-2.0",
             "dependencies": {
                 "@types/json-schema": "^7.0.15"
@@ -4092,9 +4155,9 @@
             }
         },
         "node_modules/@eslint/js": {
-            "version": "9.17.0",
-            "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.17.0.tgz",
-            "integrity": "sha512-Sxc4hqcs1kTu0iID3kcZDW3JHq2a77HO9P8CP6YEA/FpH3Ll8UXE2r/86Rz9YJLKme39S9vU5OWNjC6Xl0Cr3w==",
+            "version": "9.19.0",
+            "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.19.0.tgz",
+            "integrity": "sha512-rbq9/g38qjfqFLOVPvwjIvFFdNziEC5S65jmjPw5r6A//QH+W91akh9irMwjDN8zKUTak6W9EsAv4m/7Wnw0UQ==",
             "license": "MIT",
             "engines": {
                 "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -4110,11 +4173,12 @@
             }
         },
         "node_modules/@eslint/plugin-kit": {
-            "version": "0.2.4",
-            "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.4.tgz",
-            "integrity": "sha512-zSkKow6H5Kdm0ZUQUB2kV5JIXqoG0+uH5YADhaEHswm664N9Db8dXSi0nMJpacpMf+MyyglF1vnZohpEg5yUtg==",
+            "version": "0.2.5",
+            "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.5.tgz",
+            "integrity": "sha512-lB05FkqEdUg2AA0xEbUz0SnkXT1LcCTa438W4IWTUh4hdOnVbQyOJ81OrDXsJk/LSiJHubgGEFoR5EHq1NsH1A==",
             "license": "Apache-2.0",
             "dependencies": {
+                "@eslint/core": "^0.10.0",
                 "levn": "^0.4.1"
             },
             "engines": {
@@ -4226,9 +4290,9 @@
             }
         },
         "node_modules/@formatjs/icu-messageformat-parser": {
-            "version": "2.9.8",
-            "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.9.8.tgz",
-            "integrity": "sha512-hZlLNI3+Lev8IAXuwehLoN7QTKqbx3XXwFW1jh0AdIA9XJdzn9Uzr+2LLBspPm/PX0+NLIfykj/8IKxQqHUcUQ==",
+            "version": "2.11.0",
+            "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.11.0.tgz",
+            "integrity": "sha512-Hp81uTjjdTk3FLh/dggU5NK7EIsVWc5/ZDWrIldmf2rBuPejuZ13CZ/wpVE2SToyi4EiroPTQ1XJcJuZFIxTtw==",
             "license": "MIT",
             "dependencies": {
                 "@formatjs/ecma402-abstract": "2.3.2",
@@ -4494,9 +4558,9 @@
             }
         },
         "node_modules/@internationalized/date": {
-            "version": "3.6.0",
-            "resolved": "https://registry.npmjs.org/@internationalized/date/-/date-3.6.0.tgz",
-            "integrity": "sha512-+z6ti+CcJnRlLHok/emGEsWQhe7kfSmEW+/6qCzvKY67YPh7YOBfvc7+/+NXq+zJlbArg30tYpqLjNgcAYv2YQ==",
+            "version": "3.7.0",
+            "resolved": "https://registry.npmjs.org/@internationalized/date/-/date-3.7.0.tgz",
+            "integrity": "sha512-VJ5WS3fcVx0bejE/YHfbDKR/yawZgKqn/if+oEeLqNwBtPzVB06olkfcnojTmEMX+gTpH+FlQ69SHNitJ8/erQ==",
             "license": "Apache-2.0",
             "dependencies": {
                 "@swc/helpers": "^0.5.0"
@@ -5133,122 +5197,6 @@
             "integrity": "sha512-2XghOwu16ZwPJLOFVuIOaLbN0iKMn867evzXFyf0P22dqugezfJwLmdanAgU25ITvz1TvOfVP4jsDImlDJzcWg==",
             "license": "BSD-3-Clause"
         },
-        "node_modules/@mapbox/node-pre-gyp": {
-            "version": "1.0.11",
-            "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz",
-            "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==",
-            "license": "BSD-3-Clause",
-            "dependencies": {
-                "detect-libc": "^2.0.0",
-                "https-proxy-agent": "^5.0.0",
-                "make-dir": "^3.1.0",
-                "node-fetch": "^2.6.7",
-                "nopt": "^5.0.0",
-                "npmlog": "^5.0.1",
-                "rimraf": "^3.0.2",
-                "semver": "^7.3.5",
-                "tar": "^6.1.11"
-            },
-            "bin": {
-                "node-pre-gyp": "bin/node-pre-gyp"
-            }
-        },
-        "node_modules/@mapbox/node-pre-gyp/node_modules/agent-base": {
-            "version": "6.0.2",
-            "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
-            "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
-            "license": "MIT",
-            "dependencies": {
-                "debug": "4"
-            },
-            "engines": {
-                "node": ">= 6.0.0"
-            }
-        },
-        "node_modules/@mapbox/node-pre-gyp/node_modules/brace-expansion": {
-            "version": "1.1.11",
-            "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
-            "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
-            "license": "MIT",
-            "dependencies": {
-                "balanced-match": "^1.0.0",
-                "concat-map": "0.0.1"
-            }
-        },
-        "node_modules/@mapbox/node-pre-gyp/node_modules/glob": {
-            "version": "7.2.3",
-            "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
-            "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
-            "deprecated": "Glob versions prior to v9 are no longer supported",
-            "license": "ISC",
-            "dependencies": {
-                "fs.realpath": "^1.0.0",
-                "inflight": "^1.0.4",
-                "inherits": "2",
-                "minimatch": "^3.1.1",
-                "once": "^1.3.0",
-                "path-is-absolute": "^1.0.0"
-            },
-            "engines": {
-                "node": "*"
-            },
-            "funding": {
-                "url": "https://github.com/sponsors/isaacs"
-            }
-        },
-        "node_modules/@mapbox/node-pre-gyp/node_modules/https-proxy-agent": {
-            "version": "5.0.1",
-            "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
-            "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
-            "license": "MIT",
-            "dependencies": {
-                "agent-base": "6",
-                "debug": "4"
-            },
-            "engines": {
-                "node": ">= 6"
-            }
-        },
-        "node_modules/@mapbox/node-pre-gyp/node_modules/minimatch": {
-            "version": "3.1.2",
-            "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
-            "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
-            "license": "ISC",
-            "dependencies": {
-                "brace-expansion": "^1.1.7"
-            },
-            "engines": {
-                "node": "*"
-            }
-        },
-        "node_modules/@mapbox/node-pre-gyp/node_modules/rimraf": {
-            "version": "3.0.2",
-            "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
-            "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
-            "deprecated": "Rimraf versions prior to v4 are no longer supported",
-            "license": "ISC",
-            "dependencies": {
-                "glob": "^7.1.3"
-            },
-            "bin": {
-                "rimraf": "bin.js"
-            },
-            "funding": {
-                "url": "https://github.com/sponsors/isaacs"
-            }
-        },
-        "node_modules/@mapbox/node-pre-gyp/node_modules/semver": {
-            "version": "7.6.3",
-            "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
-            "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
-            "license": "ISC",
-            "bin": {
-                "semver": "bin/semver.js"
-            },
-            "engines": {
-                "node": ">=10"
-            }
-        },
         "node_modules/@mapbox/point-geometry": {
             "version": "0.1.0",
             "resolved": "https://registry.npmjs.org/@mapbox/point-geometry/-/point-geometry-0.1.0.tgz",
@@ -5350,9 +5298,9 @@
             }
         },
         "node_modules/@mui/core-downloads-tracker": {
-            "version": "6.3.1",
-            "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.3.1.tgz",
-            "integrity": "sha512-2OmnEyoHpj5//dJJpMuxOeLItCCHdf99pjMFfUFdBteCunAK9jW+PwEo4mtdGcLs7P+IgZ+85ypd52eY4AigoQ==",
+            "version": "6.4.1",
+            "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.4.1.tgz",
+            "integrity": "sha512-SfDLWMV5b5oXgDf3NTa2hCTPC1d2defhDH2WgFKmAiejC4mSfXYbyi+AFCLzpizauXhgBm8OaZy9BHKnrSpahQ==",
             "license": "MIT",
             "funding": {
                 "type": "opencollective",
@@ -5360,9 +5308,9 @@
             }
         },
         "node_modules/@mui/icons-material": {
-            "version": "6.3.1",
-            "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-6.3.1.tgz",
-            "integrity": "sha512-nJmWj1PBlwS3t1PnoqcixIsftE+7xrW3Su7f0yrjPw4tVjYrgkhU0hrRp+OlURfZ3ptdSkoBkalee9Bhf1Erfw==",
+            "version": "6.4.1",
+            "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-6.4.1.tgz",
+            "integrity": "sha512-wsxFcUTQxt4s+7Bg4GgobqRjyaHLmZGNOs+HJpbwrwmLbT6mhIJxhpqsKzzWq9aDY8xIe7HCjhpH7XI5UD6teA==",
             "license": "MIT",
             "dependencies": {
                 "@babel/runtime": "^7.26.0"
@@ -5375,7 +5323,7 @@
                 "url": "https://opencollective.com/mui-org"
             },
             "peerDependencies": {
-                "@mui/material": "^6.3.1",
+                "@mui/material": "^6.4.1",
                 "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
                 "react": "^17.0.0 || ^18.0.0 || ^19.0.0"
             },
@@ -5386,16 +5334,16 @@
             }
         },
         "node_modules/@mui/material": {
-            "version": "6.3.1",
-            "resolved": "https://registry.npmjs.org/@mui/material/-/material-6.3.1.tgz",
-            "integrity": "sha512-ynG9ayhxgCsHJ/dtDcT1v78/r2GwQyP3E0hPz3GdPRl0uFJz/uUTtI5KFYwadXmbC+Uv3bfB8laZ6+Cpzh03gA==",
+            "version": "6.4.1",
+            "resolved": "https://registry.npmjs.org/@mui/material/-/material-6.4.1.tgz",
+            "integrity": "sha512-MFBfia6UiKxyoLeGkAh8M15bkeDmfnsUTMRJd/vTQue6YQ8AQ6lw9HqDthyYghzDEWIvZO/lQQzLrZE8XwNJLA==",
             "license": "MIT",
             "dependencies": {
                 "@babel/runtime": "^7.26.0",
-                "@mui/core-downloads-tracker": "^6.3.1",
-                "@mui/system": "^6.3.1",
+                "@mui/core-downloads-tracker": "^6.4.1",
+                "@mui/system": "^6.4.1",
                 "@mui/types": "^7.2.21",
-                "@mui/utils": "^6.3.1",
+                "@mui/utils": "^6.4.1",
                 "@popperjs/core": "^2.11.8",
                 "@types/react-transition-group": "^4.4.12",
                 "clsx": "^2.1.1",
@@ -5414,7 +5362,7 @@
             "peerDependencies": {
                 "@emotion/react": "^11.5.0",
                 "@emotion/styled": "^11.3.0",
-                "@mui/material-pigment-css": "^6.3.1",
+                "@mui/material-pigment-css": "^6.4.1",
                 "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
                 "react": "^17.0.0 || ^18.0.0 || ^19.0.0",
                 "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
@@ -5435,13 +5383,13 @@
             }
         },
         "node_modules/@mui/private-theming": {
-            "version": "6.3.1",
-            "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-6.3.1.tgz",
-            "integrity": "sha512-g0u7hIUkmXmmrmmf5gdDYv9zdAig0KoxhIQn1JN8IVqApzf/AyRhH3uDGx5mSvs8+a1zb4+0W6LC260SyTTtdQ==",
+            "version": "6.4.1",
+            "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-6.4.1.tgz",
+            "integrity": "sha512-DcT7mwK89owwgcEuiE7w458te4CIjHbYWW6Kn6PiR6eLtxBsoBYphA968uqsQAOBQDpbYxvkuFLwhgk4bxoN/Q==",
             "license": "MIT",
             "dependencies": {
                 "@babel/runtime": "^7.26.0",
-                "@mui/utils": "^6.3.1",
+                "@mui/utils": "^6.4.1",
                 "prop-types": "^15.8.1"
             },
             "engines": {
@@ -5462,9 +5410,9 @@
             }
         },
         "node_modules/@mui/styled-engine": {
-            "version": "6.3.1",
-            "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-6.3.1.tgz",
-            "integrity": "sha512-/7CC0d2fIeiUxN5kCCwYu4AWUDd9cCTxWCyo0v/Rnv6s8uk6hWgJC3VLZBoDENBHf/KjqDZuYJ2CR+7hD6QYww==",
+            "version": "6.4.0",
+            "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-6.4.0.tgz",
+            "integrity": "sha512-ek/ZrDujrger12P6o4luQIfRd2IziH7jQod2WMbLqGE03Iy0zUwYmckRTVhRQTLPNccpD8KXGcALJF+uaUQlbg==",
             "license": "MIT",
             "dependencies": {
                 "@babel/runtime": "^7.26.0",
@@ -5496,16 +5444,16 @@
             }
         },
         "node_modules/@mui/system": {
-            "version": "6.3.1",
-            "resolved": "https://registry.npmjs.org/@mui/system/-/system-6.3.1.tgz",
-            "integrity": "sha512-AwqQ3EAIT2np85ki+N15fF0lFXX1iFPqenCzVOSl3QXKy2eifZeGd9dGtt7pGMoFw5dzW4dRGGzRpLAq9rkl7A==",
+            "version": "6.4.1",
+            "resolved": "https://registry.npmjs.org/@mui/system/-/system-6.4.1.tgz",
+            "integrity": "sha512-rgQzgcsHCTtzF9MZ+sL0tOhf2ZBLazpjrujClcb4Siju5lTrK0xX4PsiropActzCemNfM+mOu+0jezAVnfRK8g==",
             "license": "MIT",
             "dependencies": {
                 "@babel/runtime": "^7.26.0",
-                "@mui/private-theming": "^6.3.1",
-                "@mui/styled-engine": "^6.3.1",
+                "@mui/private-theming": "^6.4.1",
+                "@mui/styled-engine": "^6.4.0",
                 "@mui/types": "^7.2.21",
-                "@mui/utils": "^6.3.1",
+                "@mui/utils": "^6.4.1",
                 "clsx": "^2.1.1",
                 "csstype": "^3.1.3",
                 "prop-types": "^15.8.1"
@@ -5550,9 +5498,9 @@
             }
         },
         "node_modules/@mui/utils": {
-            "version": "6.3.1",
-            "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-6.3.1.tgz",
-            "integrity": "sha512-sjGjXAngoio6lniQZKJ5zGfjm+LD2wvLwco7FbKe1fu8A7VIFmz2SwkLb+MDPLNX1lE7IscvNNyh1pobtZg2tw==",
+            "version": "6.4.1",
+            "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-6.4.1.tgz",
+            "integrity": "sha512-iQUDUeYh87SvR4lVojaRaYnQix8BbRV51MxaV6MBmqthecQoxwSbS5e2wnbDJUeFxY2ppV505CiqPLtd0OWkqw==",
             "license": "MIT",
             "dependencies": {
                 "@babel/runtime": "^7.26.0",
@@ -5800,9 +5748,9 @@
             }
         },
         "node_modules/@octokit/auth-token": {
-            "version": "5.1.1",
-            "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-5.1.1.tgz",
-            "integrity": "sha512-rh3G3wDO8J9wSjfI436JUKzHIxq8NaiL0tVeB2aXmG6p/9859aUOAjA9pmSPNGGZxfwmaJ9ozOJImuNVJdpvbA==",
+            "version": "5.1.2",
+            "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-5.1.2.tgz",
+            "integrity": "sha512-JcQDsBdg49Yky2w2ld20IHAlwr8d/d8N6NiOXbtuoPCqzbsiJgF633mVUw3x4mo0H5ypataQIX7SFu3yy44Mpw==",
             "license": "MIT",
             "engines": {
                 "node": ">= 18"
@@ -5854,15 +5802,15 @@
             }
         },
         "node_modules/@octokit/openapi-types": {
-            "version": "22.2.0",
-            "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-22.2.0.tgz",
-            "integrity": "sha512-QBhVjcUa9W7Wwhm6DBFu6ZZ+1/t/oYxqc2tp81Pi41YNuJinbFRx8B133qVOrAaBbF7D/m0Et6f9/pZt9Rc+tg==",
+            "version": "23.0.1",
+            "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-23.0.1.tgz",
+            "integrity": "sha512-izFjMJ1sir0jn0ldEKhZ7xegCTj/ObmEDlEfpFrx4k/JyZSMRHbO3/rBwgE7f3m2DHt+RrNGIVw4wSmwnm3t/g==",
             "license": "MIT"
         },
         "node_modules/@octokit/request": {
-            "version": "9.1.4",
-            "resolved": "https://registry.npmjs.org/@octokit/request/-/request-9.1.4.tgz",
-            "integrity": "sha512-tMbOwGm6wDII6vygP3wUVqFTw3Aoo0FnVQyhihh8vVq12uO3P+vQZeo2CKMpWtPSogpACD0yyZAlVlQnjW71DA==",
+            "version": "9.2.0",
+            "resolved": "https://registry.npmjs.org/@octokit/request/-/request-9.2.0.tgz",
+            "integrity": "sha512-kXLfcxhC4ozCnAXy2ff+cSxpcF0A1UqxjvYMqNuPIeOAzJbVWQ+dy5G2fTylofB/gTbObT8O6JORab+5XtA1Kw==",
             "license": "MIT",
             "dependencies": {
                 "@octokit/endpoint": "^10.0.0",
@@ -5888,18 +5836,18 @@
             }
         },
         "node_modules/@octokit/types": {
-            "version": "13.6.3",
-            "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.6.3.tgz",
-            "integrity": "sha512-oqMRevDW47smhPi4eyJs5prSJ45HUAJIvlnqo7yls7lb8rPLv2fiMKZt2bbUda+2wG+sbzItxgEIH0G4p3Ti4Q==",
+            "version": "13.7.0",
+            "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.7.0.tgz",
+            "integrity": "sha512-BXfRP+3P3IN6fd4uF3SniaHKOO4UXWBfkdR3vA8mIvaoO/wLjGN5qivUtW0QRitBHHMcfC41SLhNVYIZZE+wkA==",
             "license": "MIT",
             "dependencies": {
-                "@octokit/openapi-types": "^22.2.0"
+                "@octokit/openapi-types": "^23.0.1"
             }
         },
         "node_modules/@parcel/watcher": {
-            "version": "2.5.0",
-            "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.0.tgz",
-            "integrity": "sha512-i0GV1yJnm2n3Yq1qw6QrUrd/LI9bE8WEBOTtOkpCXHHdyN3TAGgqAK/DAT05z4fq2x04cARXt2pDmjWjL92iTQ==",
+            "version": "2.5.1",
+            "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz",
+            "integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==",
             "hasInstallScript": true,
             "license": "MIT",
             "optional": true,
@@ -5917,25 +5865,25 @@
                 "url": "https://opencollective.com/parcel"
             },
             "optionalDependencies": {
-                "@parcel/watcher-android-arm64": "2.5.0",
-                "@parcel/watcher-darwin-arm64": "2.5.0",
-                "@parcel/watcher-darwin-x64": "2.5.0",
-                "@parcel/watcher-freebsd-x64": "2.5.0",
-                "@parcel/watcher-linux-arm-glibc": "2.5.0",
-                "@parcel/watcher-linux-arm-musl": "2.5.0",
-                "@parcel/watcher-linux-arm64-glibc": "2.5.0",
-                "@parcel/watcher-linux-arm64-musl": "2.5.0",
-                "@parcel/watcher-linux-x64-glibc": "2.5.0",
-                "@parcel/watcher-linux-x64-musl": "2.5.0",
-                "@parcel/watcher-win32-arm64": "2.5.0",
-                "@parcel/watcher-win32-ia32": "2.5.0",
-                "@parcel/watcher-win32-x64": "2.5.0"
+                "@parcel/watcher-android-arm64": "2.5.1",
+                "@parcel/watcher-darwin-arm64": "2.5.1",
+                "@parcel/watcher-darwin-x64": "2.5.1",
+                "@parcel/watcher-freebsd-x64": "2.5.1",
+                "@parcel/watcher-linux-arm-glibc": "2.5.1",
+                "@parcel/watcher-linux-arm-musl": "2.5.1",
+                "@parcel/watcher-linux-arm64-glibc": "2.5.1",
+                "@parcel/watcher-linux-arm64-musl": "2.5.1",
+                "@parcel/watcher-linux-x64-glibc": "2.5.1",
+                "@parcel/watcher-linux-x64-musl": "2.5.1",
+                "@parcel/watcher-win32-arm64": "2.5.1",
+                "@parcel/watcher-win32-ia32": "2.5.1",
+                "@parcel/watcher-win32-x64": "2.5.1"
             }
         },
         "node_modules/@parcel/watcher-android-arm64": {
-            "version": "2.5.0",
-            "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.0.tgz",
-            "integrity": "sha512-qlX4eS28bUcQCdribHkg/herLe+0A9RyYC+mm2PXpncit8z5b3nSqGVzMNR3CmtAOgRutiZ02eIJJgP/b1iEFQ==",
+            "version": "2.5.1",
+            "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz",
+            "integrity": "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==",
             "cpu": [
                 "arm64"
             ],
@@ -5953,9 +5901,9 @@
             }
         },
         "node_modules/@parcel/watcher-darwin-arm64": {
-            "version": "2.5.0",
-            "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.0.tgz",
-            "integrity": "sha512-hyZ3TANnzGfLpRA2s/4U1kbw2ZI4qGxaRJbBH2DCSREFfubMswheh8TeiC1sGZ3z2jUf3s37P0BBlrD3sjVTUw==",
+            "version": "2.5.1",
+            "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz",
+            "integrity": "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==",
             "cpu": [
                 "arm64"
             ],
@@ -5973,9 +5921,9 @@
             }
         },
         "node_modules/@parcel/watcher-darwin-x64": {
-            "version": "2.5.0",
-            "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.0.tgz",
-            "integrity": "sha512-9rhlwd78saKf18fT869/poydQK8YqlU26TMiNg7AIu7eBp9adqbJZqmdFOsbZ5cnLp5XvRo9wcFmNHgHdWaGYA==",
+            "version": "2.5.1",
+            "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz",
+            "integrity": "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==",
             "cpu": [
                 "x64"
             ],
@@ -5993,9 +5941,9 @@
             }
         },
         "node_modules/@parcel/watcher-freebsd-x64": {
-            "version": "2.5.0",
-            "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.0.tgz",
-            "integrity": "sha512-syvfhZzyM8kErg3VF0xpV8dixJ+RzbUaaGaeb7uDuz0D3FK97/mZ5AJQ3XNnDsXX7KkFNtyQyFrXZzQIcN49Tw==",
+            "version": "2.5.1",
+            "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz",
+            "integrity": "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==",
             "cpu": [
                 "x64"
             ],
@@ -6013,9 +5961,9 @@
             }
         },
         "node_modules/@parcel/watcher-linux-arm-glibc": {
-            "version": "2.5.0",
-            "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.0.tgz",
-            "integrity": "sha512-0VQY1K35DQET3dVYWpOaPFecqOT9dbuCfzjxoQyif1Wc574t3kOSkKevULddcR9znz1TcklCE7Ht6NIxjvTqLA==",
+            "version": "2.5.1",
+            "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz",
+            "integrity": "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==",
             "cpu": [
                 "arm"
             ],
@@ -6033,9 +5981,9 @@
             }
         },
         "node_modules/@parcel/watcher-linux-arm-musl": {
-            "version": "2.5.0",
-            "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.0.tgz",
-            "integrity": "sha512-6uHywSIzz8+vi2lAzFeltnYbdHsDm3iIB57d4g5oaB9vKwjb6N6dRIgZMujw4nm5r6v9/BQH0noq6DzHrqr2pA==",
+            "version": "2.5.1",
+            "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz",
+            "integrity": "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==",
             "cpu": [
                 "arm"
             ],
@@ -6053,9 +6001,9 @@
             }
         },
         "node_modules/@parcel/watcher-linux-arm64-glibc": {
-            "version": "2.5.0",
-            "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.0.tgz",
-            "integrity": "sha512-BfNjXwZKxBy4WibDb/LDCriWSKLz+jJRL3cM/DllnHH5QUyoiUNEp3GmL80ZqxeumoADfCCP19+qiYiC8gUBjA==",
+            "version": "2.5.1",
+            "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz",
+            "integrity": "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==",
             "cpu": [
                 "arm64"
             ],
@@ -6073,9 +6021,9 @@
             }
         },
         "node_modules/@parcel/watcher-linux-arm64-musl": {
-            "version": "2.5.0",
-            "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.0.tgz",
-            "integrity": "sha512-S1qARKOphxfiBEkwLUbHjCY9BWPdWnW9j7f7Hb2jPplu8UZ3nes7zpPOW9bkLbHRvWM0WDTsjdOTUgW0xLBN1Q==",
+            "version": "2.5.1",
+            "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz",
+            "integrity": "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==",
             "cpu": [
                 "arm64"
             ],
@@ -6093,9 +6041,9 @@
             }
         },
         "node_modules/@parcel/watcher-linux-x64-glibc": {
-            "version": "2.5.0",
-            "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.0.tgz",
-            "integrity": "sha512-d9AOkusyXARkFD66S6zlGXyzx5RvY+chTP9Jp0ypSTC9d4lzyRs9ovGf/80VCxjKddcUvnsGwCHWuF2EoPgWjw==",
+            "version": "2.5.1",
+            "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz",
+            "integrity": "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==",
             "cpu": [
                 "x64"
             ],
@@ -6113,9 +6061,9 @@
             }
         },
         "node_modules/@parcel/watcher-linux-x64-musl": {
-            "version": "2.5.0",
-            "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.0.tgz",
-            "integrity": "sha512-iqOC+GoTDoFyk/VYSFHwjHhYrk8bljW6zOhPuhi5t9ulqiYq1togGJB5e3PwYVFFfeVgc6pbz3JdQyDoBszVaA==",
+            "version": "2.5.1",
+            "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz",
+            "integrity": "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==",
             "cpu": [
                 "x64"
             ],
@@ -6133,9 +6081,9 @@
             }
         },
         "node_modules/@parcel/watcher-win32-arm64": {
-            "version": "2.5.0",
-            "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.0.tgz",
-            "integrity": "sha512-twtft1d+JRNkM5YbmexfcH/N4znDtjgysFaV9zvZmmJezQsKpkfLYJ+JFV3uygugK6AtIM2oADPkB2AdhBrNig==",
+            "version": "2.5.1",
+            "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz",
+            "integrity": "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==",
             "cpu": [
                 "arm64"
             ],
@@ -6153,9 +6101,9 @@
             }
         },
         "node_modules/@parcel/watcher-win32-ia32": {
-            "version": "2.5.0",
-            "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.0.tgz",
-            "integrity": "sha512-+rgpsNRKwo8A53elqbbHXdOMtY/tAtTzManTWShB5Kk54N8Q9mzNWV7tV+IbGueCbcj826MfWGU3mprWtuf1TA==",
+            "version": "2.5.1",
+            "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz",
+            "integrity": "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==",
             "cpu": [
                 "ia32"
             ],
@@ -6173,9 +6121,9 @@
             }
         },
         "node_modules/@parcel/watcher-win32-x64": {
-            "version": "2.5.0",
-            "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.0.tgz",
-            "integrity": "sha512-lPrxve92zEHdgeff3aiu4gDOIt4u7sJYha6wbdEZDCDUhtjTsOMiaJzG5lMY4GkWH8p0fMmO2Ppq5G5XXG+DQw==",
+            "version": "2.5.1",
+            "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz",
+            "integrity": "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==",
             "cpu": [
                 "x64"
             ],
@@ -6259,9 +6207,9 @@
             }
         },
         "node_modules/@puppeteer/browsers": {
-            "version": "2.6.1",
-            "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.6.1.tgz",
-            "integrity": "sha512-aBSREisdsGH890S2rQqK82qmQYU3uFpSH8wcZWHgHzl3LfzsxAKbLNiAG9mO8v1Y0UICBeClICxPJvyr0rcuxg==",
+            "version": "2.7.0",
+            "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.7.0.tgz",
+            "integrity": "sha512-bO61XnTuopsz9kvtfqhVbH6LTM1koxK0IlBR+yuVrM2LB7mk8+5o1w18l5zqd5cs8xlf+ntgambqRqGifMDjog==",
             "license": "Apache-2.0",
             "dependencies": {
                 "debug": "^4.4.0",
@@ -6392,18 +6340,43 @@
             }
         },
         "node_modules/@react-aria/actiongroup": {
-            "version": "3.7.11",
-            "resolved": "https://registry.npmjs.org/@react-aria/actiongroup/-/actiongroup-3.7.11.tgz",
-            "integrity": "sha512-fQxd32dN/e4+ctHXoRpqVe99uWzda0XAdKfePbfNO2ghETcF0UrOTugdwYqfEi+5+tgTNzGT7HFc5NeM8Zzd5Q==",
-            "license": "Apache-2.0",
-            "dependencies": {
-                "@react-aria/focus": "^3.19.0",
-                "@react-aria/i18n": "^3.12.4",
-                "@react-aria/interactions": "^3.22.5",
-                "@react-aria/utils": "^3.26.0",
-                "@react-stately/list": "^3.11.1",
-                "@react-types/actiongroup": "^3.4.13",
-                "@react-types/shared": "^3.26.0",
+            "version": "3.7.12",
+            "resolved": "https://registry.npmjs.org/@react-aria/actiongroup/-/actiongroup-3.7.12.tgz",
+            "integrity": "sha512-fBXmM8pDM0iWznB4goN+3T2HDIk7nveen6Y7FLq5Ayh+6nqvFPQtD6sbHKq7geeoECIXwYX6ZG5+thsCS8FO0g==",
+            "license": "Apache-2.0",
+            "dependencies": {
+                "@react-aria/focus": "^3.19.1",
+                "@react-aria/i18n": "^3.12.5",
+                "@react-aria/interactions": "^3.23.0",
+                "@react-aria/utils": "^3.27.0",
+                "@react-stately/list": "^3.11.2",
+                "@react-types/actiongroup": "^3.4.14",
+                "@react-types/shared": "^3.27.0",
+                "@swc/helpers": "^0.5.0"
+            },
+            "peerDependencies": {
+                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+                "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+            }
+        },
+        "node_modules/@react-aria/autocomplete": {
+            "version": "3.0.0-alpha.37",
+            "resolved": "https://registry.npmjs.org/@react-aria/autocomplete/-/autocomplete-3.0.0-alpha.37.tgz",
+            "integrity": "sha512-a7awFG3hshJ/kX7Qti/cJAKOG0XU5F/XW6fQffKGfEge7PmiWIvaLTrT5her79/v8v/bRBykIkpEgDCFE7WGzg==",
+            "license": "Apache-2.0",
+            "dependencies": {
+                "@react-aria/combobox": "^3.11.1",
+                "@react-aria/i18n": "^3.12.5",
+                "@react-aria/interactions": "^3.23.0",
+                "@react-aria/listbox": "^3.14.0",
+                "@react-aria/searchfield": "^3.8.0",
+                "@react-aria/textfield": "^3.16.0",
+                "@react-aria/utils": "^3.27.0",
+                "@react-stately/autocomplete": "3.0.0-alpha.0",
+                "@react-stately/combobox": "^3.10.2",
+                "@react-types/autocomplete": "3.0.0-alpha.28",
+                "@react-types/button": "^3.10.2",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -6412,56 +6385,58 @@
             }
         },
         "node_modules/@react-aria/breadcrumbs": {
-            "version": "3.5.19",
-            "resolved": "https://registry.npmjs.org/@react-aria/breadcrumbs/-/breadcrumbs-3.5.19.tgz",
-            "integrity": "sha512-mVngOPFYVVhec89rf/CiYQGTfaLRfHFtX+JQwY7sNYNqSA+gO8p4lNARe3Be6bJPgH+LUQuruIY9/ZDL6LT3HA==",
+            "version": "3.5.20",
+            "resolved": "https://registry.npmjs.org/@react-aria/breadcrumbs/-/breadcrumbs-3.5.20.tgz",
+            "integrity": "sha512-xqVSSDPpQuUFpJyIXMQv8L7zumk5CeGX7qTzo4XRvqm5T9qnNAX4XpYEMdktnLrQRY/OemCBScbx7SEwr0B3Kg==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-aria/i18n": "^3.12.4",
-                "@react-aria/link": "^3.7.7",
-                "@react-aria/utils": "^3.26.0",
-                "@react-types/breadcrumbs": "^3.7.9",
-                "@react-types/shared": "^3.26.0",
+                "@react-aria/i18n": "^3.12.5",
+                "@react-aria/link": "^3.7.8",
+                "@react-aria/utils": "^3.27.0",
+                "@react-types/breadcrumbs": "^3.7.10",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
-                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+                "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-aria/button": {
-            "version": "3.11.0",
-            "resolved": "https://registry.npmjs.org/@react-aria/button/-/button-3.11.0.tgz",
-            "integrity": "sha512-b37eIV6IW11KmNIAm65F3SEl2/mgj5BrHIysW6smZX3KoKWTGYsYfcQkmtNgY0GOSFfDxMCoolsZ6mxC00nSDA==",
+            "version": "3.11.1",
+            "resolved": "https://registry.npmjs.org/@react-aria/button/-/button-3.11.1.tgz",
+            "integrity": "sha512-NSs2HxHSSPSuYy5bN+PMJzsCNDVsbm1fZ/nrWM2WWWHTBrx9OqyrEXZVV9ebzQCN9q0nzhwpf6D42zHIivWtJA==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-aria/focus": "^3.19.0",
-                "@react-aria/interactions": "^3.22.5",
-                "@react-aria/toolbar": "3.0.0-beta.11",
-                "@react-aria/utils": "^3.26.0",
-                "@react-stately/toggle": "^3.8.0",
-                "@react-types/button": "^3.10.1",
-                "@react-types/shared": "^3.26.0",
+                "@react-aria/focus": "^3.19.1",
+                "@react-aria/interactions": "^3.23.0",
+                "@react-aria/toolbar": "3.0.0-beta.12",
+                "@react-aria/utils": "^3.27.0",
+                "@react-stately/toggle": "^3.8.1",
+                "@react-types/button": "^3.10.2",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
-                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+                "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-aria/calendar": {
-            "version": "3.6.0",
-            "resolved": "https://registry.npmjs.org/@react-aria/calendar/-/calendar-3.6.0.tgz",
-            "integrity": "sha512-tZ3nd5DP8uxckbj83Pt+4RqgcTWDlGi7njzc7QqFOG2ApfnYDUXbIpb/Q4KY6JNlJskG8q33wo0XfOwNy8J+eg==",
+            "version": "3.7.0",
+            "resolved": "https://registry.npmjs.org/@react-aria/calendar/-/calendar-3.7.0.tgz",
+            "integrity": "sha512-9YUbgcox7cQgvZfQtL2BLLRsIuX4mJeclk9HkFoOsAu3RGO5HNsteah8FV54W8BMjm/bNRXIPUxtjTTP+1L6jg==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@internationalized/date": "^3.6.0",
-                "@react-aria/i18n": "^3.12.4",
-                "@react-aria/interactions": "^3.22.5",
+                "@internationalized/date": "^3.7.0",
+                "@react-aria/i18n": "^3.12.5",
+                "@react-aria/interactions": "^3.23.0",
                 "@react-aria/live-announcer": "^3.4.1",
-                "@react-aria/utils": "^3.26.0",
-                "@react-stately/calendar": "^3.6.0",
-                "@react-types/button": "^3.10.1",
-                "@react-types/calendar": "^3.5.0",
-                "@react-types/shared": "^3.26.0",
+                "@react-aria/utils": "^3.27.0",
+                "@react-stately/calendar": "^3.7.0",
+                "@react-types/button": "^3.10.2",
+                "@react-types/calendar": "^3.6.0",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -6470,36 +6445,37 @@
             }
         },
         "node_modules/@react-aria/checkbox": {
-            "version": "3.15.0",
-            "resolved": "https://registry.npmjs.org/@react-aria/checkbox/-/checkbox-3.15.0.tgz",
-            "integrity": "sha512-z/8xd4em7o0MroBXwkkwv7QRwiJaA1FwqMhRUb7iqtBGP2oSytBEDf0N7L09oci32a1P4ZPz2rMK5GlLh/PD6g==",
-            "license": "Apache-2.0",
-            "dependencies": {
-                "@react-aria/form": "^3.0.11",
-                "@react-aria/interactions": "^3.22.5",
-                "@react-aria/label": "^3.7.13",
-                "@react-aria/toggle": "^3.10.10",
-                "@react-aria/utils": "^3.26.0",
-                "@react-stately/checkbox": "^3.6.10",
-                "@react-stately/form": "^3.1.0",
-                "@react-stately/toggle": "^3.8.0",
-                "@react-types/checkbox": "^3.9.0",
-                "@react-types/shared": "^3.26.0",
+            "version": "3.15.1",
+            "resolved": "https://registry.npmjs.org/@react-aria/checkbox/-/checkbox-3.15.1.tgz",
+            "integrity": "sha512-ETgsMDZ0IZzRXy/OVlGkazm8T+PcMHoTvsxp0c+U82c8iqdITA+VJ615eBPOQh6OkkYIIn4cRn/e+69RmGzXng==",
+            "license": "Apache-2.0",
+            "dependencies": {
+                "@react-aria/form": "^3.0.12",
+                "@react-aria/interactions": "^3.23.0",
+                "@react-aria/label": "^3.7.14",
+                "@react-aria/toggle": "^3.10.11",
+                "@react-aria/utils": "^3.27.0",
+                "@react-stately/checkbox": "^3.6.11",
+                "@react-stately/form": "^3.1.1",
+                "@react-stately/toggle": "^3.8.1",
+                "@react-types/checkbox": "^3.9.1",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
-                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+                "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-aria/collections": {
-            "version": "3.0.0-alpha.6",
-            "resolved": "https://registry.npmjs.org/@react-aria/collections/-/collections-3.0.0-alpha.6.tgz",
-            "integrity": "sha512-A+7Eap/zvsghMb5/C3EAPn41axSzRhtX2glQRXSBj1mK31CTPCZ9BhrMIMC5DL7ZnfA7C+Ysilo9nI2YQh5PMg==",
+            "version": "3.0.0-alpha.7",
+            "resolved": "https://registry.npmjs.org/@react-aria/collections/-/collections-3.0.0-alpha.7.tgz",
+            "integrity": "sha512-JR2Ro33Chlf26NM12zJsK+MOs5/k+PQallT5+4YawndYmbxqlDLADcoFdcORJqh0pKf9OnluWtANobCkQGd0aQ==",
             "license": "Apache-2.0",
             "dependencies": {
                 "@react-aria/ssr": "^3.9.7",
-                "@react-aria/utils": "^3.26.0",
-                "@react-types/shared": "^3.26.0",
+                "@react-aria/utils": "^3.27.0",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0",
                 "use-sync-external-store": "^1.2.0"
             },
@@ -6509,23 +6485,23 @@
             }
         },
         "node_modules/@react-aria/color": {
-            "version": "3.0.2",
-            "resolved": "https://registry.npmjs.org/@react-aria/color/-/color-3.0.2.tgz",
-            "integrity": "sha512-dSM5qQRcR1gRGYCBw0IGRmc29gjfoht3cQleKb8MMNcgHYa2oi5VdCs2yKXmYFwwVC6uPtnlNy9S6e0spqdr+w==",
-            "license": "Apache-2.0",
-            "dependencies": {
-                "@react-aria/i18n": "^3.12.4",
-                "@react-aria/interactions": "^3.22.5",
-                "@react-aria/numberfield": "^3.11.9",
-                "@react-aria/slider": "^3.7.14",
-                "@react-aria/spinbutton": "^3.6.10",
-                "@react-aria/textfield": "^3.15.0",
-                "@react-aria/utils": "^3.26.0",
-                "@react-aria/visually-hidden": "^3.8.18",
-                "@react-stately/color": "^3.8.1",
-                "@react-stately/form": "^3.1.0",
-                "@react-types/color": "^3.0.1",
-                "@react-types/shared": "^3.26.0",
+            "version": "3.0.3",
+            "resolved": "https://registry.npmjs.org/@react-aria/color/-/color-3.0.3.tgz",
+            "integrity": "sha512-DDVma2107VHBfSuEnnmy+KJvXvxEXWSAooii2vlHHmQNb5x4rv4YTk+dP5GZl/7MgT8OgPTB9UHoC83bXFMDRA==",
+            "license": "Apache-2.0",
+            "dependencies": {
+                "@react-aria/i18n": "^3.12.5",
+                "@react-aria/interactions": "^3.23.0",
+                "@react-aria/numberfield": "^3.11.10",
+                "@react-aria/slider": "^3.7.15",
+                "@react-aria/spinbutton": "^3.6.11",
+                "@react-aria/textfield": "^3.16.0",
+                "@react-aria/utils": "^3.27.0",
+                "@react-aria/visually-hidden": "^3.8.19",
+                "@react-stately/color": "^3.8.2",
+                "@react-stately/form": "^3.1.1",
+                "@react-types/color": "^3.0.2",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -6534,25 +6510,25 @@
             }
         },
         "node_modules/@react-aria/combobox": {
-            "version": "3.11.0",
-            "resolved": "https://registry.npmjs.org/@react-aria/combobox/-/combobox-3.11.0.tgz",
-            "integrity": "sha512-s88YMmPkMO1WSoiH1KIyZDLJqUwvM2wHXXakj3cYw1tBHGo4rOUFq+JWQIbM5EDO4HOR4AUUqzIUd0NO7t3zyg==",
+            "version": "3.11.1",
+            "resolved": "https://registry.npmjs.org/@react-aria/combobox/-/combobox-3.11.1.tgz",
+            "integrity": "sha512-TTNbGhUuqxzPcJzd6hufOxuHzX0UARkw+0bl+TuCwNPQnqrcPf20EoOZvd3MHZwGq6GCP4QV+qo0uGx83RpUvA==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-aria/i18n": "^3.12.4",
-                "@react-aria/listbox": "^3.13.6",
+                "@react-aria/i18n": "^3.12.5",
+                "@react-aria/listbox": "^3.14.0",
                 "@react-aria/live-announcer": "^3.4.1",
-                "@react-aria/menu": "^3.16.0",
-                "@react-aria/overlays": "^3.24.0",
-                "@react-aria/selection": "^3.21.0",
-                "@react-aria/textfield": "^3.15.0",
-                "@react-aria/utils": "^3.26.0",
-                "@react-stately/collections": "^3.12.0",
-                "@react-stately/combobox": "^3.10.1",
-                "@react-stately/form": "^3.1.0",
-                "@react-types/button": "^3.10.1",
-                "@react-types/combobox": "^3.13.1",
-                "@react-types/shared": "^3.26.0",
+                "@react-aria/menu": "^3.17.0",
+                "@react-aria/overlays": "^3.25.0",
+                "@react-aria/selection": "^3.22.0",
+                "@react-aria/textfield": "^3.16.0",
+                "@react-aria/utils": "^3.27.0",
+                "@react-stately/collections": "^3.12.1",
+                "@react-stately/combobox": "^3.10.2",
+                "@react-stately/form": "^3.1.1",
+                "@react-types/button": "^3.10.2",
+                "@react-types/combobox": "^3.13.2",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -6561,28 +6537,28 @@
             }
         },
         "node_modules/@react-aria/datepicker": {
-            "version": "3.12.0",
-            "resolved": "https://registry.npmjs.org/@react-aria/datepicker/-/datepicker-3.12.0.tgz",
-            "integrity": "sha512-VYNXioLfddIHpwQx211+rTYuunDmI7VHWBRetCpH3loIsVFuhFSRchTQpclAzxolO3g0vO7pMVj9VYt7Swp6kg==",
+            "version": "3.13.0",
+            "resolved": "https://registry.npmjs.org/@react-aria/datepicker/-/datepicker-3.13.0.tgz",
+            "integrity": "sha512-TmJan65P3Vk7VDBNW5rH9Z25cAn0vk8TEtaP3boCs8wJFE+HbEuB8EqLxBFu47khtuKTEqDP3dTlUh2Vt/f7Xw==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@internationalized/date": "^3.6.0",
+                "@internationalized/date": "^3.7.0",
                 "@internationalized/number": "^3.6.0",
                 "@internationalized/string": "^3.2.5",
-                "@react-aria/focus": "^3.19.0",
-                "@react-aria/form": "^3.0.11",
-                "@react-aria/i18n": "^3.12.4",
-                "@react-aria/interactions": "^3.22.5",
-                "@react-aria/label": "^3.7.13",
-                "@react-aria/spinbutton": "^3.6.10",
-                "@react-aria/utils": "^3.26.0",
-                "@react-stately/datepicker": "^3.11.0",
-                "@react-stately/form": "^3.1.0",
-                "@react-types/button": "^3.10.1",
-                "@react-types/calendar": "^3.5.0",
-                "@react-types/datepicker": "^3.9.0",
-                "@react-types/dialog": "^3.5.14",
-                "@react-types/shared": "^3.26.0",
+                "@react-aria/focus": "^3.19.1",
+                "@react-aria/form": "^3.0.12",
+                "@react-aria/i18n": "^3.12.5",
+                "@react-aria/interactions": "^3.23.0",
+                "@react-aria/label": "^3.7.14",
+                "@react-aria/spinbutton": "^3.6.11",
+                "@react-aria/utils": "^3.27.0",
+                "@react-stately/datepicker": "^3.12.0",
+                "@react-stately/form": "^3.1.1",
+                "@react-types/button": "^3.10.2",
+                "@react-types/calendar": "^3.6.0",
+                "@react-types/datepicker": "^3.10.0",
+                "@react-types/dialog": "^3.5.15",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -6591,16 +6567,16 @@
             }
         },
         "node_modules/@react-aria/dialog": {
-            "version": "3.5.20",
-            "resolved": "https://registry.npmjs.org/@react-aria/dialog/-/dialog-3.5.20.tgz",
-            "integrity": "sha512-l0GZVLgeOd3kL3Yj8xQW7wN3gn9WW3RLd/SGI9t7ciTq+I/FhftjXCWzXLlOCCTLMf+gv7eazecECtmoWUaZWQ==",
+            "version": "3.5.21",
+            "resolved": "https://registry.npmjs.org/@react-aria/dialog/-/dialog-3.5.21.tgz",
+            "integrity": "sha512-tBsn9swBhcptJ9QIm0+ur0PVR799N6qmGguva3rUdd+gfitknFScyT08d7AoMr9AbXYdJ+2R9XNSZ3H3uIWQMw==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-aria/focus": "^3.19.0",
-                "@react-aria/overlays": "^3.24.0",
-                "@react-aria/utils": "^3.26.0",
-                "@react-types/dialog": "^3.5.14",
-                "@react-types/shared": "^3.26.0",
+                "@react-aria/focus": "^3.19.1",
+                "@react-aria/overlays": "^3.25.0",
+                "@react-aria/utils": "^3.27.0",
+                "@react-types/dialog": "^3.5.15",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -6609,15 +6585,15 @@
             }
         },
         "node_modules/@react-aria/disclosure": {
-            "version": "3.0.0",
-            "resolved": "https://registry.npmjs.org/@react-aria/disclosure/-/disclosure-3.0.0.tgz",
-            "integrity": "sha512-xO9QTQSvymujTjCs1iCQ4+dKZvtF/rVVaFZBKlUtqIqwTHMdqeZu4fh5miLEnTyVLNHMGzLrFggsd8Q+niC9Og==",
+            "version": "3.0.1",
+            "resolved": "https://registry.npmjs.org/@react-aria/disclosure/-/disclosure-3.0.1.tgz",
+            "integrity": "sha512-rNH8RFcePoAQizcqB7KuHbBOr7sPsysFKCUwbVSOXLPgvCfXKafIhjgFJVqekfsbn5zWvkcTupnzGVJj/F9p+g==",
             "license": "Apache-2.0",
             "dependencies": {
                 "@react-aria/ssr": "^3.9.7",
-                "@react-aria/utils": "^3.26.0",
-                "@react-stately/disclosure": "^3.0.0",
-                "@react-types/button": "^3.10.1",
+                "@react-aria/utils": "^3.27.0",
+                "@react-stately/disclosure": "^3.0.1",
+                "@react-types/button": "^3.10.2",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -6626,20 +6602,20 @@
             }
         },
         "node_modules/@react-aria/dnd": {
-            "version": "3.8.0",
-            "resolved": "https://registry.npmjs.org/@react-aria/dnd/-/dnd-3.8.0.tgz",
-            "integrity": "sha512-JiqHY3E9fDU5Kb4gN22cuK6QNlpMCGe6ngR/BV+Q8mLEsdoWcoUAYOtYXVNNTRvCdVbEWI87FUU+ThyPpoDhNQ==",
+            "version": "3.8.1",
+            "resolved": "https://registry.npmjs.org/@react-aria/dnd/-/dnd-3.8.1.tgz",
+            "integrity": "sha512-FoXYQ4z33E9YBzIGRJM1B1oZep6CvEWgXvjCZGURatjr3qG7vf95mOqA5kVd9bjLL7QK4w0ujJWEBfog3WmufA==",
             "license": "Apache-2.0",
             "dependencies": {
                 "@internationalized/string": "^3.2.5",
-                "@react-aria/i18n": "^3.12.4",
-                "@react-aria/interactions": "^3.22.5",
+                "@react-aria/i18n": "^3.12.5",
+                "@react-aria/interactions": "^3.23.0",
                 "@react-aria/live-announcer": "^3.4.1",
-                "@react-aria/overlays": "^3.24.0",
-                "@react-aria/utils": "^3.26.0",
-                "@react-stately/dnd": "^3.5.0",
-                "@react-types/button": "^3.10.1",
-                "@react-types/shared": "^3.26.0",
+                "@react-aria/overlays": "^3.25.0",
+                "@react-aria/utils": "^3.27.0",
+                "@react-stately/dnd": "^3.5.1",
+                "@react-types/button": "^3.10.2",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -6648,55 +6624,57 @@
             }
         },
         "node_modules/@react-aria/focus": {
-            "version": "3.19.0",
-            "resolved": "https://registry.npmjs.org/@react-aria/focus/-/focus-3.19.0.tgz",
-            "integrity": "sha512-hPF9EXoUQeQl1Y21/rbV2H4FdUR2v+4/I0/vB+8U3bT1CJ+1AFj1hc/rqx2DqEwDlEwOHN+E4+mRahQmlybq0A==",
+            "version": "3.19.1",
+            "resolved": "https://registry.npmjs.org/@react-aria/focus/-/focus-3.19.1.tgz",
+            "integrity": "sha512-bix9Bu1Ue7RPcYmjwcjhB14BMu2qzfJ3tMQLqDc9pweJA66nOw8DThy3IfVr8Z7j2PHktOLf9kcbiZpydKHqzg==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-aria/interactions": "^3.22.5",
-                "@react-aria/utils": "^3.26.0",
-                "@react-types/shared": "^3.26.0",
+                "@react-aria/interactions": "^3.23.0",
+                "@react-aria/utils": "^3.27.0",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0",
                 "clsx": "^2.0.0"
             },
             "peerDependencies": {
-                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+                "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-aria/form": {
-            "version": "3.0.11",
-            "resolved": "https://registry.npmjs.org/@react-aria/form/-/form-3.0.11.tgz",
-            "integrity": "sha512-oXzjTiwVuuWjZ8muU0hp3BrDH5qjVctLOF50mjPvqUbvXQTHhoDxWweyIXPQjGshaqBd2w4pWaE4A2rG2O/apw==",
+            "version": "3.0.12",
+            "resolved": "https://registry.npmjs.org/@react-aria/form/-/form-3.0.12.tgz",
+            "integrity": "sha512-8uvPYEd3GDyGt5NRJIzdWW1Ry5HLZq37vzRZKUW8alZ2upFMH3KJJG55L9GP59KiF6zBrYBebvI/YK1Ye1PE1g==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-aria/interactions": "^3.22.5",
-                "@react-aria/utils": "^3.26.0",
-                "@react-stately/form": "^3.1.0",
-                "@react-types/shared": "^3.26.0",
+                "@react-aria/interactions": "^3.23.0",
+                "@react-aria/utils": "^3.27.0",
+                "@react-stately/form": "^3.1.1",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
-                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+                "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-aria/grid": {
-            "version": "3.11.0",
-            "resolved": "https://registry.npmjs.org/@react-aria/grid/-/grid-3.11.0.tgz",
-            "integrity": "sha512-lN5FpQgu2Rq0CzTPWmzRpq6QHcMmzsXYeClsgO3108uVp1/genBNAObYVTxGOKe/jb9q99trz8EtIn05O6KN1g==",
+            "version": "3.11.1",
+            "resolved": "https://registry.npmjs.org/@react-aria/grid/-/grid-3.11.1.tgz",
+            "integrity": "sha512-Wg8m68RtNWfkhP3Qjrrsl1q1et8QCjXPMRsYgKBahYRS0kq2MDcQ+UBdG1fiCQn/MfNImhTUGVeQX276dy1lww==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-aria/focus": "^3.19.0",
-                "@react-aria/i18n": "^3.12.4",
-                "@react-aria/interactions": "^3.22.5",
+                "@react-aria/focus": "^3.19.1",
+                "@react-aria/i18n": "^3.12.5",
+                "@react-aria/interactions": "^3.23.0",
                 "@react-aria/live-announcer": "^3.4.1",
-                "@react-aria/selection": "^3.21.0",
-                "@react-aria/utils": "^3.26.0",
-                "@react-stately/collections": "^3.12.0",
-                "@react-stately/grid": "^3.10.0",
-                "@react-stately/selection": "^3.18.0",
-                "@react-types/checkbox": "^3.9.0",
-                "@react-types/grid": "^3.2.10",
-                "@react-types/shared": "^3.26.0",
+                "@react-aria/selection": "^3.22.0",
+                "@react-aria/utils": "^3.27.0",
+                "@react-stately/collections": "^3.12.1",
+                "@react-stately/grid": "^3.10.1",
+                "@react-stately/selection": "^3.19.0",
+                "@react-types/checkbox": "^3.9.1",
+                "@react-types/grid": "^3.2.11",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -6705,21 +6683,21 @@
             }
         },
         "node_modules/@react-aria/gridlist": {
-            "version": "3.10.0",
-            "resolved": "https://registry.npmjs.org/@react-aria/gridlist/-/gridlist-3.10.0.tgz",
-            "integrity": "sha512-UcblfSZ7kJBrjg9mQ5VbnRevN81UiYB4NuL5PwIpBpridO7tnl4ew6+96PYU7Wj1chHhPS3x0b0zmuSVN7A0LA==",
-            "license": "Apache-2.0",
-            "dependencies": {
-                "@react-aria/focus": "^3.19.0",
-                "@react-aria/grid": "^3.11.0",
-                "@react-aria/i18n": "^3.12.4",
-                "@react-aria/interactions": "^3.22.5",
-                "@react-aria/selection": "^3.21.0",
-                "@react-aria/utils": "^3.26.0",
-                "@react-stately/collections": "^3.12.0",
-                "@react-stately/list": "^3.11.1",
-                "@react-stately/tree": "^3.8.6",
-                "@react-types/shared": "^3.26.0",
+            "version": "3.10.1",
+            "resolved": "https://registry.npmjs.org/@react-aria/gridlist/-/gridlist-3.10.1.tgz",
+            "integrity": "sha512-11FlupBg5C9ehs7R6OjqMPWEOLK/4IuSrq7D1xU+Hnm7ZYI/KKcCXvNMjMmnOz/gGzOmfgVwz5PIKaY9aZarEg==",
+            "license": "Apache-2.0",
+            "dependencies": {
+                "@react-aria/focus": "^3.19.1",
+                "@react-aria/grid": "^3.11.1",
+                "@react-aria/i18n": "^3.12.5",
+                "@react-aria/interactions": "^3.23.0",
+                "@react-aria/selection": "^3.22.0",
+                "@react-aria/utils": "^3.27.0",
+                "@react-stately/collections": "^3.12.1",
+                "@react-stately/list": "^3.11.2",
+                "@react-stately/tree": "^3.8.7",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -6728,84 +6706,88 @@
             }
         },
         "node_modules/@react-aria/i18n": {
-            "version": "3.12.4",
-            "resolved": "https://registry.npmjs.org/@react-aria/i18n/-/i18n-3.12.4.tgz",
-            "integrity": "sha512-j9+UL3q0Ls8MhXV9gtnKlyozq4aM95YywXqnmJtzT1rYeBx7w28hooqrWkCYLfqr4OIryv1KUnPiCSLwC2OC7w==",
+            "version": "3.12.5",
+            "resolved": "https://registry.npmjs.org/@react-aria/i18n/-/i18n-3.12.5.tgz",
+            "integrity": "sha512-ooeop2pTG94PuaHoN2OTk2hpkqVuoqgEYxRvnc1t7DVAtsskfhS/gVOTqyWGsxvwAvRi7m/CnDu6FYdeQ/bK5w==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@internationalized/date": "^3.6.0",
+                "@internationalized/date": "^3.7.0",
                 "@internationalized/message": "^3.1.6",
                 "@internationalized/number": "^3.6.0",
                 "@internationalized/string": "^3.2.5",
                 "@react-aria/ssr": "^3.9.7",
-                "@react-aria/utils": "^3.26.0",
-                "@react-types/shared": "^3.26.0",
+                "@react-aria/utils": "^3.27.0",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
-                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+                "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-aria/interactions": {
-            "version": "3.22.5",
-            "resolved": "https://registry.npmjs.org/@react-aria/interactions/-/interactions-3.22.5.tgz",
-            "integrity": "sha512-kMwiAD9E0TQp+XNnOs13yVJghiy8ET8L0cbkeuTgNI96sOAp/63EJ1FSrDf17iD8sdjt41LafwX/dKXW9nCcLQ==",
+            "version": "3.23.0",
+            "resolved": "https://registry.npmjs.org/@react-aria/interactions/-/interactions-3.23.0.tgz",
+            "integrity": "sha512-0qR1atBIWrb7FzQ+Tmr3s8uH5mQdyRH78n0krYaG8tng9+u1JlSi8DGRSaC9ezKyNB84m7vHT207xnHXGeJ3Fg==",
             "license": "Apache-2.0",
             "dependencies": {
                 "@react-aria/ssr": "^3.9.7",
-                "@react-aria/utils": "^3.26.0",
-                "@react-types/shared": "^3.26.0",
+                "@react-aria/utils": "^3.27.0",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
-                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+                "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-aria/label": {
-            "version": "3.7.13",
-            "resolved": "https://registry.npmjs.org/@react-aria/label/-/label-3.7.13.tgz",
-            "integrity": "sha512-brSAXZVTey5RG/Ex6mTrV/9IhGSQFU4Al34qmjEDho+Z2qT4oPwf8k7TRXWWqzOU0ugYxekYbsLd2zlN3XvWcg==",
+            "version": "3.7.14",
+            "resolved": "https://registry.npmjs.org/@react-aria/label/-/label-3.7.14.tgz",
+            "integrity": "sha512-EN1Md2YvcC4sMqBoggsGYUEGlTNqUfJZWzduSt29fbQp1rKU2KlybTe+TWxKq/r2fFd+4JsRXxMeJiwB3w2AQA==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-aria/utils": "^3.26.0",
-                "@react-types/shared": "^3.26.0",
+                "@react-aria/utils": "^3.27.0",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
-                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+                "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-aria/link": {
-            "version": "3.7.7",
-            "resolved": "https://registry.npmjs.org/@react-aria/link/-/link-3.7.7.tgz",
-            "integrity": "sha512-eVBRcHKhNSsATYWv5wRnZXRqPVcKAWWakyvfrYePIKpC3s4BaHZyTGYdefk8ZwZdEOuQZBqLMnjW80q1uhtkuA==",
+            "version": "3.7.8",
+            "resolved": "https://registry.npmjs.org/@react-aria/link/-/link-3.7.8.tgz",
+            "integrity": "sha512-oiXUPQLZmf9Q9Xehb/sG1QRxfo28NFKdh9w+unD12sHI6NdLMETl5MA4CYyTgI0dfMtTjtfrF68GCnWfc7JvXQ==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-aria/focus": "^3.19.0",
-                "@react-aria/interactions": "^3.22.5",
-                "@react-aria/utils": "^3.26.0",
-                "@react-types/link": "^3.5.9",
-                "@react-types/shared": "^3.26.0",
+                "@react-aria/focus": "^3.19.1",
+                "@react-aria/interactions": "^3.23.0",
+                "@react-aria/utils": "^3.27.0",
+                "@react-types/link": "^3.5.10",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
-                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+                "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-aria/listbox": {
-            "version": "3.13.6",
-            "resolved": "https://registry.npmjs.org/@react-aria/listbox/-/listbox-3.13.6.tgz",
-            "integrity": "sha512-6hEXEXIZVau9lgBZ4VVjFR3JnGU+fJaPmV3HP0UZ2ucUptfG0MZo24cn+ZQJsWiuaCfNFv5b8qribiv+BcO+Kg==",
-            "license": "Apache-2.0",
-            "dependencies": {
-                "@react-aria/interactions": "^3.22.5",
-                "@react-aria/label": "^3.7.13",
-                "@react-aria/selection": "^3.21.0",
-                "@react-aria/utils": "^3.26.0",
-                "@react-stately/collections": "^3.12.0",
-                "@react-stately/list": "^3.11.1",
-                "@react-types/listbox": "^3.5.3",
-                "@react-types/shared": "^3.26.0",
+            "version": "3.14.0",
+            "resolved": "https://registry.npmjs.org/@react-aria/listbox/-/listbox-3.14.0.tgz",
+            "integrity": "sha512-pyVbKavh8N8iyiwOx6I3JIcICvAzFXkKSFni1yarfgngJsJV3KSyOkzLomOfN9UhbjcV4sX61/fccwJuvlurlA==",
+            "license": "Apache-2.0",
+            "dependencies": {
+                "@react-aria/interactions": "^3.23.0",
+                "@react-aria/label": "^3.7.14",
+                "@react-aria/selection": "^3.22.0",
+                "@react-aria/utils": "^3.27.0",
+                "@react-stately/collections": "^3.12.1",
+                "@react-stately/list": "^3.11.2",
+                "@react-types/listbox": "^3.5.4",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -6823,24 +6805,24 @@
             }
         },
         "node_modules/@react-aria/menu": {
-            "version": "3.16.0",
-            "resolved": "https://registry.npmjs.org/@react-aria/menu/-/menu-3.16.0.tgz",
-            "integrity": "sha512-TNk+Vd3TbpBPUxEloAdHRTaRxf9JBK7YmkHYiq0Yj5Lc22KS0E2eTyhpPM9xJvEWN2TlC5TEvNfdyui2kYWFFQ==",
-            "license": "Apache-2.0",
-            "dependencies": {
-                "@react-aria/focus": "^3.19.0",
-                "@react-aria/i18n": "^3.12.4",
-                "@react-aria/interactions": "^3.22.5",
-                "@react-aria/overlays": "^3.24.0",
-                "@react-aria/selection": "^3.21.0",
-                "@react-aria/utils": "^3.26.0",
-                "@react-stately/collections": "^3.12.0",
-                "@react-stately/menu": "^3.9.0",
-                "@react-stately/selection": "^3.18.0",
-                "@react-stately/tree": "^3.8.6",
-                "@react-types/button": "^3.10.1",
-                "@react-types/menu": "^3.9.13",
-                "@react-types/shared": "^3.26.0",
+            "version": "3.17.0",
+            "resolved": "https://registry.npmjs.org/@react-aria/menu/-/menu-3.17.0.tgz",
+            "integrity": "sha512-aiFvSv3G1YvPC0klJQ/9quB05xIDZzJ5Lt6/CykP0UwGK5i8GCqm6/cyFLwEXsS5ooUPxS3bqmdOsgdADSSgqg==",
+            "license": "Apache-2.0",
+            "dependencies": {
+                "@react-aria/focus": "^3.19.1",
+                "@react-aria/i18n": "^3.12.5",
+                "@react-aria/interactions": "^3.23.0",
+                "@react-aria/overlays": "^3.25.0",
+                "@react-aria/selection": "^3.22.0",
+                "@react-aria/utils": "^3.27.0",
+                "@react-stately/collections": "^3.12.1",
+                "@react-stately/menu": "^3.9.1",
+                "@react-stately/selection": "^3.19.0",
+                "@react-stately/tree": "^3.8.7",
+                "@react-types/button": "^3.10.2",
+                "@react-types/menu": "^3.9.14",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -6849,14 +6831,14 @@
             }
         },
         "node_modules/@react-aria/meter": {
-            "version": "3.4.18",
-            "resolved": "https://registry.npmjs.org/@react-aria/meter/-/meter-3.4.18.tgz",
-            "integrity": "sha512-tTX3LLlmDIHqrC42dkdf+upb1c4UbhlpZ52gqB64lZD4OD4HE+vMTwNSe+7MRKMLvcdKPWCRC35PnxIHZ15kfQ==",
+            "version": "3.4.19",
+            "resolved": "https://registry.npmjs.org/@react-aria/meter/-/meter-3.4.19.tgz",
+            "integrity": "sha512-IIA+gTHrNVbMuBgcqdGLEKd/ZiKM2hOUqS6uztbT15dwPJTmtfJiTWA2872PiY52p+gqPSanZuTc2TXYJa+rew==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-aria/progress": "^3.4.18",
-                "@react-types/meter": "^3.4.5",
-                "@react-types/shared": "^3.26.0",
+                "@react-aria/progress": "^3.4.19",
+                "@react-types/meter": "^3.4.6",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -6864,21 +6846,21 @@
             }
         },
         "node_modules/@react-aria/numberfield": {
-            "version": "3.11.9",
-            "resolved": "https://registry.npmjs.org/@react-aria/numberfield/-/numberfield-3.11.9.tgz",
-            "integrity": "sha512-3tiGPx2y4zyOV7PmdBASes99ZZsFTZAJTnU45Z+p1CW4131lw7y2ZhbojBl7U6DaXAJvi1z6zY6cq2UE9w5a0Q==",
-            "license": "Apache-2.0",
-            "dependencies": {
-                "@react-aria/i18n": "^3.12.4",
-                "@react-aria/interactions": "^3.22.5",
-                "@react-aria/spinbutton": "^3.6.10",
-                "@react-aria/textfield": "^3.15.0",
-                "@react-aria/utils": "^3.26.0",
-                "@react-stately/form": "^3.1.0",
-                "@react-stately/numberfield": "^3.9.8",
-                "@react-types/button": "^3.10.1",
-                "@react-types/numberfield": "^3.8.7",
-                "@react-types/shared": "^3.26.0",
+            "version": "3.11.10",
+            "resolved": "https://registry.npmjs.org/@react-aria/numberfield/-/numberfield-3.11.10.tgz",
+            "integrity": "sha512-bYbTfO9NbAKMFOfEGGs+lvlxk0I9L0lU3WD2PFQZWdaoBz9TCkL+vK0fJk1zsuKaVjeGsmHP9VesBPRmaP0MiA==",
+            "license": "Apache-2.0",
+            "dependencies": {
+                "@react-aria/i18n": "^3.12.5",
+                "@react-aria/interactions": "^3.23.0",
+                "@react-aria/spinbutton": "^3.6.11",
+                "@react-aria/textfield": "^3.16.0",
+                "@react-aria/utils": "^3.27.0",
+                "@react-stately/form": "^3.1.1",
+                "@react-stately/numberfield": "^3.9.9",
+                "@react-types/button": "^3.10.2",
+                "@react-types/numberfield": "^3.8.8",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -6887,21 +6869,21 @@
             }
         },
         "node_modules/@react-aria/overlays": {
-            "version": "3.24.0",
-            "resolved": "https://registry.npmjs.org/@react-aria/overlays/-/overlays-3.24.0.tgz",
-            "integrity": "sha512-0kAXBsMNTc/a3M07tK9Cdt/ea8CxTAEJ223g8YgqImlmoBBYAL7dl5G01IOj67TM64uWPTmZrOklBchHWgEm3A==",
+            "version": "3.25.0",
+            "resolved": "https://registry.npmjs.org/@react-aria/overlays/-/overlays-3.25.0.tgz",
+            "integrity": "sha512-UEqJJ4duowrD1JvwXpPZreBuK79pbyNjNxFUVpFSskpGEJe3oCWwsSDKz7P1O7xbx5OYp+rDiY8fk/sE5rkaKw==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-aria/focus": "^3.19.0",
-                "@react-aria/i18n": "^3.12.4",
-                "@react-aria/interactions": "^3.22.5",
+                "@react-aria/focus": "^3.19.1",
+                "@react-aria/i18n": "^3.12.5",
+                "@react-aria/interactions": "^3.23.0",
                 "@react-aria/ssr": "^3.9.7",
-                "@react-aria/utils": "^3.26.0",
-                "@react-aria/visually-hidden": "^3.8.18",
-                "@react-stately/overlays": "^3.6.12",
-                "@react-types/button": "^3.10.1",
-                "@react-types/overlays": "^3.8.11",
-                "@react-types/shared": "^3.26.0",
+                "@react-aria/utils": "^3.27.0",
+                "@react-aria/visually-hidden": "^3.8.19",
+                "@react-stately/overlays": "^3.6.13",
+                "@react-types/button": "^3.10.2",
+                "@react-types/overlays": "^3.8.12",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -6910,81 +6892,84 @@
             }
         },
         "node_modules/@react-aria/progress": {
-            "version": "3.4.18",
-            "resolved": "https://registry.npmjs.org/@react-aria/progress/-/progress-3.4.18.tgz",
-            "integrity": "sha512-FOLgJ9t9i1u3oAAimybJG6r7/soNPBnJfWo4Yr6MmaUv90qVGa1h6kiuM5m9H/bm5JobAebhdfHit9lFlgsCmg==",
+            "version": "3.4.19",
+            "resolved": "https://registry.npmjs.org/@react-aria/progress/-/progress-3.4.19.tgz",
+            "integrity": "sha512-5HHnBJHqEUuY+dYsjIZDYsENeKr49VCuxeaDZ0OSahbOlloIOB1baCo/6jLBv1O1rwrAzZ2gCCPcVGed/cjrcw==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-aria/i18n": "^3.12.4",
-                "@react-aria/label": "^3.7.13",
-                "@react-aria/utils": "^3.26.0",
-                "@react-types/progress": "^3.5.8",
-                "@react-types/shared": "^3.26.0",
+                "@react-aria/i18n": "^3.12.5",
+                "@react-aria/label": "^3.7.14",
+                "@react-aria/utils": "^3.27.0",
+                "@react-types/progress": "^3.5.9",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
-                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+                "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-aria/radio": {
-            "version": "3.10.10",
-            "resolved": "https://registry.npmjs.org/@react-aria/radio/-/radio-3.10.10.tgz",
-            "integrity": "sha512-NVdeOVrsrHgSfwL2jWCCXFsWZb+RMRZErj5vthHQW4nkHECGOzeX56VaLWTSvdoCPqi9wdIX8A6K9peeAIgxzA==",
-            "license": "Apache-2.0",
-            "dependencies": {
-                "@react-aria/focus": "^3.19.0",
-                "@react-aria/form": "^3.0.11",
-                "@react-aria/i18n": "^3.12.4",
-                "@react-aria/interactions": "^3.22.5",
-                "@react-aria/label": "^3.7.13",
-                "@react-aria/utils": "^3.26.0",
-                "@react-stately/radio": "^3.10.9",
-                "@react-types/radio": "^3.8.5",
-                "@react-types/shared": "^3.26.0",
+            "version": "3.10.11",
+            "resolved": "https://registry.npmjs.org/@react-aria/radio/-/radio-3.10.11.tgz",
+            "integrity": "sha512-R150HsBFPr1jLMShI4aBM8heCa1k6h0KEvnFRfTAOBu+B9hMSZOPB+d6GQOwGPysNlbset90Kej8G15FGHjqiA==",
+            "license": "Apache-2.0",
+            "dependencies": {
+                "@react-aria/focus": "^3.19.1",
+                "@react-aria/form": "^3.0.12",
+                "@react-aria/i18n": "^3.12.5",
+                "@react-aria/interactions": "^3.23.0",
+                "@react-aria/label": "^3.7.14",
+                "@react-aria/utils": "^3.27.0",
+                "@react-stately/radio": "^3.10.10",
+                "@react-types/radio": "^3.8.6",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
-                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+                "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-aria/searchfield": {
-            "version": "3.7.11",
-            "resolved": "https://registry.npmjs.org/@react-aria/searchfield/-/searchfield-3.7.11.tgz",
-            "integrity": "sha512-wFf6QxtBFfoxy0ANxI0+ftFEBGynVCY0+ce4H4Y9LpUTQsIKMp3sdc7LoUFORWw5Yee6Eid5cFPQX0Ymnk+ZJg==",
+            "version": "3.8.0",
+            "resolved": "https://registry.npmjs.org/@react-aria/searchfield/-/searchfield-3.8.0.tgz",
+            "integrity": "sha512-AaZuH9YIWlMyE1m7cSjHCfOuQmlWN+w8HVW32TxeGGGL1kJsYAlSYWYHUyYFIKh245kq/m5zUxAxmw5Ygmnx5w==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-aria/i18n": "^3.12.4",
-                "@react-aria/textfield": "^3.15.0",
-                "@react-aria/utils": "^3.26.0",
-                "@react-stately/searchfield": "^3.5.8",
-                "@react-types/button": "^3.10.1",
-                "@react-types/searchfield": "^3.5.10",
-                "@react-types/shared": "^3.26.0",
+                "@react-aria/i18n": "^3.12.5",
+                "@react-aria/textfield": "^3.16.0",
+                "@react-aria/utils": "^3.27.0",
+                "@react-stately/searchfield": "^3.5.9",
+                "@react-types/button": "^3.10.2",
+                "@react-types/searchfield": "^3.5.11",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
-                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+                "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-aria/select": {
-            "version": "3.15.0",
-            "resolved": "https://registry.npmjs.org/@react-aria/select/-/select-3.15.0.tgz",
-            "integrity": "sha512-zgBOUNy81aJplfc3NKDJMv8HkXjBGzaFF3XDzNfW8vJ7nD9rcTRUN5SQ1XCEnKMv12B/Euk9zt6kd+tX0wk1vQ==",
-            "license": "Apache-2.0",
-            "dependencies": {
-                "@react-aria/form": "^3.0.11",
-                "@react-aria/i18n": "^3.12.4",
-                "@react-aria/interactions": "^3.22.5",
-                "@react-aria/label": "^3.7.13",
-                "@react-aria/listbox": "^3.13.6",
-                "@react-aria/menu": "^3.16.0",
-                "@react-aria/selection": "^3.21.0",
-                "@react-aria/utils": "^3.26.0",
-                "@react-aria/visually-hidden": "^3.8.18",
-                "@react-stately/select": "^3.6.9",
-                "@react-types/button": "^3.10.1",
-                "@react-types/select": "^3.9.8",
-                "@react-types/shared": "^3.26.0",
+            "version": "3.15.1",
+            "resolved": "https://registry.npmjs.org/@react-aria/select/-/select-3.15.1.tgz",
+            "integrity": "sha512-FOtY1tuHt0YTHwOEy/sf7LEIL+Nnkho3wJmfpWQuTxsvMCF7UJdQPYPd6/jGCcCdiqW7H4iqyjUkSp6nk/XRWQ==",
+            "license": "Apache-2.0",
+            "dependencies": {
+                "@react-aria/form": "^3.0.12",
+                "@react-aria/i18n": "^3.12.5",
+                "@react-aria/interactions": "^3.23.0",
+                "@react-aria/label": "^3.7.14",
+                "@react-aria/listbox": "^3.14.0",
+                "@react-aria/menu": "^3.17.0",
+                "@react-aria/selection": "^3.22.0",
+                "@react-aria/utils": "^3.27.0",
+                "@react-aria/visually-hidden": "^3.8.19",
+                "@react-stately/select": "^3.6.10",
+                "@react-types/button": "^3.10.2",
+                "@react-types/select": "^3.9.9",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -6993,17 +6978,17 @@
             }
         },
         "node_modules/@react-aria/selection": {
-            "version": "3.21.0",
-            "resolved": "https://registry.npmjs.org/@react-aria/selection/-/selection-3.21.0.tgz",
-            "integrity": "sha512-52JJ6hlPcM+gt0VV3DBmz6Kj1YAJr13TfutrKfGWcK36LvNCBm1j0N+TDqbdnlp8Nue6w0+5FIwZq44XPYiBGg==",
+            "version": "3.22.0",
+            "resolved": "https://registry.npmjs.org/@react-aria/selection/-/selection-3.22.0.tgz",
+            "integrity": "sha512-XFOrK525HX2eeWeLZcZscUAs5qsuC1ZxsInDXMjvLeAaUPtQNEhUKHj3psDAl6XDU4VV1IJo0qCmFTVqTTMZSg==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-aria/focus": "^3.19.0",
-                "@react-aria/i18n": "^3.12.4",
-                "@react-aria/interactions": "^3.22.5",
-                "@react-aria/utils": "^3.26.0",
-                "@react-stately/selection": "^3.18.0",
-                "@react-types/shared": "^3.26.0",
+                "@react-aria/focus": "^3.19.1",
+                "@react-aria/i18n": "^3.12.5",
+                "@react-aria/interactions": "^3.23.0",
+                "@react-aria/utils": "^3.27.0",
+                "@react-stately/selection": "^3.19.0",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -7012,50 +6997,52 @@
             }
         },
         "node_modules/@react-aria/separator": {
-            "version": "3.4.4",
-            "resolved": "https://registry.npmjs.org/@react-aria/separator/-/separator-3.4.4.tgz",
-            "integrity": "sha512-dH+qt0Mdh0nhKXCHW6AR4DF8DKLUBP26QYWaoThPdBwIpypH/JVKowpPtWms1P4b36U6XzHXHnTTEn/ZVoCqNA==",
+            "version": "3.4.5",
+            "resolved": "https://registry.npmjs.org/@react-aria/separator/-/separator-3.4.5.tgz",
+            "integrity": "sha512-RQA9sKZdAEjP1Yrv0GpDdXgmXd56kXDE8atPDHEC0/A4lpYh/YFLfXcv1JW0Hlg4kBocdX2pB2INyDGhiD+yfw==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-aria/utils": "^3.26.0",
-                "@react-types/shared": "^3.26.0",
+                "@react-aria/utils": "^3.27.0",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
-                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+                "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-aria/slider": {
-            "version": "3.7.14",
-            "resolved": "https://registry.npmjs.org/@react-aria/slider/-/slider-3.7.14.tgz",
-            "integrity": "sha512-7rOiKjLkEZ0j7mPMlwrqivc+K4OSfL14slaQp06GHRiJkhiWXh2/drPe15hgNq55HmBQBpA0umKMkJcqVgmXPA==",
-            "license": "Apache-2.0",
-            "dependencies": {
-                "@react-aria/focus": "^3.19.0",
-                "@react-aria/i18n": "^3.12.4",
-                "@react-aria/interactions": "^3.22.5",
-                "@react-aria/label": "^3.7.13",
-                "@react-aria/utils": "^3.26.0",
-                "@react-stately/slider": "^3.6.0",
-                "@react-types/shared": "^3.26.0",
-                "@react-types/slider": "^3.7.7",
+            "version": "3.7.15",
+            "resolved": "https://registry.npmjs.org/@react-aria/slider/-/slider-3.7.15.tgz",
+            "integrity": "sha512-v9tujsuvJYRX0vE/vMYBzTT9FXbzrLsjkOrouNq+UdBIr7wRjIWTHHM0j+khb2swyCWNTbdv6Ce316Zqx2qWFg==",
+            "license": "Apache-2.0",
+            "dependencies": {
+                "@react-aria/focus": "^3.19.1",
+                "@react-aria/i18n": "^3.12.5",
+                "@react-aria/interactions": "^3.23.0",
+                "@react-aria/label": "^3.7.14",
+                "@react-aria/utils": "^3.27.0",
+                "@react-stately/slider": "^3.6.1",
+                "@react-types/shared": "^3.27.0",
+                "@react-types/slider": "^3.7.8",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
-                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+                "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-aria/spinbutton": {
-            "version": "3.6.10",
-            "resolved": "https://registry.npmjs.org/@react-aria/spinbutton/-/spinbutton-3.6.10.tgz",
-            "integrity": "sha512-nhYEYk7xUNOZDaqiQ5w/nHH9ouqjJbabTWXH+KK7UR1oVGfo4z1wG94l8KWF3Z6SGGnBxzLJyTBguZ4g9aYTSg==",
+            "version": "3.6.11",
+            "resolved": "https://registry.npmjs.org/@react-aria/spinbutton/-/spinbutton-3.6.11.tgz",
+            "integrity": "sha512-RM+gYS9tf9Wb+GegV18n4ArK3NBKgcsak7Nx1CkEgX9BjJ0yayWUHdfEjRRvxGXl+1z1n84cJVkZ6FUlWOWEZA==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-aria/i18n": "^3.12.4",
+                "@react-aria/i18n": "^3.12.5",
                 "@react-aria/live-announcer": "^3.4.1",
-                "@react-aria/utils": "^3.26.0",
-                "@react-types/button": "^3.10.1",
-                "@react-types/shared": "^3.26.0",
+                "@react-aria/utils": "^3.27.0",
+                "@react-types/button": "^3.10.2",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -7079,15 +7066,15 @@
             }
         },
         "node_modules/@react-aria/switch": {
-            "version": "3.6.10",
-            "resolved": "https://registry.npmjs.org/@react-aria/switch/-/switch-3.6.10.tgz",
-            "integrity": "sha512-FtaI9WaEP1tAmra1sYlAkYXg9x75P5UtgY8pSbe9+1WRyWbuE1QZT+RNCTi3IU4fZ7iJQmXH6+VaMyzPlSUagw==",
+            "version": "3.6.11",
+            "resolved": "https://registry.npmjs.org/@react-aria/switch/-/switch-3.6.11.tgz",
+            "integrity": "sha512-paYCpH+oeL+8rgQK+cBJ+IaZ1sXSh3+50WPlg2LvLBta0QVfQhPR4juPvfXRpfHHhCjFBgF4/RGbV8q5zpl3vA==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-aria/toggle": "^3.10.10",
-                "@react-stately/toggle": "^3.8.0",
-                "@react-types/shared": "^3.26.0",
-                "@react-types/switch": "^3.5.7",
+                "@react-aria/toggle": "^3.10.11",
+                "@react-stately/toggle": "^3.8.1",
+                "@react-types/shared": "^3.27.0",
+                "@react-types/switch": "^3.5.8",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -7095,25 +7082,25 @@
             }
         },
         "node_modules/@react-aria/table": {
-            "version": "3.16.0",
-            "resolved": "https://registry.npmjs.org/@react-aria/table/-/table-3.16.0.tgz",
-            "integrity": "sha512-9xF9S3CJ7XRiiK92hsIKxPedD0kgcQWwqTMtj3IBynpQ4vsnRiW3YNIzrn9C3apjknRZDTSta8O2QPYCUMmw2A==",
+            "version": "3.16.1",
+            "resolved": "https://registry.npmjs.org/@react-aria/table/-/table-3.16.1.tgz",
+            "integrity": "sha512-T28TIGnKnPBunyErDBmm5jUX7AyzT7NVWBo9pDSt9wUuEnz0rVNd7p9sjmP2+u7I645feGG9klcdpCvFeqrk8A==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-aria/focus": "^3.19.0",
-                "@react-aria/grid": "^3.11.0",
-                "@react-aria/i18n": "^3.12.4",
-                "@react-aria/interactions": "^3.22.5",
+                "@react-aria/focus": "^3.19.1",
+                "@react-aria/grid": "^3.11.1",
+                "@react-aria/i18n": "^3.12.5",
+                "@react-aria/interactions": "^3.23.0",
                 "@react-aria/live-announcer": "^3.4.1",
-                "@react-aria/utils": "^3.26.0",
-                "@react-aria/visually-hidden": "^3.8.18",
-                "@react-stately/collections": "^3.12.0",
+                "@react-aria/utils": "^3.27.0",
+                "@react-aria/visually-hidden": "^3.8.19",
+                "@react-stately/collections": "^3.12.1",
                 "@react-stately/flags": "^3.0.5",
-                "@react-stately/table": "^3.13.0",
-                "@react-types/checkbox": "^3.9.0",
-                "@react-types/grid": "^3.2.10",
-                "@react-types/shared": "^3.26.0",
-                "@react-types/table": "^3.10.3",
+                "@react-stately/table": "^3.13.1",
+                "@react-types/checkbox": "^3.9.1",
+                "@react-types/grid": "^3.2.11",
+                "@react-types/shared": "^3.27.0",
+                "@react-types/table": "^3.10.4",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -7122,18 +7109,18 @@
             }
         },
         "node_modules/@react-aria/tabs": {
-            "version": "3.9.8",
-            "resolved": "https://registry.npmjs.org/@react-aria/tabs/-/tabs-3.9.8.tgz",
-            "integrity": "sha512-Nur/qRFBe+Zrt4xcCJV/ULXCS3Mlae+B89bp1Gl20vSDqk6uaPtGk+cS5k03eugOvas7AQapqNJsJgKd66TChw==",
-            "license": "Apache-2.0",
-            "dependencies": {
-                "@react-aria/focus": "^3.19.0",
-                "@react-aria/i18n": "^3.12.4",
-                "@react-aria/selection": "^3.21.0",
-                "@react-aria/utils": "^3.26.0",
-                "@react-stately/tabs": "^3.7.0",
-                "@react-types/shared": "^3.26.0",
-                "@react-types/tabs": "^3.3.11",
+            "version": "3.9.9",
+            "resolved": "https://registry.npmjs.org/@react-aria/tabs/-/tabs-3.9.9.tgz",
+            "integrity": "sha512-oXPtANs16xu6MdMGLHjGV/2Zupvyp9CJEt7ORPLv5xAzSY5hSjuQHJLZ0te3Lh/KSG5/0o3RW/W5yEqo7pBQQQ==",
+            "license": "Apache-2.0",
+            "dependencies": {
+                "@react-aria/focus": "^3.19.1",
+                "@react-aria/i18n": "^3.12.5",
+                "@react-aria/selection": "^3.22.0",
+                "@react-aria/utils": "^3.27.0",
+                "@react-stately/tabs": "^3.7.1",
+                "@react-types/shared": "^3.27.0",
+                "@react-types/tabs": "^3.3.12",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -7142,20 +7129,20 @@
             }
         },
         "node_modules/@react-aria/tag": {
-            "version": "3.4.8",
-            "resolved": "https://registry.npmjs.org/@react-aria/tag/-/tag-3.4.8.tgz",
-            "integrity": "sha512-exWl52bsFtJuzaqMYvSnLteUoPqb3Wf+uICru/yRtREJsWVqjJF38NCVlU73Yqd9qMPTctDrboSZFAWAWKDxoA==",
-            "license": "Apache-2.0",
-            "dependencies": {
-                "@react-aria/gridlist": "^3.10.0",
-                "@react-aria/i18n": "^3.12.4",
-                "@react-aria/interactions": "^3.22.5",
-                "@react-aria/label": "^3.7.13",
-                "@react-aria/selection": "^3.21.0",
-                "@react-aria/utils": "^3.26.0",
-                "@react-stately/list": "^3.11.1",
-                "@react-types/button": "^3.10.1",
-                "@react-types/shared": "^3.26.0",
+            "version": "3.4.9",
+            "resolved": "https://registry.npmjs.org/@react-aria/tag/-/tag-3.4.9.tgz",
+            "integrity": "sha512-Vnps+zk8vYyjevv2Bc6vc9kSp9HFLKrKUDmrWMc0DfseypwJMc3Ya6F965ZVTjF9nuWrojNmvgusNu7qyXFShQ==",
+            "license": "Apache-2.0",
+            "dependencies": {
+                "@react-aria/gridlist": "^3.10.1",
+                "@react-aria/i18n": "^3.12.5",
+                "@react-aria/interactions": "^3.23.0",
+                "@react-aria/label": "^3.7.14",
+                "@react-aria/selection": "^3.22.0",
+                "@react-aria/utils": "^3.27.0",
+                "@react-stately/list": "^3.11.2",
+                "@react-types/button": "^3.10.2",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -7164,90 +7151,94 @@
             }
         },
         "node_modules/@react-aria/textfield": {
-            "version": "3.15.0",
-            "resolved": "https://registry.npmjs.org/@react-aria/textfield/-/textfield-3.15.0.tgz",
-            "integrity": "sha512-V5mg7y1OR6WXYHdhhm4FC7QyGc9TideVRDFij1SdOJrIo5IFB7lvwpOS0GmgwkVbtr71PTRMjZnNbrJUFU6VNA==",
+            "version": "3.16.0",
+            "resolved": "https://registry.npmjs.org/@react-aria/textfield/-/textfield-3.16.0.tgz",
+            "integrity": "sha512-53RVpMeMDN/QoabqnYZ1lxTh1xTQ3IBYQARuayq5EGGMafyxoFHzttxUdSqkZGK/+zdSF2GfmjOYJVm2nDKuDQ==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-aria/focus": "^3.19.0",
-                "@react-aria/form": "^3.0.11",
-                "@react-aria/label": "^3.7.13",
-                "@react-aria/utils": "^3.26.0",
-                "@react-stately/form": "^3.1.0",
+                "@react-aria/focus": "^3.19.1",
+                "@react-aria/form": "^3.0.12",
+                "@react-aria/label": "^3.7.14",
+                "@react-aria/utils": "^3.27.0",
+                "@react-stately/form": "^3.1.1",
                 "@react-stately/utils": "^3.10.5",
-                "@react-types/shared": "^3.26.0",
-                "@react-types/textfield": "^3.10.0",
+                "@react-types/shared": "^3.27.0",
+                "@react-types/textfield": "^3.11.0",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
-                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+                "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-aria/toggle": {
-            "version": "3.10.10",
-            "resolved": "https://registry.npmjs.org/@react-aria/toggle/-/toggle-3.10.10.tgz",
-            "integrity": "sha512-QwMT/vTNrbrILxWVHfd9zVQ3mV2NdBwyRu+DphVQiFAXcmc808LEaIX2n0lI6FCsUDC9ZejCyvzd91/YemdZ1Q==",
+            "version": "3.10.11",
+            "resolved": "https://registry.npmjs.org/@react-aria/toggle/-/toggle-3.10.11.tgz",
+            "integrity": "sha512-J3jO3KJiUbaYVDEpeXSBwqcyKxpi9OreiHRGiaxb6VwB+FWCj7Gb2WKajByXNyfs8jc6kX9VUFaXa7jze60oEQ==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-aria/focus": "^3.19.0",
-                "@react-aria/interactions": "^3.22.5",
-                "@react-aria/utils": "^3.26.0",
-                "@react-stately/toggle": "^3.8.0",
-                "@react-types/checkbox": "^3.9.0",
-                "@react-types/shared": "^3.26.0",
+                "@react-aria/focus": "^3.19.1",
+                "@react-aria/interactions": "^3.23.0",
+                "@react-aria/utils": "^3.27.0",
+                "@react-stately/toggle": "^3.8.1",
+                "@react-types/checkbox": "^3.9.1",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
-                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+                "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-aria/toolbar": {
-            "version": "3.0.0-beta.11",
-            "resolved": "https://registry.npmjs.org/@react-aria/toolbar/-/toolbar-3.0.0-beta.11.tgz",
-            "integrity": "sha512-LM3jTRFNDgoEpoL568WaiuqiVM7eynSQLJis1hV0vlVnhTd7M7kzt7zoOjzxVb5Uapz02uCp1Fsm4wQMz09qwQ==",
+            "version": "3.0.0-beta.12",
+            "resolved": "https://registry.npmjs.org/@react-aria/toolbar/-/toolbar-3.0.0-beta.12.tgz",
+            "integrity": "sha512-a+Be27BtM2lzEdTzm19FikPbitfW65g/JZln3kyAvgpswhU6Ljl8lztaVw4ixjG4H0nqnKvVggMy4AlWwDUaVQ==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-aria/focus": "^3.19.0",
-                "@react-aria/i18n": "^3.12.4",
-                "@react-aria/utils": "^3.26.0",
-                "@react-types/shared": "^3.26.0",
+                "@react-aria/focus": "^3.19.1",
+                "@react-aria/i18n": "^3.12.5",
+                "@react-aria/utils": "^3.27.0",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
-                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+                "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-aria/tooltip": {
-            "version": "3.7.10",
-            "resolved": "https://registry.npmjs.org/@react-aria/tooltip/-/tooltip-3.7.10.tgz",
-            "integrity": "sha512-Udi3XOnrF/SYIz72jw9bgB74MG/yCOzF5pozHj2FH2HiJlchYv/b6rHByV/77IZemdlkmL/uugrv/7raPLSlnw==",
+            "version": "3.7.11",
+            "resolved": "https://registry.npmjs.org/@react-aria/tooltip/-/tooltip-3.7.11.tgz",
+            "integrity": "sha512-mhZgAWUj7bUWipDeJXaVPZdqnzoBCd/uaEbdafnvgETmov1udVqPTh9w4ZKX2Oh1wa2+OdLFrBOk+8vC6QbWag==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-aria/focus": "^3.19.0",
-                "@react-aria/interactions": "^3.22.5",
-                "@react-aria/utils": "^3.26.0",
-                "@react-stately/tooltip": "^3.5.0",
-                "@react-types/shared": "^3.26.0",
-                "@react-types/tooltip": "^3.4.13",
+                "@react-aria/focus": "^3.19.1",
+                "@react-aria/interactions": "^3.23.0",
+                "@react-aria/utils": "^3.27.0",
+                "@react-stately/tooltip": "^3.5.1",
+                "@react-types/shared": "^3.27.0",
+                "@react-types/tooltip": "^3.4.14",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
-                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+                "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-aria/tree": {
-            "version": "3.0.0-beta.2",
-            "resolved": "https://registry.npmjs.org/@react-aria/tree/-/tree-3.0.0-beta.2.tgz",
-            "integrity": "sha512-lH3hVl2VgG3YLN+ee1zQzm+2F+BGLd/HBhfMYPuI3IjHvDb+m+jCJXHdBOGrfG2Qydk2LYheqX8QXCluulu0qQ==",
-            "license": "Apache-2.0",
-            "dependencies": {
-                "@react-aria/gridlist": "^3.10.0",
-                "@react-aria/i18n": "^3.12.4",
-                "@react-aria/selection": "^3.21.0",
-                "@react-aria/utils": "^3.26.0",
-                "@react-stately/tree": "^3.8.6",
-                "@react-types/button": "^3.10.1",
-                "@react-types/shared": "^3.26.0",
+            "version": "3.0.0-beta.3",
+            "resolved": "https://registry.npmjs.org/@react-aria/tree/-/tree-3.0.0-beta.3.tgz",
+            "integrity": "sha512-eQnCtvDgpunCHInIT+Da3qdgzDzKEFW9REX2j1vMqWTsbM1YikVlBzB9AJOd9KIAWyn+p4TYdL8zzPWxvuSdfA==",
+            "license": "Apache-2.0",
+            "dependencies": {
+                "@react-aria/gridlist": "^3.10.1",
+                "@react-aria/i18n": "^3.12.5",
+                "@react-aria/selection": "^3.22.0",
+                "@react-aria/utils": "^3.27.0",
+                "@react-stately/tree": "^3.8.7",
+                "@react-types/button": "^3.10.2",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -7256,32 +7247,33 @@
             }
         },
         "node_modules/@react-aria/utils": {
-            "version": "3.26.0",
-            "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.26.0.tgz",
-            "integrity": "sha512-LkZouGSjjQ0rEqo4XJosS4L3YC/zzQkfRM3KoqK6fUOmUJ9t0jQ09WjiF+uOoG9u+p30AVg3TrZRUWmoTS+koQ==",
+            "version": "3.27.0",
+            "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.27.0.tgz",
+            "integrity": "sha512-p681OtApnKOdbeN8ITfnnYqfdHS0z7GE+4l8EXlfLnr70Rp/9xicBO6d2rU+V/B3JujDw2gPWxYKEnEeh0CGCw==",
             "license": "Apache-2.0",
             "dependencies": {
                 "@react-aria/ssr": "^3.9.7",
                 "@react-stately/utils": "^3.10.5",
-                "@react-types/shared": "^3.26.0",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0",
                 "clsx": "^2.0.0"
             },
             "peerDependencies": {
-                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+                "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-aria/virtualizer": {
-            "version": "4.1.0",
-            "resolved": "https://registry.npmjs.org/@react-aria/virtualizer/-/virtualizer-4.1.0.tgz",
-            "integrity": "sha512-ziSq3Y7iuaAMJWGZU1RRs/TykuPapQfx8dyH2eyKPLgEjBUoXRGWE7n6jklBwal14b0lPK0wkCzRoQbkUvX3cg==",
+            "version": "4.1.1",
+            "resolved": "https://registry.npmjs.org/@react-aria/virtualizer/-/virtualizer-4.1.1.tgz",
+            "integrity": "sha512-AYQmC/S9HhxGOj8HkQdxDW8/+sUEmmfcGpjkInzXB8UZCB1FQLC0LpvA8fOP7AfzLaAL+HVcYF5BvnGMPijHTQ==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-aria/i18n": "^3.12.4",
-                "@react-aria/interactions": "^3.22.5",
-                "@react-aria/utils": "^3.26.0",
-                "@react-stately/virtualizer": "^4.2.0",
-                "@react-types/shared": "^3.26.0",
+                "@react-aria/i18n": "^3.12.5",
+                "@react-aria/interactions": "^3.23.0",
+                "@react-aria/utils": "^3.27.0",
+                "@react-stately/virtualizer": "^4.2.1",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -7290,18 +7282,19 @@
             }
         },
         "node_modules/@react-aria/visually-hidden": {
-            "version": "3.8.18",
-            "resolved": "https://registry.npmjs.org/@react-aria/visually-hidden/-/visually-hidden-3.8.18.tgz",
-            "integrity": "sha512-l/0igp+uub/salP35SsNWq5mGmg3G5F5QMS1gDZ8p28n7CgjvzyiGhJbbca7Oxvaw1HRFzVl9ev+89I7moNnFQ==",
+            "version": "3.8.19",
+            "resolved": "https://registry.npmjs.org/@react-aria/visually-hidden/-/visually-hidden-3.8.19.tgz",
+            "integrity": "sha512-MZgCCyQ3sdG94J5iJz7I7Ai3IxoN0U5d/+EaUnA1mfK7jf2fSYQBqi6Eyp8sWUYzBTLw4giXB5h0RGAnWzk9hA==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-aria/interactions": "^3.22.5",
-                "@react-aria/utils": "^3.26.0",
-                "@react-types/shared": "^3.26.0",
+                "@react-aria/interactions": "^3.23.0",
+                "@react-aria/utils": "^3.27.0",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
-                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+                "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-google-maps/api": {
@@ -7335,17 +7328,17 @@
             "license": "MIT"
         },
         "node_modules/@react-spectrum/accordion": {
-            "version": "3.0.1",
-            "resolved": "https://registry.npmjs.org/@react-spectrum/accordion/-/accordion-3.0.1.tgz",
-            "integrity": "sha512-FhxOYXKCIyuO7by6VmKAE1AdxlUw4QTEvtHtU6KYlqZBLuNnkz1C7v90UtVC6vJlxuRt73bzEpjKmat7zOcveQ==",
+            "version": "3.0.2",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/accordion/-/accordion-3.0.2.tgz",
+            "integrity": "sha512-DOqrTVgHU2HfN9UPyVH2iPstSMElBAF4B0SBrBiOlIEBan6JvB2k1iaHiMvpdu3fA7B6s4bWo05L3wltKbU10A==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-aria/i18n": "^3.12.4",
-                "@react-spectrum/utils": "^3.12.0",
-                "@react-types/shared": "^3.26.0",
-                "@spectrum-icons/ui": "^3.6.11",
+                "@react-aria/i18n": "^3.12.5",
+                "@react-spectrum/utils": "^3.12.1",
+                "@react-types/shared": "^3.27.0",
+                "@spectrum-icons/ui": "^3.6.12",
                 "@swc/helpers": "^0.5.0",
-                "react-aria-components": "^1.5.0"
+                "react-aria-components": "^1.6.0"
             },
             "peerDependencies": {
                 "@react-spectrum/provider": "^3.0.0",
@@ -7354,25 +7347,25 @@
             }
         },
         "node_modules/@react-spectrum/actionbar": {
-            "version": "3.6.2",
-            "resolved": "https://registry.npmjs.org/@react-spectrum/actionbar/-/actionbar-3.6.2.tgz",
-            "integrity": "sha512-XeywmgJFp9hhjgSNAxyWXfpN5Rmb2bMHbD+qrQ4aWdIKLQuP+P5WbfxGwQ2FanfwvfydpW8Q+n1AxE+MVXz0zg==",
+            "version": "3.6.3",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/actionbar/-/actionbar-3.6.3.tgz",
+            "integrity": "sha512-NtL1zzujcGs+u5xXkrbsgzSfRRP9sD5mBx3+930z8QMLEFhgSsO+D6/h5iVChHt5rjsBQaF/OYCVlyP7+Q7DDg==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-aria/focus": "^3.19.0",
-                "@react-aria/i18n": "^3.12.4",
-                "@react-aria/interactions": "^3.22.5",
+                "@react-aria/focus": "^3.19.1",
+                "@react-aria/i18n": "^3.12.5",
+                "@react-aria/interactions": "^3.23.0",
                 "@react-aria/live-announcer": "^3.4.1",
-                "@react-aria/utils": "^3.26.0",
-                "@react-spectrum/actiongroup": "^3.10.10",
-                "@react-spectrum/button": "^3.16.9",
-                "@react-spectrum/overlays": "^5.7.0",
-                "@react-spectrum/text": "^3.5.10",
-                "@react-spectrum/utils": "^3.12.0",
-                "@react-stately/collections": "^3.12.0",
-                "@react-types/actionbar": "^3.1.11",
-                "@react-types/shared": "^3.26.0",
-                "@spectrum-icons/ui": "^3.6.11",
+                "@react-aria/utils": "^3.27.0",
+                "@react-spectrum/actiongroup": "^3.10.11",
+                "@react-spectrum/button": "^3.16.10",
+                "@react-spectrum/overlays": "^5.7.1",
+                "@react-spectrum/text": "^3.5.11",
+                "@react-spectrum/utils": "^3.12.1",
+                "@react-stately/collections": "^3.12.1",
+                "@react-types/actionbar": "^3.1.12",
+                "@react-types/shared": "^3.27.0",
+                "@spectrum-icons/ui": "^3.6.12",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -7382,26 +7375,26 @@
             }
         },
         "node_modules/@react-spectrum/actiongroup": {
-            "version": "3.10.10",
-            "resolved": "https://registry.npmjs.org/@react-spectrum/actiongroup/-/actiongroup-3.10.10.tgz",
-            "integrity": "sha512-ziBzYdLWVYfTotbR/uFEqKdBb7yETDigC3coT0Qz5YCG6ufuNhuvas6Bm6Alx+7nU8NRg41Xx3G5yTFdV2L0FQ==",
-            "license": "Apache-2.0",
-            "dependencies": {
-                "@react-aria/actiongroup": "^3.7.11",
-                "@react-aria/focus": "^3.19.0",
-                "@react-aria/interactions": "^3.22.5",
-                "@react-aria/utils": "^3.26.0",
-                "@react-spectrum/button": "^3.16.9",
-                "@react-spectrum/menu": "^3.21.0",
-                "@react-spectrum/text": "^3.5.10",
-                "@react-spectrum/tooltip": "^3.7.0",
-                "@react-spectrum/utils": "^3.12.0",
-                "@react-stately/collections": "^3.12.0",
-                "@react-stately/list": "^3.11.1",
-                "@react-types/actiongroup": "^3.4.13",
-                "@react-types/shared": "^3.26.0",
-                "@spectrum-icons/ui": "^3.6.11",
-                "@spectrum-icons/workflow": "^4.2.16",
+            "version": "3.10.11",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/actiongroup/-/actiongroup-3.10.11.tgz",
+            "integrity": "sha512-ddNDYxDR1qe+AK2CO/Wt7M9GDOXTE/BDDLVG6ciOoel+fhAtOidVEwQBzLvv6+g5JmIINP94U/9+Lgpr5EQBXg==",
+            "license": "Apache-2.0",
+            "dependencies": {
+                "@react-aria/actiongroup": "^3.7.12",
+                "@react-aria/focus": "^3.19.1",
+                "@react-aria/interactions": "^3.23.0",
+                "@react-aria/utils": "^3.27.0",
+                "@react-spectrum/button": "^3.16.10",
+                "@react-spectrum/menu": "^3.21.1",
+                "@react-spectrum/text": "^3.5.11",
+                "@react-spectrum/tooltip": "^3.7.1",
+                "@react-spectrum/utils": "^3.12.1",
+                "@react-stately/collections": "^3.12.1",
+                "@react-stately/list": "^3.11.2",
+                "@react-types/actiongroup": "^3.4.14",
+                "@react-types/shared": "^3.27.0",
+                "@spectrum-icons/ui": "^3.6.12",
+                "@spectrum-icons/workflow": "^4.2.17",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -7411,58 +7404,60 @@
             }
         },
         "node_modules/@react-spectrum/avatar": {
-            "version": "3.0.17",
-            "resolved": "https://registry.npmjs.org/@react-spectrum/avatar/-/avatar-3.0.17.tgz",
-            "integrity": "sha512-lmf6SzBZg46A6I2eJr3LEbm8qcrMp8svwOCdGyUOK5q2Yefu2UmOgHnUsDdHznJv9DterCrlxswriXySK2vgpg==",
+            "version": "3.0.18",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/avatar/-/avatar-3.0.18.tgz",
+            "integrity": "sha512-GQRgKxjGzQrhnGnvjBYMMGT1qsHx/hrPBYMPx76Y4WKTquKvkp0VT2acLvXE4c3f4jZYnVPav5UKCBQoMbQRiw==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-aria/utils": "^3.26.0",
-                "@react-spectrum/utils": "^3.12.0",
-                "@react-types/avatar": "^3.0.11",
-                "@react-types/shared": "^3.26.0",
+                "@react-aria/utils": "^3.27.0",
+                "@react-spectrum/utils": "^3.12.1",
+                "@react-types/avatar": "^3.0.12",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
                 "@react-spectrum/provider": "^3.2.1",
-                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+                "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-spectrum/badge": {
-            "version": "3.1.18",
-            "resolved": "https://registry.npmjs.org/@react-spectrum/badge/-/badge-3.1.18.tgz",
-            "integrity": "sha512-Zlpftxsu5C3kMW8uIamMTGfWkpVkKOA7Rzo7UQuLN0TBLT17ITkWQWdyHA/viXHGJi4osw0Eytc9tjHOHz1Ugw==",
+            "version": "3.1.19",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/badge/-/badge-3.1.19.tgz",
+            "integrity": "sha512-e0UpDtU5er+cs3buY/fdxxzn2sd9FVjnsdxL5OvKpBaaqALwDUrnU2R6WUo9FNEk2IIQfBnR7GPD8oXN0h0Gow==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-aria/utils": "^3.26.0",
-                "@react-spectrum/text": "^3.5.10",
-                "@react-spectrum/utils": "^3.12.0",
-                "@react-types/badge": "^3.1.13",
-                "@react-types/shared": "^3.26.0",
+                "@react-aria/utils": "^3.27.0",
+                "@react-spectrum/text": "^3.5.11",
+                "@react-spectrum/utils": "^3.12.1",
+                "@react-types/badge": "^3.1.14",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
                 "@react-spectrum/provider": "^3.0.0",
-                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+                "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-spectrum/breadcrumbs": {
-            "version": "3.9.12",
-            "resolved": "https://registry.npmjs.org/@react-spectrum/breadcrumbs/-/breadcrumbs-3.9.12.tgz",
-            "integrity": "sha512-p9UkUocoAId26dw9Hqyuw/h2zVcbW0yZw8Ttfz+qtyB766RhIFFgtgcWXjbdddKqv/CEgYwWt/pBcCTFkBE/qw==",
-            "license": "Apache-2.0",
-            "dependencies": {
-                "@react-aria/breadcrumbs": "^3.5.19",
-                "@react-aria/focus": "^3.19.0",
-                "@react-aria/i18n": "^3.12.4",
-                "@react-aria/interactions": "^3.22.5",
-                "@react-aria/utils": "^3.26.0",
-                "@react-spectrum/button": "^3.16.9",
-                "@react-spectrum/menu": "^3.21.0",
-                "@react-spectrum/utils": "^3.12.0",
-                "@react-stately/collections": "^3.12.0",
-                "@react-types/breadcrumbs": "^3.7.9",
-                "@react-types/shared": "^3.26.0",
-                "@spectrum-icons/ui": "^3.6.11",
+            "version": "3.9.13",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/breadcrumbs/-/breadcrumbs-3.9.13.tgz",
+            "integrity": "sha512-IKJtFoOAv3sa10FQA3pgCndHeXFmeWKYFfHP3uNIuE5YYmVa05vrfy1hmJzgbTKTZcVBXwtm90F2gm+faV50ng==",
+            "license": "Apache-2.0",
+            "dependencies": {
+                "@react-aria/breadcrumbs": "^3.5.20",
+                "@react-aria/focus": "^3.19.1",
+                "@react-aria/i18n": "^3.12.5",
+                "@react-aria/interactions": "^3.23.0",
+                "@react-aria/utils": "^3.27.0",
+                "@react-spectrum/button": "^3.16.10",
+                "@react-spectrum/menu": "^3.21.1",
+                "@react-spectrum/utils": "^3.12.1",
+                "@react-stately/collections": "^3.12.1",
+                "@react-types/breadcrumbs": "^3.7.10",
+                "@react-types/shared": "^3.27.0",
+                "@spectrum-icons/ui": "^3.6.12",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -7472,23 +7467,23 @@
             }
         },
         "node_modules/@react-spectrum/button": {
-            "version": "3.16.9",
-            "resolved": "https://registry.npmjs.org/@react-spectrum/button/-/button-3.16.9.tgz",
-            "integrity": "sha512-a8LxnRREOvKZT2oGq35xSAFyZpT8NedltluGkF3wigD/2uYBZk0wdIkX+noajcYZ9LLmF9CT9CDB/1EjqVIzxA==",
-            "license": "Apache-2.0",
-            "dependencies": {
-                "@react-aria/button": "^3.11.0",
-                "@react-aria/focus": "^3.19.0",
-                "@react-aria/i18n": "^3.12.4",
-                "@react-aria/interactions": "^3.22.5",
-                "@react-aria/utils": "^3.26.0",
-                "@react-spectrum/progress": "^3.7.11",
-                "@react-spectrum/text": "^3.5.10",
-                "@react-spectrum/utils": "^3.12.0",
-                "@react-stately/toggle": "^3.8.0",
-                "@react-types/button": "^3.10.1",
-                "@react-types/shared": "^3.26.0",
-                "@spectrum-icons/ui": "^3.6.11",
+            "version": "3.16.10",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/button/-/button-3.16.10.tgz",
+            "integrity": "sha512-kh2xoR87FHfKv4Dgj66pSst2hQrq8VZ7eKdeRmJKllKAFaEvqJ6/0PjbVCXVq+iRecBnF9N1qfjN2TiBoDMl6g==",
+            "license": "Apache-2.0",
+            "dependencies": {
+                "@react-aria/button": "^3.11.1",
+                "@react-aria/focus": "^3.19.1",
+                "@react-aria/i18n": "^3.12.5",
+                "@react-aria/interactions": "^3.23.0",
+                "@react-aria/utils": "^3.27.0",
+                "@react-spectrum/progress": "^3.7.12",
+                "@react-spectrum/text": "^3.5.11",
+                "@react-spectrum/utils": "^3.12.1",
+                "@react-stately/toggle": "^3.8.1",
+                "@react-types/button": "^3.10.2",
+                "@react-types/shared": "^3.27.0",
+                "@spectrum-icons/ui": "^3.6.12",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -7497,43 +7492,44 @@
             }
         },
         "node_modules/@react-spectrum/buttongroup": {
-            "version": "3.6.17",
-            "resolved": "https://registry.npmjs.org/@react-spectrum/buttongroup/-/buttongroup-3.6.17.tgz",
-            "integrity": "sha512-IF5LiV8n42iu5V18eq8kYy1EjVy+vINYlwOE0SgYEAgcoAvFUAXmWtrwshoftU5Q2Uglmk5NP9VAbCxivAc2KA==",
+            "version": "3.6.18",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/buttongroup/-/buttongroup-3.6.18.tgz",
+            "integrity": "sha512-CWh3xGz//0Oj8/687x1E/XqACPQJrzGVbVIqH0vVyP+npcQuEcP5MQlkT56b2pYcb0mlRC6vZOtoIoURA8Ramw==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-aria/utils": "^3.26.0",
-                "@react-spectrum/utils": "^3.12.0",
-                "@react-types/buttongroup": "^3.3.13",
-                "@react-types/shared": "^3.26.0",
+                "@react-aria/utils": "^3.27.0",
+                "@react-spectrum/utils": "^3.12.1",
+                "@react-types/buttongroup": "^3.3.14",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
                 "@react-spectrum/provider": "^3.0.0",
-                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+                "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-spectrum/calendar": {
-            "version": "3.5.0",
-            "resolved": "https://registry.npmjs.org/@react-spectrum/calendar/-/calendar-3.5.0.tgz",
-            "integrity": "sha512-lqlNHNREpoZYjljvsdGbs5wvWyG2Kkh/8CE3fsKK9zzaSmAnuD5gQPHUAKhyuxS8sWI/lZFpN3lbbA7fho6Zlg==",
-            "license": "Apache-2.0",
-            "dependencies": {
-                "@internationalized/date": "^3.6.0",
-                "@react-aria/calendar": "^3.6.0",
-                "@react-aria/focus": "^3.19.0",
-                "@react-aria/i18n": "^3.12.4",
-                "@react-aria/interactions": "^3.22.5",
-                "@react-aria/utils": "^3.26.0",
-                "@react-aria/visually-hidden": "^3.8.18",
-                "@react-spectrum/button": "^3.16.9",
-                "@react-spectrum/label": "^3.16.10",
-                "@react-spectrum/utils": "^3.12.0",
-                "@react-stately/calendar": "^3.6.0",
-                "@react-types/button": "^3.10.1",
-                "@react-types/calendar": "^3.5.0",
-                "@react-types/shared": "^3.26.0",
-                "@spectrum-icons/ui": "^3.6.11",
+            "version": "3.6.0",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/calendar/-/calendar-3.6.0.tgz",
+            "integrity": "sha512-8ssvRAi65z2Hhj+VPpzDLhO/i3Xh3fVH97hgglRL1h3Z/9ymH8tpEow07afqYa3Cofr+I8NsDfyhkHwxLbWYhA==",
+            "license": "Apache-2.0",
+            "dependencies": {
+                "@internationalized/date": "^3.7.0",
+                "@react-aria/calendar": "^3.7.0",
+                "@react-aria/focus": "^3.19.1",
+                "@react-aria/i18n": "^3.12.5",
+                "@react-aria/interactions": "^3.23.0",
+                "@react-aria/utils": "^3.27.0",
+                "@react-aria/visually-hidden": "^3.8.19",
+                "@react-spectrum/button": "^3.16.10",
+                "@react-spectrum/label": "^3.16.11",
+                "@react-spectrum/utils": "^3.12.1",
+                "@react-stately/calendar": "^3.7.0",
+                "@react-types/button": "^3.10.2",
+                "@react-types/calendar": "^3.6.0",
+                "@react-types/shared": "^3.27.0",
+                "@spectrum-icons/ui": "^3.6.12",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -7543,24 +7539,24 @@
             }
         },
         "node_modules/@react-spectrum/checkbox": {
-            "version": "3.9.11",
-            "resolved": "https://registry.npmjs.org/@react-spectrum/checkbox/-/checkbox-3.9.11.tgz",
-            "integrity": "sha512-2M7P0ZCKeuUXGxWMiVuAWZ3gkaIdNZnfXPLPx84qbxlXbDqenKFUmx3DpbN2cij47aFanvpyf2GzXIpo+HxIRw==",
-            "license": "Apache-2.0",
-            "dependencies": {
-                "@react-aria/checkbox": "^3.15.0",
-                "@react-aria/focus": "^3.19.0",
-                "@react-aria/interactions": "^3.22.5",
-                "@react-spectrum/form": "^3.7.10",
-                "@react-spectrum/label": "^3.16.10",
-                "@react-spectrum/utils": "^3.12.0",
-                "@react-stately/checkbox": "^3.6.10",
-                "@react-stately/toggle": "^3.8.0",
-                "@react-types/checkbox": "^3.9.0",
-                "@react-types/shared": "^3.26.0",
-                "@spectrum-icons/ui": "^3.6.11",
+            "version": "3.9.12",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/checkbox/-/checkbox-3.9.12.tgz",
+            "integrity": "sha512-Hl7S5ezuKEO3VON64hpWEw5EaX9SlAXhT8+bokPSRe4A9mVEiFTA3iNyLutU4fzX2JmFgLxWoIEynpCllo6TCQ==",
+            "license": "Apache-2.0",
+            "dependencies": {
+                "@react-aria/checkbox": "^3.15.1",
+                "@react-aria/focus": "^3.19.1",
+                "@react-aria/interactions": "^3.23.0",
+                "@react-spectrum/form": "^3.7.11",
+                "@react-spectrum/label": "^3.16.11",
+                "@react-spectrum/utils": "^3.12.1",
+                "@react-stately/checkbox": "^3.6.11",
+                "@react-stately/toggle": "^3.8.1",
+                "@react-types/checkbox": "^3.9.1",
+                "@react-types/shared": "^3.27.0",
+                "@spectrum-icons/ui": "^3.6.12",
                 "@swc/helpers": "^0.5.0",
-                "react-aria-components": "^1.5.0"
+                "react-aria-components": "^1.6.0"
             },
             "peerDependencies": {
                 "@react-spectrum/provider": "^3.0.0",
@@ -7568,30 +7564,30 @@
             }
         },
         "node_modules/@react-spectrum/color": {
-            "version": "3.0.2",
-            "resolved": "https://registry.npmjs.org/@react-spectrum/color/-/color-3.0.2.tgz",
-            "integrity": "sha512-6cYi4C3q4N4aCHGa3YXJ+0SESjIZng7LPC0q1ls/cci28LX4rLupTJ66SVr1q4RiPf56/0wt4J7353btNW8QPA==",
-            "license": "Apache-2.0",
-            "dependencies": {
-                "@react-aria/color": "^3.0.2",
-                "@react-aria/focus": "^3.19.0",
-                "@react-aria/i18n": "^3.12.4",
-                "@react-aria/interactions": "^3.22.5",
-                "@react-aria/utils": "^3.26.0",
-                "@react-spectrum/dialog": "^3.8.16",
-                "@react-spectrum/form": "^3.7.10",
-                "@react-spectrum/label": "^3.16.10",
-                "@react-spectrum/overlays": "^5.7.0",
-                "@react-spectrum/picker": "^3.15.4",
-                "@react-spectrum/textfield": "^3.12.7",
-                "@react-spectrum/utils": "^3.12.0",
-                "@react-spectrum/view": "^3.6.14",
-                "@react-stately/color": "^3.8.1",
-                "@react-types/color": "^3.0.1",
-                "@react-types/shared": "^3.26.0",
-                "@react-types/textfield": "^3.10.0",
+            "version": "3.0.3",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/color/-/color-3.0.3.tgz",
+            "integrity": "sha512-8Kec4L5IUGvc2F6Fn7AQ7Zeji/al0mAWm05+W3Bx6DRuY5i9iwNTs+KeWoeG/1fqEJ3HmA54w772mKkrvEluOg==",
+            "license": "Apache-2.0",
+            "dependencies": {
+                "@react-aria/color": "^3.0.3",
+                "@react-aria/focus": "^3.19.1",
+                "@react-aria/i18n": "^3.12.5",
+                "@react-aria/interactions": "^3.23.0",
+                "@react-aria/utils": "^3.27.0",
+                "@react-spectrum/dialog": "^3.8.17",
+                "@react-spectrum/form": "^3.7.11",
+                "@react-spectrum/label": "^3.16.11",
+                "@react-spectrum/overlays": "^5.7.1",
+                "@react-spectrum/picker": "^3.15.5",
+                "@react-spectrum/textfield": "^3.12.8",
+                "@react-spectrum/utils": "^3.12.1",
+                "@react-spectrum/view": "^3.6.15",
+                "@react-stately/color": "^3.8.2",
+                "@react-types/color": "^3.0.2",
+                "@react-types/shared": "^3.27.0",
+                "@react-types/textfield": "^3.11.0",
                 "@swc/helpers": "^0.5.0",
-                "react-aria-components": "^1.5.0"
+                "react-aria-components": "^1.6.0"
             },
             "peerDependencies": {
                 "@react-spectrum/provider": "^3.0.0",
@@ -7600,35 +7596,35 @@
             }
         },
         "node_modules/@react-spectrum/combobox": {
-            "version": "3.14.0",
-            "resolved": "https://registry.npmjs.org/@react-spectrum/combobox/-/combobox-3.14.0.tgz",
-            "integrity": "sha512-3Xv2pR+vmlcLbYKC2vOTx6xbkQYp9Qbr4cCez5JKvBHeQ/q+Vu8prPKAJfcl//QLGNFyV2xMSHyyP9ZUwpf89Q==",
-            "license": "Apache-2.0",
-            "dependencies": {
-                "@react-aria/button": "^3.11.0",
-                "@react-aria/combobox": "^3.11.0",
-                "@react-aria/dialog": "^3.5.20",
-                "@react-aria/focus": "^3.19.0",
-                "@react-aria/form": "^3.0.11",
-                "@react-aria/i18n": "^3.12.4",
-                "@react-aria/interactions": "^3.22.5",
-                "@react-aria/label": "^3.7.13",
-                "@react-aria/overlays": "^3.24.0",
-                "@react-aria/utils": "^3.26.0",
-                "@react-spectrum/button": "^3.16.9",
-                "@react-spectrum/form": "^3.7.10",
-                "@react-spectrum/label": "^3.16.10",
-                "@react-spectrum/listbox": "^3.14.0",
-                "@react-spectrum/overlays": "^5.7.0",
-                "@react-spectrum/progress": "^3.7.11",
-                "@react-spectrum/textfield": "^3.12.7",
-                "@react-spectrum/utils": "^3.12.0",
-                "@react-stately/collections": "^3.12.0",
-                "@react-stately/combobox": "^3.10.1",
-                "@react-types/button": "^3.10.1",
-                "@react-types/combobox": "^3.13.1",
-                "@react-types/shared": "^3.26.0",
-                "@spectrum-icons/ui": "^3.6.11",
+            "version": "3.14.1",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/combobox/-/combobox-3.14.1.tgz",
+            "integrity": "sha512-4Hs9ZL47zy/zc6FwaC+6v3isP3+cf3tHtYGE2pyKIw+GSFvzlam0VVOUJSXlfP0moOoz7xj+cQz2ahl/2RECsA==",
+            "license": "Apache-2.0",
+            "dependencies": {
+                "@react-aria/button": "^3.11.1",
+                "@react-aria/combobox": "^3.11.1",
+                "@react-aria/dialog": "^3.5.21",
+                "@react-aria/focus": "^3.19.1",
+                "@react-aria/form": "^3.0.12",
+                "@react-aria/i18n": "^3.12.5",
+                "@react-aria/interactions": "^3.23.0",
+                "@react-aria/label": "^3.7.14",
+                "@react-aria/overlays": "^3.25.0",
+                "@react-aria/utils": "^3.27.0",
+                "@react-spectrum/button": "^3.16.10",
+                "@react-spectrum/form": "^3.7.11",
+                "@react-spectrum/label": "^3.16.11",
+                "@react-spectrum/listbox": "^3.14.1",
+                "@react-spectrum/overlays": "^5.7.1",
+                "@react-spectrum/progress": "^3.7.12",
+                "@react-spectrum/textfield": "^3.12.8",
+                "@react-spectrum/utils": "^3.12.1",
+                "@react-stately/collections": "^3.12.1",
+                "@react-stately/combobox": "^3.10.2",
+                "@react-types/button": "^3.10.2",
+                "@react-types/combobox": "^3.13.2",
+                "@react-types/shared": "^3.27.0",
+                "@spectrum-icons/ui": "^3.6.12",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -7638,19 +7634,19 @@
             }
         },
         "node_modules/@react-spectrum/contextualhelp": {
-            "version": "3.6.16",
-            "resolved": "https://registry.npmjs.org/@react-spectrum/contextualhelp/-/contextualhelp-3.6.16.tgz",
-            "integrity": "sha512-Vi9+HfZgafWphYzlzXaAewvclgbktNkrsHb/ed4B89Xk4gkwqI5oPYPObNcMqFm9WfNMVrtS6D7Iu00vdTnKpQ==",
-            "license": "Apache-2.0",
-            "dependencies": {
-                "@react-aria/i18n": "^3.12.4",
-                "@react-aria/utils": "^3.26.0",
-                "@react-spectrum/button": "^3.16.9",
-                "@react-spectrum/dialog": "^3.8.16",
-                "@react-spectrum/utils": "^3.12.0",
-                "@react-types/contextualhelp": "^3.2.14",
-                "@react-types/shared": "^3.26.0",
-                "@spectrum-icons/workflow": "^4.2.16",
+            "version": "3.6.17",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/contextualhelp/-/contextualhelp-3.6.17.tgz",
+            "integrity": "sha512-toz3rJhWXZPJtedALPcp9ZZPV/pvxxkWCx5GiSLwOPkskCge2FE4TThgR8VBSBjkrGgboeHSw30mR5ki7CR9JQ==",
+            "license": "Apache-2.0",
+            "dependencies": {
+                "@react-aria/i18n": "^3.12.5",
+                "@react-aria/utils": "^3.27.0",
+                "@react-spectrum/button": "^3.16.10",
+                "@react-spectrum/dialog": "^3.8.17",
+                "@react-spectrum/utils": "^3.12.1",
+                "@react-types/contextualhelp": "^3.2.15",
+                "@react-types/shared": "^3.27.0",
+                "@spectrum-icons/workflow": "^4.2.17",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -7660,30 +7656,30 @@
             }
         },
         "node_modules/@react-spectrum/datepicker": {
-            "version": "3.11.0",
-            "resolved": "https://registry.npmjs.org/@react-spectrum/datepicker/-/datepicker-3.11.0.tgz",
-            "integrity": "sha512-8cEFuO8gO0a2dLEgyA6/OM3HPVEQM1hcoNN9dixPY4rPza0Y1f+GVV40/szsfP0Dnd19WL/NOABv9omGYxh5Lg==",
-            "license": "Apache-2.0",
-            "dependencies": {
-                "@internationalized/date": "^3.6.0",
-                "@react-aria/datepicker": "^3.12.0",
-                "@react-aria/focus": "^3.19.0",
-                "@react-aria/i18n": "^3.12.4",
-                "@react-aria/interactions": "^3.22.5",
-                "@react-aria/utils": "^3.26.0",
-                "@react-spectrum/button": "^3.16.9",
-                "@react-spectrum/calendar": "^3.5.0",
-                "@react-spectrum/dialog": "^3.8.16",
-                "@react-spectrum/form": "^3.7.10",
-                "@react-spectrum/label": "^3.16.10",
-                "@react-spectrum/layout": "^3.6.10",
-                "@react-spectrum/utils": "^3.12.0",
-                "@react-spectrum/view": "^3.6.14",
-                "@react-stately/datepicker": "^3.11.0",
-                "@react-types/datepicker": "^3.9.0",
-                "@react-types/shared": "^3.26.0",
-                "@spectrum-icons/ui": "^3.6.11",
-                "@spectrum-icons/workflow": "^4.2.16",
+            "version": "3.12.0",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/datepicker/-/datepicker-3.12.0.tgz",
+            "integrity": "sha512-JI6/us96eQAmunRaAFRE5LHNicS3N3G0F28zuPumEu2iIa0ZZqDBS5D8wvVFhGLq/J22WMvqHddXRbS03bJlxA==",
+            "license": "Apache-2.0",
+            "dependencies": {
+                "@internationalized/date": "^3.7.0",
+                "@react-aria/datepicker": "^3.13.0",
+                "@react-aria/focus": "^3.19.1",
+                "@react-aria/i18n": "^3.12.5",
+                "@react-aria/interactions": "^3.23.0",
+                "@react-aria/utils": "^3.27.0",
+                "@react-spectrum/button": "^3.16.10",
+                "@react-spectrum/calendar": "^3.6.0",
+                "@react-spectrum/dialog": "^3.8.17",
+                "@react-spectrum/form": "^3.7.11",
+                "@react-spectrum/label": "^3.16.11",
+                "@react-spectrum/layout": "^3.6.11",
+                "@react-spectrum/utils": "^3.12.1",
+                "@react-spectrum/view": "^3.6.15",
+                "@react-stately/datepicker": "^3.12.0",
+                "@react-types/datepicker": "^3.10.0",
+                "@react-types/shared": "^3.27.0",
+                "@spectrum-icons/ui": "^3.6.12",
+                "@spectrum-icons/workflow": "^4.2.17",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -7693,29 +7689,29 @@
             }
         },
         "node_modules/@react-spectrum/dialog": {
-            "version": "3.8.16",
-            "resolved": "https://registry.npmjs.org/@react-spectrum/dialog/-/dialog-3.8.16.tgz",
-            "integrity": "sha512-uPtoO+fLmGOPGRVQS10rdhMa6jcOVxy82G/nLKodYLqvJL1y8JFZSSElWMkspT8TKh+uHN8uFnV6OGe9MpFSyg==",
-            "license": "Apache-2.0",
-            "dependencies": {
-                "@react-aria/dialog": "^3.5.20",
-                "@react-aria/i18n": "^3.12.4",
-                "@react-aria/interactions": "^3.22.5",
-                "@react-aria/overlays": "^3.24.0",
-                "@react-aria/utils": "^3.26.0",
-                "@react-spectrum/button": "^3.16.9",
-                "@react-spectrum/buttongroup": "^3.6.17",
-                "@react-spectrum/divider": "^3.5.18",
-                "@react-spectrum/layout": "^3.6.10",
-                "@react-spectrum/overlays": "^5.7.0",
-                "@react-spectrum/text": "^3.5.10",
-                "@react-spectrum/utils": "^3.12.0",
-                "@react-spectrum/view": "^3.6.14",
-                "@react-stately/overlays": "^3.6.12",
-                "@react-types/button": "^3.10.1",
-                "@react-types/dialog": "^3.5.14",
-                "@react-types/shared": "^3.26.0",
-                "@spectrum-icons/ui": "^3.6.11",
+            "version": "3.8.17",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/dialog/-/dialog-3.8.17.tgz",
+            "integrity": "sha512-EfNI3ZmA5rmf+8jHXXHcmXvCgAev+z271VMG2yTyUq2+565IypmhNzdMYyGHeO+a3ogMxRPigFqNyMjv1AsUsQ==",
+            "license": "Apache-2.0",
+            "dependencies": {
+                "@react-aria/dialog": "^3.5.21",
+                "@react-aria/i18n": "^3.12.5",
+                "@react-aria/interactions": "^3.23.0",
+                "@react-aria/overlays": "^3.25.0",
+                "@react-aria/utils": "^3.27.0",
+                "@react-spectrum/button": "^3.16.10",
+                "@react-spectrum/buttongroup": "^3.6.18",
+                "@react-spectrum/divider": "^3.5.19",
+                "@react-spectrum/layout": "^3.6.11",
+                "@react-spectrum/overlays": "^5.7.1",
+                "@react-spectrum/text": "^3.5.11",
+                "@react-spectrum/utils": "^3.12.1",
+                "@react-spectrum/view": "^3.6.15",
+                "@react-stately/overlays": "^3.6.13",
+                "@react-types/button": "^3.10.2",
+                "@react-types/dialog": "^3.5.15",
+                "@react-types/shared": "^3.27.0",
+                "@spectrum-icons/ui": "^3.6.12",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -7725,15 +7721,15 @@
             }
         },
         "node_modules/@react-spectrum/divider": {
-            "version": "3.5.18",
-            "resolved": "https://registry.npmjs.org/@react-spectrum/divider/-/divider-3.5.18.tgz",
-            "integrity": "sha512-CzT3Zbt1d+xN8erwYJqHcqklEZdYTkXZokKRcPP0JaVhpeSnmw1U8iIYkXUcJOtDm4WpSauF0ioSFp8U1zCxJQ==",
+            "version": "3.5.19",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/divider/-/divider-3.5.19.tgz",
+            "integrity": "sha512-SQgLNiqK8xnE4tCPrLgMEezH5DaFlM03CRANCs4Ewav2M+48+DuDij5iZkQpnFmNZ0W8CVOjmm3lAyn5eVrLMQ==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-aria/separator": "^3.4.4",
-                "@react-spectrum/utils": "^3.12.0",
-                "@react-types/divider": "^3.3.13",
-                "@react-types/shared": "^3.26.0",
+                "@react-aria/separator": "^3.4.5",
+                "@react-spectrum/utils": "^3.12.1",
+                "@react-types/divider": "^3.3.14",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -7741,14 +7737,14 @@
             }
         },
         "node_modules/@react-spectrum/dnd": {
-            "version": "3.5.0",
-            "resolved": "https://registry.npmjs.org/@react-spectrum/dnd/-/dnd-3.5.0.tgz",
-            "integrity": "sha512-NTiyMBPsgHVgvVxPuaesK3d59r7Sgdh5r/gjiMJ5kRWYN48xwCs2VZD5gPo3sq9uzw6MXV1ujqch52Bs05TVEA==",
+            "version": "3.5.1",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/dnd/-/dnd-3.5.1.tgz",
+            "integrity": "sha512-iNFm7wM1647L2nnusmRqyw0BJ4vHtL5OkCdVkTKqgS/yI/z5f9ahWUvkDKhIrBFcRz99bQmCYU56vuEJEPy3sA==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-aria/dnd": "^3.8.0",
-                "@react-stately/dnd": "^3.5.0",
-                "@react-types/shared": "^3.26.0",
+                "@react-aria/dnd": "^3.8.1",
+                "@react-stately/dnd": "^3.5.1",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -7758,17 +7754,17 @@
             }
         },
         "node_modules/@react-spectrum/dropzone": {
-            "version": "3.0.6",
-            "resolved": "https://registry.npmjs.org/@react-spectrum/dropzone/-/dropzone-3.0.6.tgz",
-            "integrity": "sha512-0Bp50lXhIPNGVG912f6LAR60f9LmPvtsAkz2s/V1rgH347RCc6CpYOTGi5CgKIsoiXz/pecTAaSW7Q6qKi7W0w==",
+            "version": "3.0.7",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/dropzone/-/dropzone-3.0.7.tgz",
+            "integrity": "sha512-C0yjLqgKmPG76jSNygARQgtjv/vqV4dR5JZGNZ848BB63rrTwXi1kdCC6MG1G2IPxJ/n5SUgZ1WpMMRk1oTRlQ==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-aria/i18n": "^3.12.4",
-                "@react-aria/utils": "^3.26.0",
-                "@react-spectrum/utils": "^3.12.0",
-                "@react-types/shared": "^3.26.0",
+                "@react-aria/i18n": "^3.12.5",
+                "@react-aria/utils": "^3.27.0",
+                "@react-spectrum/utils": "^3.12.1",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0",
-                "react-aria-components": "^1.5.0"
+                "react-aria-components": "^1.6.0"
             },
             "peerDependencies": {
                 "@react-spectrum/provider": "^3.0.0",
@@ -7776,13 +7772,13 @@
             }
         },
         "node_modules/@react-spectrum/filetrigger": {
-            "version": "3.0.6",
-            "resolved": "https://registry.npmjs.org/@react-spectrum/filetrigger/-/filetrigger-3.0.6.tgz",
-            "integrity": "sha512-zR0sdl80VDTF+3FeDopUO4ooTlsmw97GNlBwjd0B9bJIbeyl1oTDwLIAqE8OEyQxmsBlnfxWmCCDn4laDN+QnQ==",
+            "version": "3.0.7",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/filetrigger/-/filetrigger-3.0.7.tgz",
+            "integrity": "sha512-uemm4begyWP276yOY5BjnQ0SsYYnfspr2BQ1GH3TFSNJTgdSgSH36Hjl8Mgp1exXITF2O5DQvhw2M2iA/1r64A==",
             "license": "Apache-2.0",
             "dependencies": {
                 "@swc/helpers": "^0.5.0",
-                "react-aria-components": "^1.5.0"
+                "react-aria-components": "^1.6.0"
             },
             "peerDependencies": {
                 "@react-spectrum/provider": "^3.0.0",
@@ -7790,199 +7786,208 @@
             }
         },
         "node_modules/@react-spectrum/form": {
-            "version": "3.7.10",
-            "resolved": "https://registry.npmjs.org/@react-spectrum/form/-/form-3.7.10.tgz",
-            "integrity": "sha512-AebgYhpbQXuAPq8w596dmhVu9/1pjMcAlhcfnXI0ZgXwFzz8ZnZQ34vPNxPoX3GRPy8Zkjt+WdSWf8f6fZavLg==",
+            "version": "3.7.11",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/form/-/form-3.7.11.tgz",
+            "integrity": "sha512-/Wp+qN6h3IWHH20ls9EJkzuQgMiDfdqCHLvSgHfYyvqeF8CGsis97YMjlNlFkA8qtcDQj+B2GMQM1icdEKMAJw==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-aria/utils": "^3.26.0",
-                "@react-spectrum/utils": "^3.12.0",
-                "@react-stately/form": "^3.1.0",
-                "@react-types/form": "^3.7.8",
-                "@react-types/shared": "^3.26.0",
+                "@react-aria/utils": "^3.27.0",
+                "@react-spectrum/utils": "^3.12.1",
+                "@react-stately/form": "^3.1.1",
+                "@react-types/form": "^3.7.9",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
                 "@react-spectrum/provider": "^3.0.0",
-                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+                "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-spectrum/icon": {
-            "version": "3.8.0",
-            "resolved": "https://registry.npmjs.org/@react-spectrum/icon/-/icon-3.8.0.tgz",
-            "integrity": "sha512-l4TlpCoGbnms/E9OwQqAx2P6TGI+dGqc2x5o4jcLO+BCpgWMbaWROvRIQNBY4JP5XG+QIb8GwOeCIiX6Fml18A==",
+            "version": "3.8.1",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/icon/-/icon-3.8.1.tgz",
+            "integrity": "sha512-qjhzrZWDDrNG3YetloKQMYQgtoyhWMI9nglv6Jl31ZP3RwZrORDeHthVs02QB5pWSdMl6on4Po2g7W61lo0dzg==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-aria/utils": "^3.26.0",
-                "@react-spectrum/utils": "^3.12.0",
-                "@react-types/shared": "^3.26.0",
+                "@react-aria/utils": "^3.27.0",
+                "@react-spectrum/utils": "^3.12.1",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
                 "@react-spectrum/provider": "^3.0.0",
-                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+                "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-spectrum/illustratedmessage": {
-            "version": "3.5.5",
-            "resolved": "https://registry.npmjs.org/@react-spectrum/illustratedmessage/-/illustratedmessage-3.5.5.tgz",
-            "integrity": "sha512-mjdUBYif9LsY5ZKtvLq5rQj0uExBE/tVLRy/KL3TbrJDHh9I4bE9c1neILhPFT3udF85kmOFg+cX3101zcLolg==",
+            "version": "3.5.6",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/illustratedmessage/-/illustratedmessage-3.5.6.tgz",
+            "integrity": "sha512-9BMvRx4XCiEWLR53RbYENgqESqthcU3kp0Slpo3Fo9li9GPKyFI7xEMGCzWuBMG+DitwkkjgN1wQxnENu/UFXQ==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-aria/utils": "^3.26.0",
-                "@react-spectrum/layout": "^3.6.10",
-                "@react-spectrum/utils": "^3.12.0",
-                "@react-types/illustratedmessage": "^3.3.13",
-                "@react-types/shared": "^3.26.0",
+                "@react-aria/utils": "^3.27.0",
+                "@react-spectrum/layout": "^3.6.11",
+                "@react-spectrum/utils": "^3.12.1",
+                "@react-types/illustratedmessage": "^3.3.14",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
                 "@react-spectrum/provider": "^3.0.0",
-                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+                "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-spectrum/image": {
-            "version": "3.5.6",
-            "resolved": "https://registry.npmjs.org/@react-spectrum/image/-/image-3.5.6.tgz",
-            "integrity": "sha512-5c5Ac3Uuf8E0NKtZm+iDBRkTzvmbjMgtYiBb9NZJnNvBvpvvYZ9bCdE8K1WUHfu7MELczexZH2aGwWbtCr3hnA==",
+            "version": "3.5.7",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/image/-/image-3.5.7.tgz",
+            "integrity": "sha512-n+E2GN6/Ce5SRhE8lDVQSOCX2Joag64AQDNmrjaT/urb4L0gcMccNym1LcHibzJSXonisPC9eUgb++w+x/p3Cg==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-aria/utils": "^3.26.0",
-                "@react-spectrum/utils": "^3.12.0",
-                "@react-types/image": "^3.4.5",
-                "@react-types/shared": "^3.26.0",
+                "@react-aria/utils": "^3.27.0",
+                "@react-spectrum/utils": "^3.12.1",
+                "@react-types/image": "^3.4.6",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
                 "@react-spectrum/provider": "^3.0.0",
-                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+                "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-spectrum/inlinealert": {
-            "version": "3.2.10",
-            "resolved": "https://registry.npmjs.org/@react-spectrum/inlinealert/-/inlinealert-3.2.10.tgz",
-            "integrity": "sha512-oP8dhN3yqJkRREQDAvnd+vaPe64uNYvE2r0Un0UHPWEUVmE0fKrEFVxrPZSIQCtC/3JxwTpvh1r3baLTW7HNCA==",
-            "license": "Apache-2.0",
-            "dependencies": {
-                "@react-aria/focus": "^3.19.0",
-                "@react-aria/i18n": "^3.12.4",
-                "@react-aria/utils": "^3.26.0",
-                "@react-spectrum/layout": "^3.6.10",
-                "@react-spectrum/utils": "^3.12.0",
-                "@react-types/shared": "^3.26.0",
-                "@spectrum-icons/ui": "^3.6.11",
+            "version": "3.2.11",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/inlinealert/-/inlinealert-3.2.11.tgz",
+            "integrity": "sha512-JXXwEh3rRFWIPpyTcx2tSDqyEHwG1geS4TPoRVzGCTs0NFTnMzo06+1xtAcOoQrSavRot6x+l/OMNVNG+1BMLQ==",
+            "license": "Apache-2.0",
+            "dependencies": {
+                "@react-aria/focus": "^3.19.1",
+                "@react-aria/i18n": "^3.12.5",
+                "@react-aria/utils": "^3.27.0",
+                "@react-spectrum/layout": "^3.6.11",
+                "@react-spectrum/utils": "^3.12.1",
+                "@react-types/shared": "^3.27.0",
+                "@spectrum-icons/ui": "^3.6.12",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
                 "@react-spectrum/provider": "^3.0.0",
-                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+                "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-spectrum/label": {
-            "version": "3.16.10",
-            "resolved": "https://registry.npmjs.org/@react-spectrum/label/-/label-3.16.10.tgz",
-            "integrity": "sha512-8IpP3DMM6bbqt14D0oo//dmQRW4ghycQVgmGbaotsvHPdazLdzOZCzo3kQRC3AVB1WOZBrwnGikNnBQdaBjX9Q==",
-            "license": "Apache-2.0",
-            "dependencies": {
-                "@react-aria/i18n": "^3.12.4",
-                "@react-aria/utils": "^3.26.0",
-                "@react-spectrum/form": "^3.7.10",
-                "@react-spectrum/layout": "^3.6.10",
-                "@react-spectrum/utils": "^3.12.0",
-                "@react-types/label": "^3.9.7",
-                "@react-types/shared": "^3.26.0",
-                "@spectrum-icons/ui": "^3.6.11",
+            "version": "3.16.11",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/label/-/label-3.16.11.tgz",
+            "integrity": "sha512-ndFSDIn5jkk3zmLtesyZx4LPXdyFOb0OD1t23vG2IePYhoOkown5Mklq3KWla1gtp4hqPW77qsSlve28xZL5Cw==",
+            "license": "Apache-2.0",
+            "dependencies": {
+                "@react-aria/i18n": "^3.12.5",
+                "@react-aria/utils": "^3.27.0",
+                "@react-spectrum/form": "^3.7.11",
+                "@react-spectrum/layout": "^3.6.11",
+                "@react-spectrum/utils": "^3.12.1",
+                "@react-types/label": "^3.9.8",
+                "@react-types/shared": "^3.27.0",
+                "@spectrum-icons/ui": "^3.6.12",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
                 "@react-spectrum/provider": "^3.0.0",
-                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+                "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-spectrum/labeledvalue": {
-            "version": "3.1.18",
-            "resolved": "https://registry.npmjs.org/@react-spectrum/labeledvalue/-/labeledvalue-3.1.18.tgz",
-            "integrity": "sha512-GG6bxGpLI8b3RowCptp4lGdXFOv0xy4gl+g91ar4d6QZGBLPnOZN7zHF+3hBAOI/2dEHsYj3RXbiLbxD05n0ew==",
+            "version": "3.1.19",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/labeledvalue/-/labeledvalue-3.1.19.tgz",
+            "integrity": "sha512-rxqY45ZcGgUIICiB2bjxyUWWtjdrfBcIvpOpL/JcET0fjAOZhdsJ9zvuPlF+ohkuTlMNxa8tLWUwx3aASjW0YA==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@internationalized/date": "^3.6.0",
-                "@react-aria/i18n": "^3.12.4",
-                "@react-aria/utils": "^3.26.0",
-                "@react-spectrum/label": "^3.16.10",
-                "@react-spectrum/utils": "^3.12.0",
-                "@react-types/shared": "^3.26.0",
+                "@internationalized/date": "^3.7.0",
+                "@react-aria/i18n": "^3.12.5",
+                "@react-aria/utils": "^3.27.0",
+                "@react-spectrum/label": "^3.16.11",
+                "@react-spectrum/utils": "^3.12.1",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
                 "@react-spectrum/provider": "^3.0.0",
-                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+                "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-spectrum/layout": {
-            "version": "3.6.10",
-            "resolved": "https://registry.npmjs.org/@react-spectrum/layout/-/layout-3.6.10.tgz",
-            "integrity": "sha512-iIjfxchH4M6dG2MbiEA6vpqeBhjz2qkmKPOBaFHm3iiGr2s8Iuk8emttPYrKlOql+bgOZwJymZiNFdvyvxyNkg==",
+            "version": "3.6.11",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/layout/-/layout-3.6.11.tgz",
+            "integrity": "sha512-xorbUUROT4VgKah18361UWGCa68PI1lJO4WYLlMWk/LNEECHdsj+/tCJHsjLbSAnxEMTKqTGaJLXlputMidRwA==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-aria/utils": "^3.26.0",
-                "@react-spectrum/utils": "^3.12.0",
-                "@react-types/layout": "^3.3.19",
-                "@react-types/shared": "^3.26.0",
+                "@react-aria/utils": "^3.27.0",
+                "@react-spectrum/utils": "^3.12.1",
+                "@react-types/layout": "^3.3.20",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
                 "@react-spectrum/provider": "^3.0.0",
-                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+                "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-spectrum/link": {
-            "version": "3.6.12",
-            "resolved": "https://registry.npmjs.org/@react-spectrum/link/-/link-3.6.12.tgz",
-            "integrity": "sha512-bEMaDXzZpgBo+9eRqhuEjnh/Z2jzU7B/v8BER0kk9Wttoyo9asAaygE0vPWx94lOibPXooDGJzXhZoawAmGpMg==",
-            "license": "Apache-2.0",
-            "dependencies": {
-                "@react-aria/focus": "^3.19.0",
-                "@react-aria/interactions": "^3.22.5",
-                "@react-aria/link": "^3.7.7",
-                "@react-aria/utils": "^3.26.0",
-                "@react-spectrum/utils": "^3.12.0",
-                "@react-types/link": "^3.5.9",
-                "@react-types/shared": "^3.26.0",
+            "version": "3.6.13",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/link/-/link-3.6.13.tgz",
+            "integrity": "sha512-iBFSllw25uTZPfE5SB9WmxXht+2525cnJIPt01U0hRYPlDtpZc81+8JXBLSE7nOPm+X/s77ofgtxDqmhrV01kg==",
+            "license": "Apache-2.0",
+            "dependencies": {
+                "@react-aria/focus": "^3.19.1",
+                "@react-aria/interactions": "^3.23.0",
+                "@react-aria/link": "^3.7.8",
+                "@react-aria/utils": "^3.27.0",
+                "@react-spectrum/utils": "^3.12.1",
+                "@react-types/link": "^3.5.10",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
                 "@react-spectrum/provider": "^3.0.0",
-                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+                "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-spectrum/list": {
-            "version": "3.9.0",
-            "resolved": "https://registry.npmjs.org/@react-spectrum/list/-/list-3.9.0.tgz",
-            "integrity": "sha512-4WW3gs4cf4Z38rdvOuNynnbqPaipRgN8Ar7/i9iYBv6gQOILpaodL6LJeIPtpCN/TWja/zbedeO1FinMJRiLDA==",
-            "license": "Apache-2.0",
-            "dependencies": {
-                "@react-aria/button": "^3.11.0",
-                "@react-aria/focus": "^3.19.0",
-                "@react-aria/gridlist": "^3.10.0",
-                "@react-aria/i18n": "^3.12.4",
-                "@react-aria/interactions": "^3.22.5",
-                "@react-aria/selection": "^3.21.0",
-                "@react-aria/utils": "^3.26.0",
-                "@react-aria/virtualizer": "^4.1.0",
-                "@react-aria/visually-hidden": "^3.8.18",
-                "@react-spectrum/checkbox": "^3.9.11",
-                "@react-spectrum/dnd": "^3.5.0",
-                "@react-spectrum/layout": "^3.6.10",
-                "@react-spectrum/progress": "^3.7.11",
-                "@react-spectrum/text": "^3.5.10",
-                "@react-spectrum/utils": "^3.12.0",
-                "@react-stately/collections": "^3.12.0",
-                "@react-stately/layout": "^4.1.0",
-                "@react-stately/list": "^3.11.1",
-                "@react-stately/virtualizer": "^4.2.0",
-                "@react-types/grid": "^3.2.10",
-                "@react-types/shared": "^3.26.0",
-                "@spectrum-icons/ui": "^3.6.11",
+            "version": "3.9.1",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/list/-/list-3.9.1.tgz",
+            "integrity": "sha512-Gz4yTwO6V6nZsY3B82HZ0PP1HMbSc/Baay61NqPzzlgsZfJvDpWfnNwd1dkIpmW/xotUtV6misrreQoup97U4g==",
+            "license": "Apache-2.0",
+            "dependencies": {
+                "@react-aria/button": "^3.11.1",
+                "@react-aria/focus": "^3.19.1",
+                "@react-aria/gridlist": "^3.10.1",
+                "@react-aria/i18n": "^3.12.5",
+                "@react-aria/interactions": "^3.23.0",
+                "@react-aria/selection": "^3.22.0",
+                "@react-aria/utils": "^3.27.0",
+                "@react-aria/virtualizer": "^4.1.1",
+                "@react-aria/visually-hidden": "^3.8.19",
+                "@react-spectrum/checkbox": "^3.9.12",
+                "@react-spectrum/dnd": "^3.5.1",
+                "@react-spectrum/layout": "^3.6.11",
+                "@react-spectrum/progress": "^3.7.12",
+                "@react-spectrum/text": "^3.5.11",
+                "@react-spectrum/utils": "^3.12.1",
+                "@react-stately/collections": "^3.12.1",
+                "@react-stately/layout": "^4.1.1",
+                "@react-stately/list": "^3.11.2",
+                "@react-stately/virtualizer": "^4.2.1",
+                "@react-types/grid": "^3.2.11",
+                "@react-types/shared": "^3.27.0",
+                "@spectrum-icons/ui": "^3.6.12",
                 "@swc/helpers": "^0.5.0",
                 "react-transition-group": "^4.4.5"
             },
@@ -7993,28 +7998,28 @@
             }
         },
         "node_modules/@react-spectrum/listbox": {
-            "version": "3.14.0",
-            "resolved": "https://registry.npmjs.org/@react-spectrum/listbox/-/listbox-3.14.0.tgz",
-            "integrity": "sha512-1JT8n/8/sL8YqyKa0mPAbT143H0km93V3V+c7RhhKtDOO0UoHuPXGZS0XN014TfOOOJm9sPQNPF9mTpuptj6AA==",
-            "license": "Apache-2.0",
-            "dependencies": {
-                "@react-aria/focus": "^3.19.0",
-                "@react-aria/i18n": "^3.12.4",
-                "@react-aria/interactions": "^3.22.5",
-                "@react-aria/listbox": "^3.13.6",
-                "@react-aria/utils": "^3.26.0",
-                "@react-aria/virtualizer": "^4.1.0",
-                "@react-spectrum/layout": "^3.6.10",
-                "@react-spectrum/progress": "^3.7.11",
-                "@react-spectrum/text": "^3.5.10",
-                "@react-spectrum/utils": "^3.12.0",
-                "@react-stately/collections": "^3.12.0",
-                "@react-stately/layout": "^4.1.0",
-                "@react-stately/list": "^3.11.1",
-                "@react-stately/virtualizer": "^4.2.0",
-                "@react-types/listbox": "^3.5.3",
-                "@react-types/shared": "^3.26.0",
-                "@spectrum-icons/ui": "^3.6.11",
+            "version": "3.14.1",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/listbox/-/listbox-3.14.1.tgz",
+            "integrity": "sha512-6F5NaFkHGwxJhaJsObPbCt8p00Ku8JWvB2QA7Jwt15GLTPGnel6m2Uj+zey+qAwyc47i0W2Eeki5lWEv+g52SQ==",
+            "license": "Apache-2.0",
+            "dependencies": {
+                "@react-aria/focus": "^3.19.1",
+                "@react-aria/i18n": "^3.12.5",
+                "@react-aria/interactions": "^3.23.0",
+                "@react-aria/listbox": "^3.14.0",
+                "@react-aria/utils": "^3.27.0",
+                "@react-aria/virtualizer": "^4.1.1",
+                "@react-spectrum/layout": "^3.6.11",
+                "@react-spectrum/progress": "^3.7.12",
+                "@react-spectrum/text": "^3.5.11",
+                "@react-spectrum/utils": "^3.12.1",
+                "@react-stately/collections": "^3.12.1",
+                "@react-stately/layout": "^4.1.1",
+                "@react-stately/list": "^3.11.2",
+                "@react-stately/virtualizer": "^4.2.1",
+                "@react-types/listbox": "^3.5.4",
+                "@react-types/shared": "^3.27.0",
+                "@spectrum-icons/ui": "^3.6.12",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -8024,32 +8029,32 @@
             }
         },
         "node_modules/@react-spectrum/menu": {
-            "version": "3.21.0",
-            "resolved": "https://registry.npmjs.org/@react-spectrum/menu/-/menu-3.21.0.tgz",
-            "integrity": "sha512-5FHHBtkhuOTYECQHTjay5/LwLZWhtnHAQ/8s5S8xgJqGeo304GKlVQnOYU73HzFPIN39JucxLzj1ommL/pVv3Q==",
-            "license": "Apache-2.0",
-            "dependencies": {
-                "@react-aria/focus": "^3.19.0",
-                "@react-aria/i18n": "^3.12.4",
-                "@react-aria/interactions": "^3.22.5",
-                "@react-aria/menu": "^3.16.0",
-                "@react-aria/overlays": "^3.24.0",
-                "@react-aria/separator": "^3.4.4",
-                "@react-aria/utils": "^3.26.0",
-                "@react-spectrum/button": "^3.16.9",
-                "@react-spectrum/layout": "^3.6.10",
-                "@react-spectrum/overlays": "^5.7.0",
-                "@react-spectrum/text": "^3.5.10",
-                "@react-spectrum/utils": "^3.12.0",
-                "@react-stately/collections": "^3.12.0",
-                "@react-stately/menu": "^3.9.0",
-                "@react-stately/overlays": "^3.6.12",
-                "@react-stately/tree": "^3.8.6",
-                "@react-types/menu": "^3.9.13",
-                "@react-types/overlays": "^3.8.11",
-                "@react-types/shared": "^3.26.0",
-                "@spectrum-icons/ui": "^3.6.11",
-                "@spectrum-icons/workflow": "^4.2.16",
+            "version": "3.21.1",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/menu/-/menu-3.21.1.tgz",
+            "integrity": "sha512-D64qiCLOr2vib3tgnHAWgaOB47kICfu34aIN5EOOTniElQOEsd/a22D9dgv4v99FTycdtSfZTvWEHMipTGuXQg==",
+            "license": "Apache-2.0",
+            "dependencies": {
+                "@react-aria/focus": "^3.19.1",
+                "@react-aria/i18n": "^3.12.5",
+                "@react-aria/interactions": "^3.23.0",
+                "@react-aria/menu": "^3.17.0",
+                "@react-aria/overlays": "^3.25.0",
+                "@react-aria/separator": "^3.4.5",
+                "@react-aria/utils": "^3.27.0",
+                "@react-spectrum/button": "^3.16.10",
+                "@react-spectrum/layout": "^3.6.11",
+                "@react-spectrum/overlays": "^5.7.1",
+                "@react-spectrum/text": "^3.5.11",
+                "@react-spectrum/utils": "^3.12.1",
+                "@react-stately/collections": "^3.12.1",
+                "@react-stately/menu": "^3.9.1",
+                "@react-stately/overlays": "^3.6.13",
+                "@react-stately/tree": "^3.8.7",
+                "@react-types/menu": "^3.9.14",
+                "@react-types/overlays": "^3.8.12",
+                "@react-types/shared": "^3.27.0",
+                "@spectrum-icons/ui": "^3.6.12",
+                "@spectrum-icons/workflow": "^4.2.17",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -8059,45 +8064,46 @@
             }
         },
         "node_modules/@react-spectrum/meter": {
-            "version": "3.5.5",
-            "resolved": "https://registry.npmjs.org/@react-spectrum/meter/-/meter-3.5.5.tgz",
-            "integrity": "sha512-FWctQTukfclzxBLz7cvpTmC28soqEQ/7vHAyWuyEJiuNBrfuGqpghvzMlNtWR7oTp0wEtdxX46W7WtcpA6V0ZQ==",
+            "version": "3.5.6",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/meter/-/meter-3.5.6.tgz",
+            "integrity": "sha512-LVaE58W8XRzpP4VnWnOf5hkOa+EUGELziJyC50EQFSNUpgWFSKbbdrzuxEwR6kUUsbRzQ4V6Z+cyzTaQUa7A3Q==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-aria/meter": "^3.4.18",
-                "@react-spectrum/progress": "^3.7.11",
-                "@react-spectrum/utils": "^3.12.0",
-                "@react-types/meter": "^3.4.5",
-                "@react-types/shared": "^3.26.0",
+                "@react-aria/meter": "^3.4.19",
+                "@react-spectrum/progress": "^3.7.12",
+                "@react-spectrum/utils": "^3.12.1",
+                "@react-types/meter": "^3.4.6",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
                 "@react-spectrum/provider": "^3.0.0",
-                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+                "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-spectrum/numberfield": {
-            "version": "3.9.8",
-            "resolved": "https://registry.npmjs.org/@react-spectrum/numberfield/-/numberfield-3.9.8.tgz",
-            "integrity": "sha512-u/ZF+cvzmgvUvFCyjImZ7spW/OWbdkCwaVxht8joPkJMeIZxMn9FZ+NgdnhpSy7HdEFQ6ujMq12IcgfBD3J2RQ==",
-            "license": "Apache-2.0",
-            "dependencies": {
-                "@react-aria/button": "^3.11.0",
-                "@react-aria/focus": "^3.19.0",
-                "@react-aria/i18n": "^3.12.4",
-                "@react-aria/interactions": "^3.22.5",
-                "@react-aria/numberfield": "^3.11.9",
-                "@react-aria/utils": "^3.26.0",
-                "@react-spectrum/form": "^3.7.10",
-                "@react-spectrum/label": "^3.16.10",
-                "@react-spectrum/textfield": "^3.12.7",
-                "@react-spectrum/utils": "^3.12.0",
-                "@react-stately/numberfield": "^3.9.8",
-                "@react-types/button": "^3.10.1",
-                "@react-types/numberfield": "^3.8.7",
-                "@react-types/shared": "^3.26.0",
-                "@spectrum-icons/ui": "^3.6.11",
-                "@spectrum-icons/workflow": "^4.2.16",
+            "version": "3.9.9",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/numberfield/-/numberfield-3.9.9.tgz",
+            "integrity": "sha512-4eNn4MMs1T3VKQG8TKkB5qTILJwEhCNhk9v/KKGc4UBO1y5/EyIY68h3zqk9b7d8MyTZ8OAUyKK+mL8gRL012g==",
+            "license": "Apache-2.0",
+            "dependencies": {
+                "@react-aria/button": "^3.11.1",
+                "@react-aria/focus": "^3.19.1",
+                "@react-aria/i18n": "^3.12.5",
+                "@react-aria/interactions": "^3.23.0",
+                "@react-aria/numberfield": "^3.11.10",
+                "@react-aria/utils": "^3.27.0",
+                "@react-spectrum/form": "^3.7.11",
+                "@react-spectrum/label": "^3.16.11",
+                "@react-spectrum/textfield": "^3.12.8",
+                "@react-spectrum/utils": "^3.12.1",
+                "@react-stately/numberfield": "^3.9.9",
+                "@react-types/button": "^3.10.2",
+                "@react-types/numberfield": "^3.8.8",
+                "@react-types/shared": "^3.27.0",
+                "@spectrum-icons/ui": "^3.6.12",
+                "@spectrum-icons/workflow": "^4.2.17",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -8107,18 +8113,18 @@
             }
         },
         "node_modules/@react-spectrum/overlays": {
-            "version": "5.7.0",
-            "resolved": "https://registry.npmjs.org/@react-spectrum/overlays/-/overlays-5.7.0.tgz",
-            "integrity": "sha512-a9CzED5cFT0UhDjLrYAL/rFrCjZJfUyT1vfw1aaSYRAnXlI6utm15wCir+QBpHIU8avGazM+xbYtQ7akyacqmg==",
-            "license": "Apache-2.0",
-            "dependencies": {
-                "@react-aria/interactions": "^3.22.5",
-                "@react-aria/overlays": "^3.24.0",
-                "@react-aria/utils": "^3.26.0",
-                "@react-spectrum/utils": "^3.12.0",
-                "@react-stately/overlays": "^3.6.12",
-                "@react-types/overlays": "^3.8.11",
-                "@react-types/shared": "^3.26.0",
+            "version": "5.7.1",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/overlays/-/overlays-5.7.1.tgz",
+            "integrity": "sha512-A5zvmUTOl9k1Cl+wBIO2yL+eaJ7sLKpd67QxAworkGMhHibL7y2e4cwRc9RcCJPL+v1Dah6q3Vsfih3bZXYNhA==",
+            "license": "Apache-2.0",
+            "dependencies": {
+                "@react-aria/interactions": "^3.23.0",
+                "@react-aria/overlays": "^3.25.0",
+                "@react-aria/utils": "^3.27.0",
+                "@react-spectrum/utils": "^3.12.1",
+                "@react-stately/overlays": "^3.6.13",
+                "@react-types/overlays": "^3.8.12",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0",
                 "react-transition-group": "^4.4.5"
             },
@@ -8129,28 +8135,28 @@
             }
         },
         "node_modules/@react-spectrum/picker": {
-            "version": "3.15.4",
-            "resolved": "https://registry.npmjs.org/@react-spectrum/picker/-/picker-3.15.4.tgz",
-            "integrity": "sha512-Vcdan9F0LHN9/XhaxetQRi8CWMannwgLY7pv0e0mBS4ZC15MrA3NsJ3j7rGHpgdgVN9KBNYIPosASlU/Zv2SRA==",
-            "license": "Apache-2.0",
-            "dependencies": {
-                "@react-aria/i18n": "^3.12.4",
-                "@react-aria/interactions": "^3.22.5",
-                "@react-aria/select": "^3.15.0",
-                "@react-aria/utils": "^3.26.0",
-                "@react-spectrum/button": "^3.16.9",
-                "@react-spectrum/form": "^3.7.10",
-                "@react-spectrum/label": "^3.16.10",
-                "@react-spectrum/listbox": "^3.14.0",
-                "@react-spectrum/overlays": "^5.7.0",
-                "@react-spectrum/progress": "^3.7.11",
-                "@react-spectrum/text": "^3.5.10",
-                "@react-spectrum/utils": "^3.12.0",
-                "@react-stately/collections": "^3.12.0",
-                "@react-stately/select": "^3.6.9",
-                "@react-types/select": "^3.9.8",
-                "@react-types/shared": "^3.26.0",
-                "@spectrum-icons/ui": "^3.6.11",
+            "version": "3.15.5",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/picker/-/picker-3.15.5.tgz",
+            "integrity": "sha512-FqWhFe8tT6p8r2lfRFEskZ8mO+SnXoD8HC+QqRhpQIG+rZNYErrlvw1mUw8gPsfnlqSZ2LsyAjDffsCHw6hYZQ==",
+            "license": "Apache-2.0",
+            "dependencies": {
+                "@react-aria/i18n": "^3.12.5",
+                "@react-aria/interactions": "^3.23.0",
+                "@react-aria/select": "^3.15.1",
+                "@react-aria/utils": "^3.27.0",
+                "@react-spectrum/button": "^3.16.10",
+                "@react-spectrum/form": "^3.7.11",
+                "@react-spectrum/label": "^3.16.11",
+                "@react-spectrum/listbox": "^3.14.1",
+                "@react-spectrum/overlays": "^5.7.1",
+                "@react-spectrum/progress": "^3.7.12",
+                "@react-spectrum/text": "^3.5.11",
+                "@react-spectrum/utils": "^3.12.1",
+                "@react-stately/collections": "^3.12.1",
+                "@react-stately/select": "^3.6.10",
+                "@react-types/select": "^3.9.9",
+                "@react-types/shared": "^3.27.0",
+                "@spectrum-icons/ui": "^3.6.12",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -8160,35 +8166,36 @@
             }
         },
         "node_modules/@react-spectrum/progress": {
-            "version": "3.7.11",
-            "resolved": "https://registry.npmjs.org/@react-spectrum/progress/-/progress-3.7.11.tgz",
-            "integrity": "sha512-vszMcO2OlPu5207hndIY1z1fn28/NIcyUcVs/JA0+NGdfnGfSaHfI1Z2BcNUimAT46Bk4kmJOwoFfQJq3nZO1w==",
+            "version": "3.7.12",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/progress/-/progress-3.7.12.tgz",
+            "integrity": "sha512-auTLJvEKWUzY4u7QHrWGY1bKYjM1rVyK/zH6mE/W5XXKNOUurtMwMZv0W0LdQbJpTrzRvQKGW8mXwQ+O/flWHg==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-aria/progress": "^3.4.18",
-                "@react-aria/utils": "^3.26.0",
-                "@react-spectrum/utils": "^3.12.0",
-                "@react-types/progress": "^3.5.8",
-                "@react-types/shared": "^3.26.0",
+                "@react-aria/progress": "^3.4.19",
+                "@react-aria/utils": "^3.27.0",
+                "@react-spectrum/utils": "^3.12.1",
+                "@react-types/progress": "^3.5.9",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
                 "@react-spectrum/provider": "^3.0.0",
-                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+                "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-spectrum/provider": {
-            "version": "3.10.0",
-            "resolved": "https://registry.npmjs.org/@react-spectrum/provider/-/provider-3.10.0.tgz",
-            "integrity": "sha512-NF3Uz0jaJG9Abfm3IppEroM10o6Fs8L2PgZCwhllWjeMQeIAix6lrzey+I1zRYjMZ8E3+hFdPlsBkUr5yXm31Q==",
+            "version": "3.10.1",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/provider/-/provider-3.10.1.tgz",
+            "integrity": "sha512-k5gQr9TOSA/amSlaBlSrsdoDtrSg+FGwVXihvjR6E6dPLCO2anwqPXtn+Gp15jFwqwpqFLJG6AdAUUwAtzcfBA==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-aria/i18n": "^3.12.4",
-                "@react-aria/overlays": "^3.24.0",
-                "@react-aria/utils": "^3.26.0",
-                "@react-spectrum/utils": "^3.12.0",
-                "@react-types/provider": "^3.8.5",
-                "@react-types/shared": "^3.26.0",
+                "@react-aria/i18n": "^3.12.5",
+                "@react-aria/overlays": "^3.25.0",
+                "@react-aria/utils": "^3.27.0",
+                "@react-spectrum/utils": "^3.12.1",
+                "@react-types/provider": "^3.8.6",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0",
                 "clsx": "^2.0.0"
             },
@@ -8198,102 +8205,106 @@
             }
         },
         "node_modules/@react-spectrum/radio": {
-            "version": "3.7.11",
-            "resolved": "https://registry.npmjs.org/@react-spectrum/radio/-/radio-3.7.11.tgz",
-            "integrity": "sha512-OsetEk7+vfEqcYCKj3AJb6SpZ4PGUtSVU6ocIjZjQEhP4LAyup7dSqv5ZdEDoTX+y83lHWBcoOqAUfhsASNAcA==",
-            "license": "Apache-2.0",
-            "dependencies": {
-                "@react-aria/focus": "^3.19.0",
-                "@react-aria/interactions": "^3.22.5",
-                "@react-aria/radio": "^3.10.10",
-                "@react-spectrum/form": "^3.7.10",
-                "@react-spectrum/label": "^3.16.10",
-                "@react-spectrum/utils": "^3.12.0",
-                "@react-stately/radio": "^3.10.9",
-                "@react-types/radio": "^3.8.5",
-                "@react-types/shared": "^3.26.0",
+            "version": "3.7.12",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/radio/-/radio-3.7.12.tgz",
+            "integrity": "sha512-Ak9FWbtIaTeILIqUAxeHnu8zkeQ81Q6dQAZ9VPd1+1MnCntdypqtlSbpN1oNEZ8AquWa6lu4Z3tLKTaH/wiavg==",
+            "license": "Apache-2.0",
+            "dependencies": {
+                "@react-aria/focus": "^3.19.1",
+                "@react-aria/interactions": "^3.23.0",
+                "@react-aria/radio": "^3.10.11",
+                "@react-spectrum/form": "^3.7.11",
+                "@react-spectrum/label": "^3.16.11",
+                "@react-spectrum/utils": "^3.12.1",
+                "@react-stately/radio": "^3.10.10",
+                "@react-types/radio": "^3.8.6",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
                 "@react-spectrum/provider": "^3.0.0",
-                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+                "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-spectrum/searchfield": {
-            "version": "3.8.11",
-            "resolved": "https://registry.npmjs.org/@react-spectrum/searchfield/-/searchfield-3.8.11.tgz",
-            "integrity": "sha512-IXExrW9Ze/Jmq+MnHB0kwwvD9nuL+vrXOggozCtmCChPveY98nlXRZpmcxq+uDf3/RQZuU7TFkmHmbK0LD7QKQ==",
-            "license": "Apache-2.0",
-            "dependencies": {
-                "@react-aria/searchfield": "^3.7.11",
-                "@react-spectrum/button": "^3.16.9",
-                "@react-spectrum/form": "^3.7.10",
-                "@react-spectrum/textfield": "^3.12.7",
-                "@react-spectrum/utils": "^3.12.0",
-                "@react-stately/searchfield": "^3.5.8",
-                "@react-types/searchfield": "^3.5.10",
-                "@react-types/textfield": "^3.10.0",
-                "@spectrum-icons/ui": "^3.6.11",
+            "version": "3.8.12",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/searchfield/-/searchfield-3.8.12.tgz",
+            "integrity": "sha512-YpVzBeShsebQ/KhxMay7wqMzR6GJFM1rxrVf2ta/8f7vXA5FgOMkJgRuR733pvvU6grXXhOvDc3c71g0d70d9w==",
+            "license": "Apache-2.0",
+            "dependencies": {
+                "@react-aria/searchfield": "^3.8.0",
+                "@react-spectrum/button": "^3.16.10",
+                "@react-spectrum/form": "^3.7.11",
+                "@react-spectrum/textfield": "^3.12.8",
+                "@react-spectrum/utils": "^3.12.1",
+                "@react-stately/searchfield": "^3.5.9",
+                "@react-types/searchfield": "^3.5.11",
+                "@react-types/textfield": "^3.11.0",
+                "@spectrum-icons/ui": "^3.6.12",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
                 "@react-spectrum/provider": "^3.0.0",
-                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+                "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-spectrum/slider": {
-            "version": "3.7.0",
-            "resolved": "https://registry.npmjs.org/@react-spectrum/slider/-/slider-3.7.0.tgz",
-            "integrity": "sha512-pnrlbjN+Nk/Fss0fDp13hkhCWO6JFZsnjGO6BnKTv1jj3KWn6+zvbjfNVHu+YRI+6mgYPFM3p715pXgdNxHR8w==",
-            "license": "Apache-2.0",
-            "dependencies": {
-                "@react-aria/focus": "^3.19.0",
-                "@react-aria/i18n": "^3.12.4",
-                "@react-aria/interactions": "^3.22.5",
-                "@react-aria/slider": "^3.7.14",
-                "@react-aria/utils": "^3.26.0",
-                "@react-aria/visually-hidden": "^3.8.18",
-                "@react-spectrum/utils": "^3.12.0",
-                "@react-stately/slider": "^3.6.0",
-                "@react-types/shared": "^3.26.0",
-                "@react-types/slider": "^3.7.7",
+            "version": "3.7.1",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/slider/-/slider-3.7.1.tgz",
+            "integrity": "sha512-koXey5lYtSr4V/+GV1/INiSYdrrKHD08VRtBhIZ0vlQVCYmZNdP7frPRl+O8EIqQ2zyGuF0XslnN24YQXL0ucQ==",
+            "license": "Apache-2.0",
+            "dependencies": {
+                "@react-aria/focus": "^3.19.1",
+                "@react-aria/i18n": "^3.12.5",
+                "@react-aria/interactions": "^3.23.0",
+                "@react-aria/slider": "^3.7.15",
+                "@react-aria/utils": "^3.27.0",
+                "@react-aria/visually-hidden": "^3.8.19",
+                "@react-spectrum/utils": "^3.12.1",
+                "@react-stately/slider": "^3.6.1",
+                "@react-types/shared": "^3.27.0",
+                "@react-types/slider": "^3.7.8",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
                 "@react-spectrum/provider": "^3.0.0",
-                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+                "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-spectrum/statuslight": {
-            "version": "3.5.17",
-            "resolved": "https://registry.npmjs.org/@react-spectrum/statuslight/-/statuslight-3.5.17.tgz",
-            "integrity": "sha512-gwpdh0Td9eMbqBnIP+0ARq/2Kj0xSiRzDshQtk7AMPT8u0MVswCA/gzHnj94e40cEb3m+Xn/Mh/DkXb3EWNebg==",
+            "version": "3.5.18",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/statuslight/-/statuslight-3.5.18.tgz",
+            "integrity": "sha512-oZIGxzBFFUFYbSyXmmORnecIKPD6HFxbnG1jACFa8UBKEo1YD8k6nOuycGV1p7FqswxIEbOSJfjtnRinf1rvsQ==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-aria/utils": "^3.26.0",
-                "@react-spectrum/utils": "^3.12.0",
-                "@react-types/shared": "^3.26.0",
-                "@react-types/statuslight": "^3.3.13",
+                "@react-aria/utils": "^3.27.0",
+                "@react-spectrum/utils": "^3.12.1",
+                "@react-types/shared": "^3.27.0",
+                "@react-types/statuslight": "^3.3.14",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
                 "@react-spectrum/provider": "^3.0.0",
-                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+                "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-spectrum/switch": {
-            "version": "3.5.10",
-            "resolved": "https://registry.npmjs.org/@react-spectrum/switch/-/switch-3.5.10.tgz",
-            "integrity": "sha512-xIL+Us/3GGDpt8Y6rnWW79BxPUq+pMK02ZSd7Mz7x1wAfQXvWn4fE8SDBtuZtCxPcrBSyxhR6hdTXEid75UpeQ==",
-            "license": "Apache-2.0",
-            "dependencies": {
-                "@react-aria/focus": "^3.19.0",
-                "@react-aria/interactions": "^3.22.5",
-                "@react-aria/switch": "^3.6.10",
-                "@react-spectrum/utils": "^3.12.0",
-                "@react-stately/toggle": "^3.8.0",
-                "@react-types/shared": "^3.26.0",
-                "@react-types/switch": "^3.5.7",
+            "version": "3.5.11",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/switch/-/switch-3.5.11.tgz",
+            "integrity": "sha512-FI/C8QBhnsdCnArUtrWNMkszHgBnQYtWLJ/nknLkNJU6EhU5ZDEg2WgQSK5UUM33P2Hg7QJES2Kqh91abliiSA==",
+            "license": "Apache-2.0",
+            "dependencies": {
+                "@react-aria/focus": "^3.19.1",
+                "@react-aria/interactions": "^3.23.0",
+                "@react-aria/switch": "^3.6.11",
+                "@react-spectrum/utils": "^3.12.1",
+                "@react-stately/toggle": "^3.8.1",
+                "@react-types/shared": "^3.27.0",
+                "@react-types/switch": "^3.5.8",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -8302,36 +8313,36 @@
             }
         },
         "node_modules/@react-spectrum/table": {
-            "version": "3.15.0",
-            "resolved": "https://registry.npmjs.org/@react-spectrum/table/-/table-3.15.0.tgz",
-            "integrity": "sha512-v1v24REhM02u7X3vHNv91k9JrWrZd4DlRQI/sRBj0uNO+l0/MLc+MIxB8yjaZKIrm55VEvY6vLo6dHNcZPWMOQ==",
-            "license": "Apache-2.0",
-            "dependencies": {
-                "@react-aria/button": "^3.11.0",
-                "@react-aria/focus": "^3.19.0",
-                "@react-aria/i18n": "^3.12.4",
-                "@react-aria/interactions": "^3.22.5",
-                "@react-aria/overlays": "^3.24.0",
-                "@react-aria/selection": "^3.21.0",
-                "@react-aria/table": "^3.16.0",
-                "@react-aria/utils": "^3.26.0",
-                "@react-aria/virtualizer": "^4.1.0",
-                "@react-aria/visually-hidden": "^3.8.18",
-                "@react-spectrum/checkbox": "^3.9.11",
-                "@react-spectrum/dnd": "^3.5.0",
-                "@react-spectrum/layout": "^3.6.10",
-                "@react-spectrum/menu": "^3.21.0",
-                "@react-spectrum/progress": "^3.7.11",
-                "@react-spectrum/tooltip": "^3.7.0",
-                "@react-spectrum/utils": "^3.12.0",
+            "version": "3.15.1",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/table/-/table-3.15.1.tgz",
+            "integrity": "sha512-VCFb43uxmLhCNnoKrlqo9WiMpe/Z4kdfv3XPcnFzWcOxZ45i+eWDOAbv6akUUdwPOAMnD3SazVBNOYbG8vdAAA==",
+            "license": "Apache-2.0",
+            "dependencies": {
+                "@react-aria/button": "^3.11.1",
+                "@react-aria/focus": "^3.19.1",
+                "@react-aria/i18n": "^3.12.5",
+                "@react-aria/interactions": "^3.23.0",
+                "@react-aria/overlays": "^3.25.0",
+                "@react-aria/selection": "^3.22.0",
+                "@react-aria/table": "^3.16.1",
+                "@react-aria/utils": "^3.27.0",
+                "@react-aria/virtualizer": "^4.1.1",
+                "@react-aria/visually-hidden": "^3.8.19",
+                "@react-spectrum/checkbox": "^3.9.12",
+                "@react-spectrum/dnd": "^3.5.1",
+                "@react-spectrum/layout": "^3.6.11",
+                "@react-spectrum/menu": "^3.21.1",
+                "@react-spectrum/progress": "^3.7.12",
+                "@react-spectrum/tooltip": "^3.7.1",
+                "@react-spectrum/utils": "^3.12.1",
                 "@react-stately/flags": "^3.0.5",
-                "@react-stately/layout": "^4.1.0",
-                "@react-stately/table": "^3.13.0",
-                "@react-stately/virtualizer": "^4.2.0",
-                "@react-types/grid": "^3.2.10",
-                "@react-types/shared": "^3.26.0",
-                "@react-types/table": "^3.10.3",
-                "@spectrum-icons/ui": "^3.6.11",
+                "@react-stately/layout": "^4.1.1",
+                "@react-stately/table": "^3.13.1",
+                "@react-stately/virtualizer": "^4.2.1",
+                "@react-types/grid": "^3.2.11",
+                "@react-types/shared": "^3.27.0",
+                "@react-types/table": "^3.10.4",
+                "@spectrum-icons/ui": "^3.6.12",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -8341,25 +8352,25 @@
             }
         },
         "node_modules/@react-spectrum/tabs": {
-            "version": "3.8.15",
-            "resolved": "https://registry.npmjs.org/@react-spectrum/tabs/-/tabs-3.8.15.tgz",
-            "integrity": "sha512-6a/sBzuhl8mfrIraU2oo4yQJ0HWz6AlEys4MLPHopdaAEI5QNdl7upXVgjzAi0M46HicjY3nT7T1CJeQP3e9nQ==",
-            "license": "Apache-2.0",
-            "dependencies": {
-                "@react-aria/focus": "^3.19.0",
-                "@react-aria/i18n": "^3.12.4",
-                "@react-aria/interactions": "^3.22.5",
-                "@react-aria/tabs": "^3.9.8",
-                "@react-aria/utils": "^3.26.0",
-                "@react-spectrum/picker": "^3.15.4",
-                "@react-spectrum/text": "^3.5.10",
-                "@react-spectrum/utils": "^3.12.0",
-                "@react-stately/collections": "^3.12.0",
-                "@react-stately/list": "^3.11.1",
-                "@react-stately/tabs": "^3.7.0",
-                "@react-types/select": "^3.9.8",
-                "@react-types/shared": "^3.26.0",
-                "@react-types/tabs": "^3.3.11",
+            "version": "3.8.16",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/tabs/-/tabs-3.8.16.tgz",
+            "integrity": "sha512-YW+qDrK1jxK16TTpXsQvM+ODhjhiUNKwJqouWvlMCBiRpFoUXlzinujrwATeifSeUeCLS/yPaZF1q7wKr63HfQ==",
+            "license": "Apache-2.0",
+            "dependencies": {
+                "@react-aria/focus": "^3.19.1",
+                "@react-aria/i18n": "^3.12.5",
+                "@react-aria/interactions": "^3.23.0",
+                "@react-aria/tabs": "^3.9.9",
+                "@react-aria/utils": "^3.27.0",
+                "@react-spectrum/picker": "^3.15.5",
+                "@react-spectrum/text": "^3.5.11",
+                "@react-spectrum/utils": "^3.12.1",
+                "@react-stately/collections": "^3.12.1",
+                "@react-stately/list": "^3.11.2",
+                "@react-stately/tabs": "^3.7.1",
+                "@react-types/select": "^3.9.9",
+                "@react-types/shared": "^3.27.0",
+                "@react-types/tabs": "^3.3.12",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -8369,25 +8380,25 @@
             }
         },
         "node_modules/@react-spectrum/tag": {
-            "version": "3.2.11",
-            "resolved": "https://registry.npmjs.org/@react-spectrum/tag/-/tag-3.2.11.tgz",
-            "integrity": "sha512-WF6ybH3GJMkUy1xpfLjNimedd0tXTzsX8fGIZ6f22d/Z5EJLej9UlFOjzJ3Vs3d1QU7gOGIB28dBLXR0ra6clg==",
-            "license": "Apache-2.0",
-            "dependencies": {
-                "@react-aria/focus": "^3.19.0",
-                "@react-aria/i18n": "^3.12.4",
-                "@react-aria/interactions": "^3.22.5",
-                "@react-aria/selection": "^3.21.0",
-                "@react-aria/tag": "^3.4.8",
-                "@react-aria/utils": "^3.26.0",
-                "@react-spectrum/button": "^3.16.9",
-                "@react-spectrum/form": "^3.7.10",
-                "@react-spectrum/label": "^3.16.10",
-                "@react-spectrum/text": "^3.5.10",
-                "@react-spectrum/utils": "^3.12.0",
-                "@react-stately/collections": "^3.12.0",
-                "@react-stately/list": "^3.11.1",
-                "@react-types/shared": "^3.26.0",
+            "version": "3.2.12",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/tag/-/tag-3.2.12.tgz",
+            "integrity": "sha512-r5eMBdnnIz1oI0fuLr1z3HV5RoMyjteLjYNqeDPAcIlBWf46YGU3xYcMlMfeqEplhisqeIgNiNGv5tVr1pCZzQ==",
+            "license": "Apache-2.0",
+            "dependencies": {
+                "@react-aria/focus": "^3.19.1",
+                "@react-aria/i18n": "^3.12.5",
+                "@react-aria/interactions": "^3.23.0",
+                "@react-aria/selection": "^3.22.0",
+                "@react-aria/tag": "^3.4.9",
+                "@react-aria/utils": "^3.27.0",
+                "@react-spectrum/button": "^3.16.10",
+                "@react-spectrum/form": "^3.7.11",
+                "@react-spectrum/label": "^3.16.11",
+                "@react-spectrum/text": "^3.5.11",
+                "@react-spectrum/utils": "^3.12.1",
+                "@react-stately/collections": "^3.12.1",
+                "@react-stately/list": "^3.11.2",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -8397,17 +8408,17 @@
             }
         },
         "node_modules/@react-spectrum/text": {
-            "version": "3.5.10",
-            "resolved": "https://registry.npmjs.org/@react-spectrum/text/-/text-3.5.10.tgz",
-            "integrity": "sha512-T4ko4xgLFWxdBqNLpjCW50z6FQ3SdoVtQZVI6Jmf0ZJisZwEb4HgzKhUcI5bbofkphNKqfgu+ODC/284fh+nkA==",
+            "version": "3.5.11",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/text/-/text-3.5.11.tgz",
+            "integrity": "sha512-tZbEREhzZlPkzipL+/EGiWZ8xSgNdb9JmTh3PXg5B6AJu6cStmyPQ+ybdwmDRkkemVOTPKnoDTOs3K462yDVHQ==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-aria/utils": "^3.26.0",
-                "@react-spectrum/utils": "^3.12.0",
-                "@react-types/shared": "^3.26.0",
-                "@react-types/text": "^3.3.13",
+                "@react-aria/utils": "^3.27.0",
+                "@react-spectrum/utils": "^3.12.1",
+                "@react-types/shared": "^3.27.0",
+                "@react-types/text": "^3.3.14",
                 "@swc/helpers": "^0.5.0",
-                "react-aria-components": "^1.5.0"
+                "react-aria-components": "^1.6.0"
             },
             "peerDependencies": {
                 "@react-spectrum/provider": "^3.0.0",
@@ -8415,36 +8426,37 @@
             }
         },
         "node_modules/@react-spectrum/textfield": {
-            "version": "3.12.7",
-            "resolved": "https://registry.npmjs.org/@react-spectrum/textfield/-/textfield-3.12.7.tgz",
-            "integrity": "sha512-rINcfLxyyGhL2FVb/1U7IOzfVsvpEclH/qYF08WatAuzxnyqDrC+qSMuG/MsHm/EqrNFFwm7oYKqcBTbya1ZGQ==",
-            "license": "Apache-2.0",
-            "dependencies": {
-                "@react-aria/focus": "^3.19.0",
-                "@react-aria/interactions": "^3.22.5",
-                "@react-aria/textfield": "^3.15.0",
-                "@react-aria/utils": "^3.26.0",
-                "@react-spectrum/form": "^3.7.10",
-                "@react-spectrum/label": "^3.16.10",
-                "@react-spectrum/utils": "^3.12.0",
+            "version": "3.12.8",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/textfield/-/textfield-3.12.8.tgz",
+            "integrity": "sha512-h/z1OOJQiGVh7SDld23VfSbXkgHhcyDy2/+dJYldN24UyWh39efG7d7SeZ7E6v00l7cQjX1JM2UKRFYM/EzoLg==",
+            "license": "Apache-2.0",
+            "dependencies": {
+                "@react-aria/focus": "^3.19.1",
+                "@react-aria/interactions": "^3.23.0",
+                "@react-aria/textfield": "^3.16.0",
+                "@react-aria/utils": "^3.27.0",
+                "@react-spectrum/form": "^3.7.11",
+                "@react-spectrum/label": "^3.16.11",
+                "@react-spectrum/utils": "^3.12.1",
                 "@react-stately/utils": "^3.10.5",
-                "@react-types/shared": "^3.26.0",
-                "@react-types/textfield": "^3.10.0",
-                "@spectrum-icons/ui": "^3.6.11",
+                "@react-types/shared": "^3.27.0",
+                "@react-types/textfield": "^3.11.0",
+                "@spectrum-icons/ui": "^3.6.12",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
                 "@react-spectrum/provider": "^3.0.0",
-                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+                "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-spectrum/theme-dark": {
-            "version": "3.5.14",
-            "resolved": "https://registry.npmjs.org/@react-spectrum/theme-dark/-/theme-dark-3.5.14.tgz",
-            "integrity": "sha512-KE6ft1MhKPUtuDcA330cYf+bhHdffuhyvVxYvSyAHSbgOrWNmFU+VjBUYQ+eq3tm1ASmPDqTeBSzMjMUcdtRuw==",
+            "version": "3.5.15",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/theme-dark/-/theme-dark-3.5.15.tgz",
+            "integrity": "sha512-xUNBvt1xVNPvxDayaF7gg1R5MQIgTIv+wdm7zfNM4xJKUJb+rduw/dC9XkJpvzfGoaZlx3ROwW3cpQJ5IeV9uA==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-types/provider": "^3.8.5",
+                "@react-types/provider": "^3.8.6",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -8452,12 +8464,12 @@
             }
         },
         "node_modules/@react-spectrum/theme-default": {
-            "version": "3.5.14",
-            "resolved": "https://registry.npmjs.org/@react-spectrum/theme-default/-/theme-default-3.5.14.tgz",
-            "integrity": "sha512-aP5WWpsfwfeSEpSLhrsHroWIDUYf8S/+GqZWDcvD8ejJYHDD9P/o91FjttxOoFw0Dx7tCnPPinofIwjCj5/blg==",
+            "version": "3.5.15",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/theme-default/-/theme-default-3.5.15.tgz",
+            "integrity": "sha512-2Ra9MfWkwSyYqQX1HoP6PlAuuAE0WD3LEqdFQbR+hPJp3aT2IPDckELN1Qn/fjDCWrHcnmIaoq4Le9+3gg+BJQ==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-types/provider": "^3.8.5",
+                "@react-types/provider": "^3.8.6",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -8465,12 +8477,12 @@
             }
         },
         "node_modules/@react-spectrum/theme-light": {
-            "version": "3.4.14",
-            "resolved": "https://registry.npmjs.org/@react-spectrum/theme-light/-/theme-light-3.4.14.tgz",
-            "integrity": "sha512-3zJSgzLxFJqqhz+g6IXHA6nb3aLoHhMmrb46835PxWM6qqUWdTzvirGqg2E/jRZ/jBZOmL9U9y3hbXXvhwdLvQ==",
+            "version": "3.4.15",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/theme-light/-/theme-light-3.4.15.tgz",
+            "integrity": "sha512-tlHUVedp5PuLRmg5VC64lsrz+kaR05DQfmjxd2z75eiA7rztxSqsw8oWRdPZHWj8KUI4hLgdQ43U+B8+9Y3iOA==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-types/provider": "^3.8.5",
+                "@react-types/provider": "^3.8.6",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -8478,22 +8490,22 @@
             }
         },
         "node_modules/@react-spectrum/tooltip": {
-            "version": "3.7.0",
-            "resolved": "https://registry.npmjs.org/@react-spectrum/tooltip/-/tooltip-3.7.0.tgz",
-            "integrity": "sha512-gNRUZVIR94zPjQ/Xg5V+rVByvxebJ5RfLUfwwt1bEkEOsv1VjTHRrVXruLEh5sy8q6XT1d01e4fpF2Axqd0qoQ==",
-            "license": "Apache-2.0",
-            "dependencies": {
-                "@react-aria/focus": "^3.19.0",
-                "@react-aria/overlays": "^3.24.0",
-                "@react-aria/tooltip": "^3.7.10",
-                "@react-aria/utils": "^3.26.0",
-                "@react-spectrum/overlays": "^5.7.0",
-                "@react-spectrum/utils": "^3.12.0",
-                "@react-stately/tooltip": "^3.5.0",
-                "@react-types/overlays": "^3.8.11",
-                "@react-types/shared": "^3.26.0",
-                "@react-types/tooltip": "^3.4.13",
-                "@spectrum-icons/ui": "^3.6.11",
+            "version": "3.7.1",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/tooltip/-/tooltip-3.7.1.tgz",
+            "integrity": "sha512-NuOtRrSYnAYUGnU+MJ+8NX5bk6dR2TmMMmoac2xfVsl3kA/1FESGnyEBEfcNVEQCdiUY4iIPbJNPGeKSbGMyBg==",
+            "license": "Apache-2.0",
+            "dependencies": {
+                "@react-aria/focus": "^3.19.1",
+                "@react-aria/overlays": "^3.25.0",
+                "@react-aria/tooltip": "^3.7.11",
+                "@react-aria/utils": "^3.27.0",
+                "@react-spectrum/overlays": "^5.7.1",
+                "@react-spectrum/utils": "^3.12.1",
+                "@react-stately/tooltip": "^3.5.1",
+                "@react-types/overlays": "^3.8.12",
+                "@react-types/shared": "^3.27.0",
+                "@react-types/tooltip": "^3.4.14",
+                "@spectrum-icons/ui": "^3.6.12",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -8503,53 +8515,56 @@
             }
         },
         "node_modules/@react-spectrum/utils": {
-            "version": "3.12.0",
-            "resolved": "https://registry.npmjs.org/@react-spectrum/utils/-/utils-3.12.0.tgz",
-            "integrity": "sha512-r3IAp9h766gATsYT6B66GmjgsTuhXSrEqKuPJenkPzshBeQPrD57ai0fyj5ihtiM3BINbkCQ+AXLrAmJunOV/A==",
+            "version": "3.12.1",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/utils/-/utils-3.12.1.tgz",
+            "integrity": "sha512-YaIUoqjxbdtohE283oisGaIWV14XgeLQUBQt/FS7yMIWkdkbLiyaXq8g5BgshhnC5WaVWrS6xIeZvUnrebyF8Q==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-aria/i18n": "^3.12.4",
+                "@react-aria/i18n": "^3.12.5",
                 "@react-aria/ssr": "^3.9.7",
-                "@react-aria/utils": "^3.26.0",
-                "@react-types/shared": "^3.26.0",
+                "@react-aria/utils": "^3.27.0",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0",
                 "clsx": "^2.0.0"
             },
             "peerDependencies": {
-                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+                "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-spectrum/view": {
-            "version": "3.6.14",
-            "resolved": "https://registry.npmjs.org/@react-spectrum/view/-/view-3.6.14.tgz",
-            "integrity": "sha512-v+9nYw+w066PVOUSJkN+whwk5PRiYwM0Qprz/EyeVfURsbnxEC7lncUJZiUKfXr2Y6dRcb89W9ArUnInpBVG1Q==",
+            "version": "3.6.15",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/view/-/view-3.6.15.tgz",
+            "integrity": "sha512-hoU9r5TKBxdMdujfw2itdPPjpdpvDsHrtnCBV4uDqz1fRPYBEURvzXJklpgg/LmwPhtwhcm7AERvrniKxKTk9A==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-aria/utils": "^3.26.0",
-                "@react-spectrum/utils": "^3.12.0",
-                "@react-types/shared": "^3.26.0",
-                "@react-types/view": "^3.4.13",
+                "@react-aria/utils": "^3.27.0",
+                "@react-spectrum/utils": "^3.12.1",
+                "@react-types/shared": "^3.27.0",
+                "@react-types/view": "^3.4.14",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
                 "@react-spectrum/provider": "^3.0.0",
-                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+                "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-spectrum/well": {
-            "version": "3.4.18",
-            "resolved": "https://registry.npmjs.org/@react-spectrum/well/-/well-3.4.18.tgz",
-            "integrity": "sha512-LYs+9spuxpmT5WwTDkM3pBafvia3ddLjIohCzDKNMYDf75dC2y0UZ/ODR06S4kHgfWx0ZtybWoBssfWOgDBQrA==",
+            "version": "3.4.19",
+            "resolved": "https://registry.npmjs.org/@react-spectrum/well/-/well-3.4.19.tgz",
+            "integrity": "sha512-bPbQ/AL3ilvVLRhcHaH/VjSt683yHHs0p+tMs9EIs1KQUGeFCzpTmMEoGA5//G4KbVpbcWRyNbDIfkauu5+8Vw==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-aria/utils": "^3.26.0",
-                "@react-spectrum/utils": "^3.12.0",
-                "@react-types/shared": "^3.26.0",
-                "@react-types/well": "^3.3.13",
+                "@react-aria/utils": "^3.27.0",
+                "@react-spectrum/utils": "^3.12.1",
+                "@react-types/shared": "^3.27.0",
+                "@react-types/well": "^3.3.14",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
-                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+                "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-spring/animated": {
@@ -8624,16 +8639,29 @@
                 "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
             }
         },
+        "node_modules/@react-stately/autocomplete": {
+            "version": "3.0.0-alpha.0",
+            "resolved": "https://registry.npmjs.org/@react-stately/autocomplete/-/autocomplete-3.0.0-alpha.0.tgz",
+            "integrity": "sha512-as4si0pBcnGnggwpvemMwCLTeV0h9GS9e5eHSR3RFg14eqUHZBEzYJ0kh9oTugpsGuf1TSM/HDizo8GQk3EtPA==",
+            "license": "Apache-2.0",
+            "dependencies": {
+                "@react-stately/utils": "^3.10.4",
+                "@swc/helpers": "^0.5.0"
+            },
+            "peerDependencies": {
+                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+            }
+        },
         "node_modules/@react-stately/calendar": {
-            "version": "3.6.0",
-            "resolved": "https://registry.npmjs.org/@react-stately/calendar/-/calendar-3.6.0.tgz",
-            "integrity": "sha512-GqUtOtGnwWjtNrJud8nY/ywI4VBP5byToNVRTnxbMl+gYO1Qe/uc5NG7zjwMxhb2kqSBHZFdkF0DXVqG2Ul+BA==",
+            "version": "3.7.0",
+            "resolved": "https://registry.npmjs.org/@react-stately/calendar/-/calendar-3.7.0.tgz",
+            "integrity": "sha512-N15zKubP2S7eWfPSJjKVlmJA7YpWzrIGx52BFhwLSQAZcV+OPcMgvOs71WtB7PLwl6DUYQGsgc0B3tcHzzvdvQ==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@internationalized/date": "^3.6.0",
+                "@internationalized/date": "^3.7.0",
                 "@react-stately/utils": "^3.10.5",
-                "@react-types/calendar": "^3.5.0",
-                "@react-types/shared": "^3.26.0",
+                "@react-types/calendar": "^3.6.0",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -8641,15 +8669,15 @@
             }
         },
         "node_modules/@react-stately/checkbox": {
-            "version": "3.6.10",
-            "resolved": "https://registry.npmjs.org/@react-stately/checkbox/-/checkbox-3.6.10.tgz",
-            "integrity": "sha512-LHm7i4YI8A/RdgWAuADrnSAYIaYYpQeZqsp1a03Og0pJHAlZL0ymN3y2IFwbZueY0rnfM+yF+kWNXjJqbKrFEQ==",
+            "version": "3.6.11",
+            "resolved": "https://registry.npmjs.org/@react-stately/checkbox/-/checkbox-3.6.11.tgz",
+            "integrity": "sha512-jApdBis+Q1sXLivg+f7krcVaP/AMMMiQcVqcz5gwxlweQN+dRZ/NpL0BYaDOuGc26Mp0lcuVaET3jIZeHwtyxA==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-stately/form": "^3.1.0",
+                "@react-stately/form": "^3.1.1",
                 "@react-stately/utils": "^3.10.5",
-                "@react-types/checkbox": "^3.9.0",
-                "@react-types/shared": "^3.26.0",
+                "@react-types/checkbox": "^3.9.1",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -8657,12 +8685,12 @@
             }
         },
         "node_modules/@react-stately/collections": {
-            "version": "3.12.0",
-            "resolved": "https://registry.npmjs.org/@react-stately/collections/-/collections-3.12.0.tgz",
-            "integrity": "sha512-MfR9hwCxe5oXv4qrLUnjidwM50U35EFmInUeFf8i9mskYwWlRYS0O1/9PZ0oF1M0cKambaRHKEy98jczgb9ycA==",
+            "version": "3.12.1",
+            "resolved": "https://registry.npmjs.org/@react-stately/collections/-/collections-3.12.1.tgz",
+            "integrity": "sha512-8QmFBL7f+P64dEP4o35pYH61/lP0T/ziSdZAvNMrCqaM+fXcMfUp2yu1E63kADVX7WRDsFJWE3CVMeqirPH6Xg==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-types/shared": "^3.26.0",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -8670,20 +8698,19 @@
             }
         },
         "node_modules/@react-stately/color": {
-            "version": "3.8.1",
-            "resolved": "https://registry.npmjs.org/@react-stately/color/-/color-3.8.1.tgz",
-            "integrity": "sha512-7eN7K+KJRu+rxK351eGrzoq2cG+yipr90i5b1cUu4lioYmcH4WdsfjmM5Ku6gypbafH+kTDfflvO6hiY1NZH+A==",
+            "version": "3.8.2",
+            "resolved": "https://registry.npmjs.org/@react-stately/color/-/color-3.8.2.tgz",
+            "integrity": "sha512-GXwLmv1Eos2OwOiRsGFrXBKx8+uZh2q0qzLZEVYrWsedNhIdTm7nnpwO68nCYZPHkqhv6rhhVSlOOFmDLY++ow==",
             "license": "Apache-2.0",
             "dependencies": {
                 "@internationalized/number": "^3.6.0",
                 "@internationalized/string": "^3.2.5",
-                "@react-aria/i18n": "^3.12.4",
-                "@react-stately/form": "^3.1.0",
-                "@react-stately/numberfield": "^3.9.8",
-                "@react-stately/slider": "^3.6.0",
+                "@react-stately/form": "^3.1.1",
+                "@react-stately/numberfield": "^3.9.9",
+                "@react-stately/slider": "^3.6.1",
                 "@react-stately/utils": "^3.10.5",
-                "@react-types/color": "^3.0.1",
-                "@react-types/shared": "^3.26.0",
+                "@react-types/color": "^3.0.2",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -8691,19 +8718,19 @@
             }
         },
         "node_modules/@react-stately/combobox": {
-            "version": "3.10.1",
-            "resolved": "https://registry.npmjs.org/@react-stately/combobox/-/combobox-3.10.1.tgz",
-            "integrity": "sha512-Rso+H+ZEDGFAhpKWbnRxRR/r7YNmYVtt+Rn0eNDNIUp3bYaxIBCdCySyAtALs4I8RZXZQ9zoUznP7YeVwG3cLg==",
+            "version": "3.10.2",
+            "resolved": "https://registry.npmjs.org/@react-stately/combobox/-/combobox-3.10.2.tgz",
+            "integrity": "sha512-uT642Dool4tQBh+8UQjlJnTisrJVtg3LqmiP/HqLQ4O3pW0O+ImbG+2r6c9dUzlAnH4kEfmEwCp9dxkBkmFWsg==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-stately/collections": "^3.12.0",
-                "@react-stately/form": "^3.1.0",
-                "@react-stately/list": "^3.11.1",
-                "@react-stately/overlays": "^3.6.12",
-                "@react-stately/select": "^3.6.9",
+                "@react-stately/collections": "^3.12.1",
+                "@react-stately/form": "^3.1.1",
+                "@react-stately/list": "^3.11.2",
+                "@react-stately/overlays": "^3.6.13",
+                "@react-stately/select": "^3.6.10",
                 "@react-stately/utils": "^3.10.5",
-                "@react-types/combobox": "^3.13.1",
-                "@react-types/shared": "^3.26.0",
+                "@react-types/combobox": "^3.13.2",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -8711,12 +8738,12 @@
             }
         },
         "node_modules/@react-stately/data": {
-            "version": "3.12.0",
-            "resolved": "https://registry.npmjs.org/@react-stately/data/-/data-3.12.0.tgz",
-            "integrity": "sha512-6PiW2oA56lcH1CVjDcajutzsv91w/PER8K61/OGxtNFFUWaIZH80RWmK4kjU/Lf0vygzXCxout3kEb388lUk0w==",
+            "version": "3.12.1",
+            "resolved": "https://registry.npmjs.org/@react-stately/data/-/data-3.12.1.tgz",
+            "integrity": "sha512-/Nc8X1FmrJ53QU4rN/1i1JtNir4iqo+39Xn5ZOJ74Nng7T+xVVuEuWSo+OEGaycCJf2eZRsomauPxUnnZgCM1A==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-types/shared": "^3.26.0",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -8724,18 +8751,18 @@
             }
         },
         "node_modules/@react-stately/datepicker": {
-            "version": "3.11.0",
-            "resolved": "https://registry.npmjs.org/@react-stately/datepicker/-/datepicker-3.11.0.tgz",
-            "integrity": "sha512-d9MJF34A0VrhL5y5S8mAISA8uwfNCQKmR2k4KoQJm3De1J8SQeNzSjLviAwh1faDow6FXGlA6tVbTrHyDcBgBg==",
+            "version": "3.12.0",
+            "resolved": "https://registry.npmjs.org/@react-stately/datepicker/-/datepicker-3.12.0.tgz",
+            "integrity": "sha512-AfJEP36d+QgQ30GfacXtYdGsJvqY2yuCJ+JrjHct+m1nYuTkMvMMnhwNBFasgDJPLCDyHzyANlWkl2kQGfsBFw==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@internationalized/date": "^3.6.0",
+                "@internationalized/date": "^3.7.0",
                 "@internationalized/string": "^3.2.5",
-                "@react-stately/form": "^3.1.0",
-                "@react-stately/overlays": "^3.6.12",
+                "@react-stately/form": "^3.1.1",
+                "@react-stately/overlays": "^3.6.13",
                 "@react-stately/utils": "^3.10.5",
-                "@react-types/datepicker": "^3.9.0",
-                "@react-types/shared": "^3.26.0",
+                "@react-types/datepicker": "^3.10.0",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -8743,13 +8770,13 @@
             }
         },
         "node_modules/@react-stately/disclosure": {
-            "version": "3.0.0",
-            "resolved": "https://registry.npmjs.org/@react-stately/disclosure/-/disclosure-3.0.0.tgz",
-            "integrity": "sha512-Z9+fi0/41ZXHjGopORQza7mk4lFEFslKhy65ehEo6O6j2GuIV0659ExIVDsmJoJSFjXCfGh0sX8oTSOlXi9gqg==",
+            "version": "3.0.1",
+            "resolved": "https://registry.npmjs.org/@react-stately/disclosure/-/disclosure-3.0.1.tgz",
+            "integrity": "sha512-afpNy5b0UcqRGjU/W5OD0xkx4PbymvhMrgQZ4o4OdtDVMMvr9T5UqMF8/j3J591DxgQfXM872tJu0kotqT0L6Q==",
             "license": "Apache-2.0",
             "dependencies": {
                 "@react-stately/utils": "^3.10.5",
-                "@react-types/shared": "^3.26.0",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -8757,13 +8784,13 @@
             }
         },
         "node_modules/@react-stately/dnd": {
-            "version": "3.5.0",
-            "resolved": "https://registry.npmjs.org/@react-stately/dnd/-/dnd-3.5.0.tgz",
-            "integrity": "sha512-ZcWFw1npEDnATiy3TEdzA1skQ3UEIyfbNA6VhPNO8yiSVLxoxBOaEaq8VVS72fRGAtxud6dgOy8BnsP9JwDClQ==",
+            "version": "3.5.1",
+            "resolved": "https://registry.npmjs.org/@react-stately/dnd/-/dnd-3.5.1.tgz",
+            "integrity": "sha512-N18wt6fka9ngJJqxfAzmdtyrk9whAnqWUxZn22CatjNQsqukI4a6KRYwZTXM9x/wm7KamhVOp+GBl85zM8GLdA==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-stately/selection": "^3.18.0",
-                "@react-types/shared": "^3.26.0",
+                "@react-stately/selection": "^3.19.0",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -8780,12 +8807,12 @@
             }
         },
         "node_modules/@react-stately/form": {
-            "version": "3.1.0",
-            "resolved": "https://registry.npmjs.org/@react-stately/form/-/form-3.1.0.tgz",
-            "integrity": "sha512-E2wxNQ0QaTyDHD0nJFtTSnEH9A3bpJurwxhS4vgcUmESHgjFEMLlC9irUSZKgvOgb42GAq+fHoWBsgKeTp9Big==",
+            "version": "3.1.1",
+            "resolved": "https://registry.npmjs.org/@react-stately/form/-/form-3.1.1.tgz",
+            "integrity": "sha512-qavrz5X5Mdf/Q1v/QJRxc0F8UTNEyRCNSM1we/nnF7GV64+aYSDLOtaRGmzq+09RSwo1c8ZYnIkK5CnwsPhTsQ==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-types/shared": "^3.26.0",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -8793,15 +8820,15 @@
             }
         },
         "node_modules/@react-stately/grid": {
-            "version": "3.10.0",
-            "resolved": "https://registry.npmjs.org/@react-stately/grid/-/grid-3.10.0.tgz",
-            "integrity": "sha512-ii+DdsOBvCnHMgL0JvUfFwO1kiAPP19Bpdpl6zn/oOltk6F5TmnoyNrzyz+2///1hCiySI3FE1O7ujsAQs7a6Q==",
+            "version": "3.10.1",
+            "resolved": "https://registry.npmjs.org/@react-stately/grid/-/grid-3.10.1.tgz",
+            "integrity": "sha512-MOIy//AdxZxIXIzvWSKpvMvaPEMZGQNj+/cOsElHepv/Veh0psNURZMh2TP6Mr0+MnDTZbX+5XIeinGkWYO3JQ==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-stately/collections": "^3.12.0",
-                "@react-stately/selection": "^3.18.0",
-                "@react-types/grid": "^3.2.10",
-                "@react-types/shared": "^3.26.0",
+                "@react-stately/collections": "^3.12.1",
+                "@react-stately/selection": "^3.19.0",
+                "@react-types/grid": "^3.2.11",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -8809,17 +8836,17 @@
             }
         },
         "node_modules/@react-stately/layout": {
-            "version": "4.1.0",
-            "resolved": "https://registry.npmjs.org/@react-stately/layout/-/layout-4.1.0.tgz",
-            "integrity": "sha512-pSBqn+4EeOaf2QMK+w2SHgsWKYHdgMZWY3qgJijdzWGZ4JpYmHuiD0yOq80qFdUwxcexPW3vFg3hqIQlMpXeCw==",
+            "version": "4.1.1",
+            "resolved": "https://registry.npmjs.org/@react-stately/layout/-/layout-4.1.1.tgz",
+            "integrity": "sha512-kXeo7HKYTOcqMKru1sKFoMoZA+YywSUqHeIA90MptzRugbFhQGq4nUbIYM2p3FeHAX9HU1JAXThuLcwDOHhB8Q==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-stately/collections": "^3.12.0",
-                "@react-stately/table": "^3.13.0",
-                "@react-stately/virtualizer": "^4.2.0",
-                "@react-types/grid": "^3.2.10",
-                "@react-types/shared": "^3.26.0",
-                "@react-types/table": "^3.10.3",
+                "@react-stately/collections": "^3.12.1",
+                "@react-stately/table": "^3.13.1",
+                "@react-stately/virtualizer": "^4.2.1",
+                "@react-types/grid": "^3.2.11",
+                "@react-types/shared": "^3.27.0",
+                "@react-types/table": "^3.10.4",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -8827,15 +8854,15 @@
             }
         },
         "node_modules/@react-stately/list": {
-            "version": "3.11.1",
-            "resolved": "https://registry.npmjs.org/@react-stately/list/-/list-3.11.1.tgz",
-            "integrity": "sha512-UCOpIvqBOjwLtk7zVTYWuKU1m1Oe61Q5lNar/GwHaV1nAiSQ8/yYlhr40NkBEs9X3plEfsV28UIpzOrYnu1tPg==",
+            "version": "3.11.2",
+            "resolved": "https://registry.npmjs.org/@react-stately/list/-/list-3.11.2.tgz",
+            "integrity": "sha512-eU2tY3aWj0SEeC7lH9AQoeAB4LL9mwS54FvTgHHoOgc1ZIwRJUaZoiuETyWQe98AL8KMgR1nrnDJ1I+CcT1Y7g==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-stately/collections": "^3.12.0",
-                "@react-stately/selection": "^3.18.0",
+                "@react-stately/collections": "^3.12.1",
+                "@react-stately/selection": "^3.19.0",
                 "@react-stately/utils": "^3.10.5",
-                "@react-types/shared": "^3.26.0",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -8843,14 +8870,14 @@
             }
         },
         "node_modules/@react-stately/menu": {
-            "version": "3.9.0",
-            "resolved": "https://registry.npmjs.org/@react-stately/menu/-/menu-3.9.0.tgz",
-            "integrity": "sha512-++sm0fzZeUs9GvtRbj5RwrP+KL9KPANp9f4SvtI3s+MP+Y/X3X7LNNePeeccGeyikB5fzMsuyvd82bRRW9IhDQ==",
+            "version": "3.9.1",
+            "resolved": "https://registry.npmjs.org/@react-stately/menu/-/menu-3.9.1.tgz",
+            "integrity": "sha512-WRjGGImhQlQaer/hhahGytwd1BDq3fjpTkY/04wv3cQJPJR6lkVI5nSvGFMHfCaErsA1bNyB8/T9Y5F5u4u9ng==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-stately/overlays": "^3.6.12",
-                "@react-types/menu": "^3.9.13",
-                "@react-types/shared": "^3.26.0",
+                "@react-stately/overlays": "^3.6.13",
+                "@react-types/menu": "^3.9.14",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -8858,15 +8885,15 @@
             }
         },
         "node_modules/@react-stately/numberfield": {
-            "version": "3.9.8",
-            "resolved": "https://registry.npmjs.org/@react-stately/numberfield/-/numberfield-3.9.8.tgz",
-            "integrity": "sha512-J6qGILxDNEtu7yvd3/y+FpbrxEaAeIODwlrFo6z1kvuDlLAm/KszXAc75yoDi0OtakFTCMP6/HR5VnHaQdMJ3w==",
+            "version": "3.9.9",
+            "resolved": "https://registry.npmjs.org/@react-stately/numberfield/-/numberfield-3.9.9.tgz",
+            "integrity": "sha512-hZsLiGGHTHmffjFymbH1qVmA633rU2GNjMFQTuSsN4lqqaP8fgxngd5pPCoTCUFEkUgWjdHenw+ZFByw8lIE+g==",
             "license": "Apache-2.0",
             "dependencies": {
                 "@internationalized/number": "^3.6.0",
-                "@react-stately/form": "^3.1.0",
+                "@react-stately/form": "^3.1.1",
                 "@react-stately/utils": "^3.10.5",
-                "@react-types/numberfield": "^3.8.7",
+                "@react-types/numberfield": "^3.8.8",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -8874,13 +8901,13 @@
             }
         },
         "node_modules/@react-stately/overlays": {
-            "version": "3.6.12",
-            "resolved": "https://registry.npmjs.org/@react-stately/overlays/-/overlays-3.6.12.tgz",
-            "integrity": "sha512-QinvZhwZgj8obUyPIcyURSCjTZlqZYRRCS60TF8jH8ZpT0tEAuDb3wvhhSXuYA3Xo9EHLwvLjEf3tQKKdAQArw==",
+            "version": "3.6.13",
+            "resolved": "https://registry.npmjs.org/@react-stately/overlays/-/overlays-3.6.13.tgz",
+            "integrity": "sha512-WsU85Gf/b+HbWsnnYw7P/Ila3wD+C37Uk/WbU4/fHgJ26IEOWsPE6wlul8j54NZ1PnLNhV9Fn+Kffi+PaJMQXQ==",
             "license": "Apache-2.0",
             "dependencies": {
                 "@react-stately/utils": "^3.10.5",
-                "@react-types/overlays": "^3.8.11",
+                "@react-types/overlays": "^3.8.12",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -8888,15 +8915,15 @@
             }
         },
         "node_modules/@react-stately/radio": {
-            "version": "3.10.9",
-            "resolved": "https://registry.npmjs.org/@react-stately/radio/-/radio-3.10.9.tgz",
-            "integrity": "sha512-kUQ7VdqFke8SDRCatw2jW3rgzMWbvw+n2imN2THETynI47NmNLzNP11dlGO2OllRtTrsLhmBNlYHa3W62pFpAw==",
+            "version": "3.10.10",
+            "resolved": "https://registry.npmjs.org/@react-stately/radio/-/radio-3.10.10.tgz",
+            "integrity": "sha512-9x3bpq87uV8iYA4NaioTTWjriQSlSdp+Huqlxll0T3W3okpyraTTejE91PbIoRTUmL5qByIh2WzxYmr4QdBgAA==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-stately/form": "^3.1.0",
+                "@react-stately/form": "^3.1.1",
                 "@react-stately/utils": "^3.10.5",
-                "@react-types/radio": "^3.8.5",
-                "@react-types/shared": "^3.26.0",
+                "@react-types/radio": "^3.8.6",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -8904,13 +8931,13 @@
             }
         },
         "node_modules/@react-stately/searchfield": {
-            "version": "3.5.8",
-            "resolved": "https://registry.npmjs.org/@react-stately/searchfield/-/searchfield-3.5.8.tgz",
-            "integrity": "sha512-jtquvGadx1DmtQqPKaVO6Qg/xpBjNxsOd59ciig9xRxpxV+90i996EX1E2R6R+tGJdSM1pD++7PVOO4yE++HOg==",
+            "version": "3.5.9",
+            "resolved": "https://registry.npmjs.org/@react-stately/searchfield/-/searchfield-3.5.9.tgz",
+            "integrity": "sha512-7/aO/oLJ4czKEji0taI/lbHKqPJRag9p3YmRaZ4yqjIMpKxzmJCWQcov5lzWeFhG/1hINKndYlxFnVIKV/urpg==",
             "license": "Apache-2.0",
             "dependencies": {
                 "@react-stately/utils": "^3.10.5",
-                "@react-types/searchfield": "^3.5.10",
+                "@react-types/searchfield": "^3.5.11",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -8918,16 +8945,16 @@
             }
         },
         "node_modules/@react-stately/select": {
-            "version": "3.6.9",
-            "resolved": "https://registry.npmjs.org/@react-stately/select/-/select-3.6.9.tgz",
-            "integrity": "sha512-vASUDv7FhEYQURzM+JIwcusPv7/x/l3zHc/oKJPvoCl3aa9pwS8hZwS82SC00o2iFnrDscfDJju4IE/cd4hucg==",
+            "version": "3.6.10",
+            "resolved": "https://registry.npmjs.org/@react-stately/select/-/select-3.6.10.tgz",
+            "integrity": "sha512-V7V0FCL9T+GzLjyfnJB6PUaKldFyT/8Rj6M+R9ura1A0O+s/FEOesy0pdMXFoL1l5zeUpGlCnhJrsI5HFWHfDw==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-stately/form": "^3.1.0",
-                "@react-stately/list": "^3.11.1",
-                "@react-stately/overlays": "^3.6.12",
-                "@react-types/select": "^3.9.8",
-                "@react-types/shared": "^3.26.0",
+                "@react-stately/form": "^3.1.1",
+                "@react-stately/list": "^3.11.2",
+                "@react-stately/overlays": "^3.6.13",
+                "@react-types/select": "^3.9.9",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -8935,14 +8962,14 @@
             }
         },
         "node_modules/@react-stately/selection": {
-            "version": "3.18.0",
-            "resolved": "https://registry.npmjs.org/@react-stately/selection/-/selection-3.18.0.tgz",
-            "integrity": "sha512-6EaNNP3exxBhW2LkcRR4a3pg+3oDguZlBSqIVVR7lyahv/D8xXHRC4dX+m0mgGHJpsgjs7664Xx6c8v193TFxg==",
+            "version": "3.19.0",
+            "resolved": "https://registry.npmjs.org/@react-stately/selection/-/selection-3.19.0.tgz",
+            "integrity": "sha512-AvbUqnWjqVQC48RD39S9BpMKMLl55Zo5l/yx5JQFPl55cFwe9Tpku1KY0wzt3fXXiXWaqjDn/7Gkg1VJYy8esQ==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-stately/collections": "^3.12.0",
+                "@react-stately/collections": "^3.12.1",
                 "@react-stately/utils": "^3.10.5",
-                "@react-types/shared": "^3.26.0",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -8950,14 +8977,14 @@
             }
         },
         "node_modules/@react-stately/slider": {
-            "version": "3.6.0",
-            "resolved": "https://registry.npmjs.org/@react-stately/slider/-/slider-3.6.0.tgz",
-            "integrity": "sha512-w5vJxVh267pmD1X+Ppd9S3ZzV1hcg0cV8q5P4Egr160b9WMcWlUspZPtsthwUlN7qQe/C8y5IAhtde4s29eNag==",
+            "version": "3.6.1",
+            "resolved": "https://registry.npmjs.org/@react-stately/slider/-/slider-3.6.1.tgz",
+            "integrity": "sha512-8kij5O82Xe233vZZ6qNGqPXidnlNQiSnyF1q613c7ktFmzAyGjkIWVUapHi23T1fqm7H2Rs3RWlmwE9bo2KecA==",
             "license": "Apache-2.0",
             "dependencies": {
                 "@react-stately/utils": "^3.10.5",
-                "@react-types/shared": "^3.26.0",
-                "@react-types/slider": "^3.7.7",
+                "@react-types/shared": "^3.27.0",
+                "@react-types/slider": "^3.7.8",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -8965,19 +8992,19 @@
             }
         },
         "node_modules/@react-stately/table": {
-            "version": "3.13.0",
-            "resolved": "https://registry.npmjs.org/@react-stately/table/-/table-3.13.0.tgz",
-            "integrity": "sha512-mRbNYrwQIE7xzVs09Lk3kPteEVFVyOc20vA8ph6EP54PiUf/RllJpxZe/WUYLf4eom9lUkRYej5sffuUBpxjCA==",
+            "version": "3.13.1",
+            "resolved": "https://registry.npmjs.org/@react-stately/table/-/table-3.13.1.tgz",
+            "integrity": "sha512-Im8W+F8o9EhglY5kqRa3xcMGXl8zBi6W5phGpAjXb+UGDL1tBIlAcYj733bw8g/ITCnaSz9ubsmON0HekPd6Jg==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-stately/collections": "^3.12.0",
+                "@react-stately/collections": "^3.12.1",
                 "@react-stately/flags": "^3.0.5",
-                "@react-stately/grid": "^3.10.0",
-                "@react-stately/selection": "^3.18.0",
+                "@react-stately/grid": "^3.10.1",
+                "@react-stately/selection": "^3.19.0",
                 "@react-stately/utils": "^3.10.5",
-                "@react-types/grid": "^3.2.10",
-                "@react-types/shared": "^3.26.0",
-                "@react-types/table": "^3.10.3",
+                "@react-types/grid": "^3.2.11",
+                "@react-types/shared": "^3.27.0",
+                "@react-types/table": "^3.10.4",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -8985,14 +9012,14 @@
             }
         },
         "node_modules/@react-stately/tabs": {
-            "version": "3.7.0",
-            "resolved": "https://registry.npmjs.org/@react-stately/tabs/-/tabs-3.7.0.tgz",
-            "integrity": "sha512-ox4hTkfZCoR4Oyr3Op3rBlWNq2Wxie04vhEYpTZQ2hobR3l4fYaOkd7CPClILktJ3TC104j8wcb0knWxIBRx9w==",
+            "version": "3.7.1",
+            "resolved": "https://registry.npmjs.org/@react-stately/tabs/-/tabs-3.7.1.tgz",
+            "integrity": "sha512-gr9ACyuWrYuc727h7WaHdmNw8yxVlUyQlguziR94MdeRtFGQnf3V6fNQG3kxyB77Ljko69tgDF7Nf6kfPUPAQQ==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-stately/list": "^3.11.1",
-                "@react-types/shared": "^3.26.0",
-                "@react-types/tabs": "^3.3.11",
+                "@react-stately/list": "^3.11.2",
+                "@react-types/shared": "^3.27.0",
+                "@react-types/tabs": "^3.3.12",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -9000,14 +9027,14 @@
             }
         },
         "node_modules/@react-stately/toggle": {
-            "version": "3.8.0",
-            "resolved": "https://registry.npmjs.org/@react-stately/toggle/-/toggle-3.8.0.tgz",
-            "integrity": "sha512-pyt/k/J8BwE/2g6LL6Z6sMSWRx9HEJB83Sm/MtovXnI66sxJ2EfQ1OaXB7Su5PEL9OMdoQF6Mb+N1RcW3zAoPw==",
+            "version": "3.8.1",
+            "resolved": "https://registry.npmjs.org/@react-stately/toggle/-/toggle-3.8.1.tgz",
+            "integrity": "sha512-MVpe79ghVQiwLmVzIPhF/O/UJAUc9B+ZSylVTyJiEPi0cwhbkKGQv9thOF0ebkkRkace5lojASqUAYtSTZHQJA==",
             "license": "Apache-2.0",
             "dependencies": {
                 "@react-stately/utils": "^3.10.5",
-                "@react-types/checkbox": "^3.9.0",
-                "@react-types/shared": "^3.26.0",
+                "@react-types/checkbox": "^3.9.1",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -9015,13 +9042,13 @@
             }
         },
         "node_modules/@react-stately/tooltip": {
-            "version": "3.5.0",
-            "resolved": "https://registry.npmjs.org/@react-stately/tooltip/-/tooltip-3.5.0.tgz",
-            "integrity": "sha512-+xzPNztJDd2XJD0X3DgWKlrgOhMqZpSzsIssXeJgO7uCnP8/Z513ESaipJhJCFC8fxj5caO/DK4Uu8hEtlB8cQ==",
+            "version": "3.5.1",
+            "resolved": "https://registry.npmjs.org/@react-stately/tooltip/-/tooltip-3.5.1.tgz",
+            "integrity": "sha512-0aI3U5kB7Cop9OCW9/Bag04zkivFSdUcQgy/TWL4JtpXidVWmOha8txI1WySawFSjZhH83KIyPc+wKm1msfLMQ==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-stately/overlays": "^3.6.12",
-                "@react-types/tooltip": "^3.4.13",
+                "@react-stately/overlays": "^3.6.13",
+                "@react-types/tooltip": "^3.4.14",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -9029,15 +9056,15 @@
             }
         },
         "node_modules/@react-stately/tree": {
-            "version": "3.8.6",
-            "resolved": "https://registry.npmjs.org/@react-stately/tree/-/tree-3.8.6.tgz",
-            "integrity": "sha512-lblUaxf1uAuIz5jm6PYtcJ+rXNNVkqyFWTIMx6g6gW/mYvm8GNx1G/0MLZE7E6CuDGaO9dkLSY2bB1uqyKHidA==",
+            "version": "3.8.7",
+            "resolved": "https://registry.npmjs.org/@react-stately/tree/-/tree-3.8.7.tgz",
+            "integrity": "sha512-hpc3pyuXWeQV5ufQ02AeNQg/MYhnzZ4NOznlY5OOUoPzpLYiI3ZJubiY3Dot4jw5N/LR7CqvDLHmrHaJPmZlHg==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-stately/collections": "^3.12.0",
-                "@react-stately/selection": "^3.18.0",
+                "@react-stately/collections": "^3.12.1",
+                "@react-stately/selection": "^3.19.0",
                 "@react-stately/utils": "^3.10.5",
-                "@react-types/shared": "^3.26.0",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -9057,539 +9084,554 @@
             }
         },
         "node_modules/@react-stately/virtualizer": {
-            "version": "4.2.0",
-            "resolved": "https://registry.npmjs.org/@react-stately/virtualizer/-/virtualizer-4.2.0.tgz",
-            "integrity": "sha512-aTMpa9AQoz/xLqn8AI1BR/caUUY7/OUo9GbuF434w2u5eGCL7+SAn3Fmq7WSCwqYyDsO+jEIERek4JTX7pEW0A==",
+            "version": "4.2.1",
+            "resolved": "https://registry.npmjs.org/@react-stately/virtualizer/-/virtualizer-4.2.1.tgz",
+            "integrity": "sha512-GHGEXV0ZRhq34U/P3LzkByCBfy2IDynYlV1SE4njkUWWGE/0AH56UegM6w2l3GeiNpXsXCgXl7jpAKeIGMEnrQ==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-aria/utils": "^3.26.0",
-                "@react-types/shared": "^3.26.0",
+                "@react-aria/utils": "^3.27.0",
+                "@react-types/shared": "^3.27.0",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
-                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+                "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-types/actionbar": {
-            "version": "3.1.11",
-            "resolved": "https://registry.npmjs.org/@react-types/actionbar/-/actionbar-3.1.11.tgz",
-            "integrity": "sha512-e/wuRd2p4NbfJYaDxB29Owihqe1jVqSrvcQzEJ9GBhiY408KIVtq7fBfQbCDH7tIkZIGsm3yf+SWPNKG79lROw==",
+            "version": "3.1.12",
+            "resolved": "https://registry.npmjs.org/@react-types/actionbar/-/actionbar-3.1.12.tgz",
+            "integrity": "sha512-iXX5lpk6S7Si8fyAvf4hSAs83UM0+miuXNX9xh80CQk3TZYy4WWzQRE/NDEdKy6FZ2eqzE4PJracOZ8znguqhA==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-types/shared": "^3.26.0"
+                "@react-types/shared": "^3.27.0"
             },
             "peerDependencies": {
                 "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-types/actiongroup": {
-            "version": "3.4.13",
-            "resolved": "https://registry.npmjs.org/@react-types/actiongroup/-/actiongroup-3.4.13.tgz",
-            "integrity": "sha512-OnHwPHeXsVq65jrb6ZeL2HJwoW1a2c1ogO+dZhAxrn194XwGU7p62tpXpnu3U/2Lk+tV23C/V5YMjzdbNwpTPQ==",
+            "version": "3.4.14",
+            "resolved": "https://registry.npmjs.org/@react-types/actiongroup/-/actiongroup-3.4.14.tgz",
+            "integrity": "sha512-qwbJs3NvrWz/YenEAk11Ia3RqGVeaZOJAyKbeiE8ZRU1uj4E8CzWivFrrhsBwnTChGzLWnUql3AGLgTebt+nrQ==",
+            "license": "Apache-2.0",
+            "dependencies": {
+                "@react-types/shared": "^3.27.0"
+            },
+            "peerDependencies": {
+                "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+            }
+        },
+        "node_modules/@react-types/autocomplete": {
+            "version": "3.0.0-alpha.28",
+            "resolved": "https://registry.npmjs.org/@react-types/autocomplete/-/autocomplete-3.0.0-alpha.28.tgz",
+            "integrity": "sha512-meHxBVS5H2L7lVOX99jiAfhcvtG0s7EE7iF7X20/yqEnkwWSpyeMKcDKFpvx/bLGUSmRTVFCBLgvPpwUyhcFkg==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-types/shared": "^3.26.0"
+                "@react-types/combobox": "^3.13.2",
+                "@react-types/searchfield": "^3.5.11",
+                "@react-types/shared": "^3.27.0"
             },
             "peerDependencies": {
                 "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-types/avatar": {
-            "version": "3.0.11",
-            "resolved": "https://registry.npmjs.org/@react-types/avatar/-/avatar-3.0.11.tgz",
-            "integrity": "sha512-vlycZ3X9xJt/83gDUp89gxZXBq8yqOwdOydkMfisDut3NyyGVejeSPYjW2/ysE+xRQsXAEzfTzTPO5rPVsJiYQ==",
+            "version": "3.0.12",
+            "resolved": "https://registry.npmjs.org/@react-types/avatar/-/avatar-3.0.12.tgz",
+            "integrity": "sha512-R2cLbebg46SKRxNo2sCrN5foenEOiD89nD0JhMoZt4Ju2CQ2DlS6JAMW2rPD3hKcPKE6imSuw5wNRCMJLb+8SQ==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-types/shared": "^3.26.0"
+                "@react-types/shared": "^3.27.0"
             },
             "peerDependencies": {
                 "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-types/badge": {
-            "version": "3.1.13",
-            "resolved": "https://registry.npmjs.org/@react-types/badge/-/badge-3.1.13.tgz",
-            "integrity": "sha512-CjhHa719iuknX8ikHw++Zk9rix3pAMpOTUMjaCzc8fHdQGxGVw+NjcEp7srEp7Y/aXRS9NOk56d1JJnl97VMJQ==",
+            "version": "3.1.14",
+            "resolved": "https://registry.npmjs.org/@react-types/badge/-/badge-3.1.14.tgz",
+            "integrity": "sha512-3TBp29rTITyBHsX79uHSZWwkxcpVkFOrwlAkBscVXKkRW59ufApkjGoxR9TRvrpQKVQnYPuylErDeUovqF+utg==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-types/shared": "^3.26.0"
+                "@react-types/shared": "^3.27.0"
             },
             "peerDependencies": {
                 "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-types/breadcrumbs": {
-            "version": "3.7.9",
-            "resolved": "https://registry.npmjs.org/@react-types/breadcrumbs/-/breadcrumbs-3.7.9.tgz",
-            "integrity": "sha512-eARYJo8J+VfNV8vP4uw3L2Qliba9wLV2bx9YQCYf5Lc/OE5B/y4gaTLz+Y2P3Rtn6gBPLXY447zCs5i7gf+ICg==",
+            "version": "3.7.10",
+            "resolved": "https://registry.npmjs.org/@react-types/breadcrumbs/-/breadcrumbs-3.7.10.tgz",
+            "integrity": "sha512-5HhRxkKHfAQBoyOYzyf4HT+24HgPE/C/QerxJLNNId303LXO03yeYrbvRqhYZSlD1ACLJW9OmpPpREcw5iSqgw==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-types/link": "^3.5.9",
-                "@react-types/shared": "^3.26.0"
+                "@react-types/link": "^3.5.10",
+                "@react-types/shared": "^3.27.0"
             },
             "peerDependencies": {
                 "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-types/button": {
-            "version": "3.10.1",
-            "resolved": "https://registry.npmjs.org/@react-types/button/-/button-3.10.1.tgz",
-            "integrity": "sha512-XTtap8o04+4QjPNAshFWOOAusUTxQlBjU2ai0BTVLShQEjHhRVDBIWsI2B2FKJ4KXT6AZ25llaxhNrreWGonmA==",
+            "version": "3.10.2",
+            "resolved": "https://registry.npmjs.org/@react-types/button/-/button-3.10.2.tgz",
+            "integrity": "sha512-h8SB/BLoCgoBulCpyzaoZ+miKXrolK9XC48+n1dKJXT8g4gImrficurDW6+PRTQWaRai0Q0A6bu8UibZOU4syg==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-types/shared": "^3.26.0"
+                "@react-types/shared": "^3.27.0"
             },
             "peerDependencies": {
                 "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-types/buttongroup": {
-            "version": "3.3.13",
-            "resolved": "https://registry.npmjs.org/@react-types/buttongroup/-/buttongroup-3.3.13.tgz",
-            "integrity": "sha512-p75vTOdt+6BkwVxke6jQpQLqyks1axq7afjLt4IghsVRpK6stsfJQC5wqyc9zaf6ESuzEEbmV+RcXN8aE92jIA==",
+            "version": "3.3.14",
+            "resolved": "https://registry.npmjs.org/@react-types/buttongroup/-/buttongroup-3.3.14.tgz",
+            "integrity": "sha512-JphWxwrxvu7OfpDrHINXYyoNR7T6VuK6wx1eiAsH09REObuRPx81sDodLBjObtkip8ylpybTYaABneibAZF+Vw==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-types/shared": "^3.26.0"
+                "@react-types/shared": "^3.27.0"
             },
             "peerDependencies": {
                 "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-types/calendar": {
-            "version": "3.5.0",
-            "resolved": "https://registry.npmjs.org/@react-types/calendar/-/calendar-3.5.0.tgz",
-            "integrity": "sha512-O3IRE7AGwAWYnvJIJ80cOy7WwoJ0m8GtX/qSmvXQAjC4qx00n+b5aFNBYAQtcyc3RM5QpW6obs9BfwGetFiI8w==",
+            "version": "3.6.0",
+            "resolved": "https://registry.npmjs.org/@react-types/calendar/-/calendar-3.6.0.tgz",
+            "integrity": "sha512-BtFh4BFwvsYlsaSqUOVxlqXZSlJ6u4aozgO3PwHykhpemwidlzNwm9qDZhcMWPioNF/w2cU/6EqhvEKUHDnFZg==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@internationalized/date": "^3.6.0",
-                "@react-types/shared": "^3.26.0"
+                "@internationalized/date": "^3.7.0",
+                "@react-types/shared": "^3.27.0"
             },
             "peerDependencies": {
                 "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-types/checkbox": {
-            "version": "3.9.0",
-            "resolved": "https://registry.npmjs.org/@react-types/checkbox/-/checkbox-3.9.0.tgz",
-            "integrity": "sha512-9hbHx0Oo2Hp5a8nV8Q75LQR0DHtvOIJbFaeqESSopqmV9EZoYjtY/h0NS7cZetgahQgnqYWQi44XGooMDCsmxA==",
+            "version": "3.9.1",
+            "resolved": "https://registry.npmjs.org/@react-types/checkbox/-/checkbox-3.9.1.tgz",
+            "integrity": "sha512-0x/KQcipfNM9Nvy6UMwYG25roRLvsiqf0J3woTYylNNWzF+72XT0iI5FdJkE3w2wfa0obmSoeq4WcbFREQrH/A==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-types/shared": "^3.26.0"
+                "@react-types/shared": "^3.27.0"
             },
             "peerDependencies": {
                 "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-types/color": {
-            "version": "3.0.1",
-            "resolved": "https://registry.npmjs.org/@react-types/color/-/color-3.0.1.tgz",
-            "integrity": "sha512-KemFziO3GbmT3HEKrgOGdqNA6Gsmy9xrwFO3f8qXSG7gVz6M27Ic4R9HVQv4iAjap5uti6W13/pk2bc/jLVcEA==",
+            "version": "3.0.2",
+            "resolved": "https://registry.npmjs.org/@react-types/color/-/color-3.0.2.tgz",
+            "integrity": "sha512-4k9c0l5SACwTtkHV0dQ0GrF0Kktk/NChkxtyu58BamyUQOsCe8sqny+uul2nPrqQvuVof/dkRjKhv/DVyyx2mw==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-types/shared": "^3.26.0",
-                "@react-types/slider": "^3.7.7"
+                "@react-types/shared": "^3.27.0",
+                "@react-types/slider": "^3.7.8"
             },
             "peerDependencies": {
                 "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-types/combobox": {
-            "version": "3.13.1",
-            "resolved": "https://registry.npmjs.org/@react-types/combobox/-/combobox-3.13.1.tgz",
-            "integrity": "sha512-7xr+HknfhReN4QPqKff5tbKTe2kGZvH+DGzPYskAtb51FAAiZsKo+WvnNAvLwg3kRoC9Rkn4TAiVBp/HgymRDw==",
+            "version": "3.13.2",
+            "resolved": "https://registry.npmjs.org/@react-types/combobox/-/combobox-3.13.2.tgz",
+            "integrity": "sha512-yl2yMcM5/v3lJiNZWjpAhQ9vRW6dD55CD4rYmO2K7XvzYJaFVT4WYI/AymPYD8RqomMp7coBmBHfHW0oupk8gg==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-types/shared": "^3.26.0"
+                "@react-types/shared": "^3.27.0"
             },
             "peerDependencies": {
                 "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-types/contextualhelp": {
-            "version": "3.2.14",
-            "resolved": "https://registry.npmjs.org/@react-types/contextualhelp/-/contextualhelp-3.2.14.tgz",
-            "integrity": "sha512-fNj3Iz3giCs7tx36flzFuLsR2nhPpa/4hD14WXj6iJ9Y6e0GcY8pZXUZhglAFVcfUatwN1ifmfwpzh7FcbfKFQ==",
+            "version": "3.2.15",
+            "resolved": "https://registry.npmjs.org/@react-types/contextualhelp/-/contextualhelp-3.2.15.tgz",
+            "integrity": "sha512-hLNCuK4aecct2ATxoS4Q6yOoOB4E9cZqoEPhtIeNSlDd4/2zewzDCXbFlsHrSFibU5Vp51n3iNmllTvYBIAc4A==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-types/overlays": "^3.8.11",
-                "@react-types/shared": "^3.26.0"
+                "@react-types/overlays": "^3.8.12",
+                "@react-types/shared": "^3.27.0"
             },
             "peerDependencies": {
                 "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-types/datepicker": {
-            "version": "3.9.0",
-            "resolved": "https://registry.npmjs.org/@react-types/datepicker/-/datepicker-3.9.0.tgz",
-            "integrity": "sha512-dbKL5Qsm2MQwOTtVQdOcKrrphcXAqDD80WLlSQrBLg+waDuuQ7H+TrvOT0thLKloNBlFUGnZZfXGRHINpih/0g==",
+            "version": "3.10.0",
+            "resolved": "https://registry.npmjs.org/@react-types/datepicker/-/datepicker-3.10.0.tgz",
+            "integrity": "sha512-Att7y4NedNH1CogMDIX9URXgMLxGbZgnFCZ8oxgFAVndWzbh3TBcc4s7uoJDPvgRMAalq+z+SrlFFeoBeJmvvg==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@internationalized/date": "^3.6.0",
-                "@react-types/calendar": "^3.5.0",
-                "@react-types/overlays": "^3.8.11",
-                "@react-types/shared": "^3.26.0"
+                "@internationalized/date": "^3.7.0",
+                "@react-types/calendar": "^3.6.0",
+                "@react-types/overlays": "^3.8.12",
+                "@react-types/shared": "^3.27.0"
             },
             "peerDependencies": {
                 "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-types/dialog": {
-            "version": "3.5.14",
-            "resolved": "https://registry.npmjs.org/@react-types/dialog/-/dialog-3.5.14.tgz",
-            "integrity": "sha512-OXWMjrALwrlgw8aHD8SeRm/s3tbAssdaEh2h73KUSeFau3fU3n5mfKv+WnFqsEaOtN261o48l7hTlS6615H9AA==",
+            "version": "3.5.15",
+            "resolved": "https://registry.npmjs.org/@react-types/dialog/-/dialog-3.5.15.tgz",
+            "integrity": "sha512-BX1+mV35Oa0aIlhu98OzJaSB7uiCWDPQbr0AkpFBajSSlESUoAjntN+4N+QJmj24z2v6UE9zxGQ85/U/0Le+bw==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-types/overlays": "^3.8.11",
-                "@react-types/shared": "^3.26.0"
+                "@react-types/overlays": "^3.8.12",
+                "@react-types/shared": "^3.27.0"
             },
             "peerDependencies": {
                 "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-types/divider": {
-            "version": "3.3.13",
-            "resolved": "https://registry.npmjs.org/@react-types/divider/-/divider-3.3.13.tgz",
-            "integrity": "sha512-8Re0C1kCFKQHd+G6beIyS5t76dWK7QIiHDTm6TUcDz+fIwiwSp2BN/CoAWIJLdi/GW4nXeW7Th0aHZ3NOpux0A==",
+            "version": "3.3.14",
+            "resolved": "https://registry.npmjs.org/@react-types/divider/-/divider-3.3.14.tgz",
+            "integrity": "sha512-xigrYHP3eEFLeYcNngKkJspvLPv52MBnBdPGKEUZpO9axFL2OsijrAVmRjucXrT7ZbtKX1EJsztqE6LqrnlM2Q==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-types/shared": "^3.26.0"
+                "@react-types/shared": "^3.27.0"
             },
             "peerDependencies": {
                 "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-types/form": {
-            "version": "3.7.8",
-            "resolved": "https://registry.npmjs.org/@react-types/form/-/form-3.7.8.tgz",
-            "integrity": "sha512-0wOS97/X0ijTVuIqik1lHYTZnk13QkvMTKvIEhM7c6YMU3vPiirBwLbT2kJiAdwLiymwcCkrBdDF1NTRG6kPFA==",
+            "version": "3.7.9",
+            "resolved": "https://registry.npmjs.org/@react-types/form/-/form-3.7.9.tgz",
+            "integrity": "sha512-+qGDrQFdIh8umU82zmnYJ0V2rLoGSQ3yApFT02URz//NWeTA7qo0Oab2veKvXUkcBb47oSvytZYmkExPikxIEg==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-types/shared": "^3.26.0"
+                "@react-types/shared": "^3.27.0"
             },
             "peerDependencies": {
                 "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-types/grid": {
-            "version": "3.2.10",
-            "resolved": "https://registry.npmjs.org/@react-types/grid/-/grid-3.2.10.tgz",
-            "integrity": "sha512-Z5cG0ITwqjUE4kWyU5/7VqiPl4wqMJ7kG/ZP7poAnLmwRsR8Ai0ceVn+qzp5nTA19cgURi8t3LsXn3Ar1FBoog==",
+            "version": "3.2.11",
+            "resolved": "https://registry.npmjs.org/@react-types/grid/-/grid-3.2.11.tgz",
+            "integrity": "sha512-Mww9nrasppvPbsBi+uUqFnf7ya8fXN0cTVzDNG+SveD8mhW+sbtuy+gPtEpnFD2Oyi8qLuObefzt4gdekJX2Yw==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-types/shared": "^3.26.0"
+                "@react-types/shared": "^3.27.0"
             },
             "peerDependencies": {
                 "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-types/illustratedmessage": {
-            "version": "3.3.13",
-            "resolved": "https://registry.npmjs.org/@react-types/illustratedmessage/-/illustratedmessage-3.3.13.tgz",
-            "integrity": "sha512-1+YgtGzAff7Mj1eLPKryuGBUrhXlfr6OjTIe3ppw9gK4kjt/kUtFh+oW34ccQvBIwncFrkkLISXATr+/UwB1qQ==",
+            "version": "3.3.14",
+            "resolved": "https://registry.npmjs.org/@react-types/illustratedmessage/-/illustratedmessage-3.3.14.tgz",
+            "integrity": "sha512-jfpjXjDWv6v82bUa3p1vw00SW0pmpaNg/V7vKDbv5EN3l+p9NDMb171WNb+CWs0tWc8OPc6WJU4irosHB+ohtw==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-types/shared": "^3.26.0"
+                "@react-types/shared": "^3.27.0"
             },
             "peerDependencies": {
                 "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-types/image": {
-            "version": "3.4.5",
-            "resolved": "https://registry.npmjs.org/@react-types/image/-/image-3.4.5.tgz",
-            "integrity": "sha512-TGUMXyRLXebjPTdYnLRiiled3IDGDysdF37gnuw2zpGk+eM+/GxPAiOu2tho/rJTDLgkeN3P5q4x1nLK7HUxVA==",
+            "version": "3.4.6",
+            "resolved": "https://registry.npmjs.org/@react-types/image/-/image-3.4.6.tgz",
+            "integrity": "sha512-92MufABuMqoxVvVIcdK+InLVwozobdxAdqAGKhK7BdOASzSTY+yIV0b4/thQQ/xdbj14ZXY0pttrFNdNYRJZfw==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-types/shared": "^3.26.0"
+                "@react-types/shared": "^3.27.0"
             },
             "peerDependencies": {
                 "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-types/label": {
-            "version": "3.9.7",
-            "resolved": "https://registry.npmjs.org/@react-types/label/-/label-3.9.7.tgz",
-            "integrity": "sha512-qXGviqfctIq4QWb3dxoYDX0bn+LHeUd/sehs/bWmkQpzprqMdOTMOeJUW8OvC/l3rImsZoCcEVSqQuINcGXVUw==",
+            "version": "3.9.8",
+            "resolved": "https://registry.npmjs.org/@react-types/label/-/label-3.9.8.tgz",
+            "integrity": "sha512-a26VU/P1S1bw0h+EGLoJv2mmM5ZiTmQlPMWaAVaqE1+ANcTTmBrU8w3A1rhvyBcrZD6XQmVRK7mBpMexQr7AaA==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-types/shared": "^3.26.0"
+                "@react-types/shared": "^3.27.0"
             },
             "peerDependencies": {
                 "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-types/layout": {
-            "version": "3.3.19",
-            "resolved": "https://registry.npmjs.org/@react-types/layout/-/layout-3.3.19.tgz",
-            "integrity": "sha512-d8lC3FuQOC6Zevkm7ha1DIRbeMvFuw2R11m0+BArkZ/W20wfRcl7B6wh1Xm6WhoKMmFhH7QhiCJipReFHJMZDg==",
+            "version": "3.3.20",
+            "resolved": "https://registry.npmjs.org/@react-types/layout/-/layout-3.3.20.tgz",
+            "integrity": "sha512-+idyZy8Fh6nakVc7Khk/Rv1MX1IDYN93rvMeNpcidECWNN7RSGBj4nslp1DoUo9CyLoRt0ZBYB25G+VfU8j+BQ==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-types/shared": "^3.26.0"
+                "@react-types/shared": "^3.27.0"
             },
             "peerDependencies": {
                 "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-types/link": {
-            "version": "3.5.9",
-            "resolved": "https://registry.npmjs.org/@react-types/link/-/link-3.5.9.tgz",
-            "integrity": "sha512-JcKDiDMqrq/5Vpn+BdWQEuXit4KN4HR/EgIi3yKnNbYkLzxBoeQZpQgvTaC7NEQeZnSqkyXQo3/vMUeX/ZNIKw==",
+            "version": "3.5.10",
+            "resolved": "https://registry.npmjs.org/@react-types/link/-/link-3.5.10.tgz",
+            "integrity": "sha512-IM2mbSpB0qP44Jh1Iqpevo7bQdZAr0iDyDi13OhsiUYJeWgPMHzGEnQqdBMkrfQeOTXLtZtUyOYLXE2v39bhzQ==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-types/shared": "^3.26.0"
+                "@react-types/shared": "^3.27.0"
             },
             "peerDependencies": {
                 "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-types/listbox": {
-            "version": "3.5.3",
-            "resolved": "https://registry.npmjs.org/@react-types/listbox/-/listbox-3.5.3.tgz",
-            "integrity": "sha512-v1QXd9/XU3CCKr2Vgs7WLcTr6VMBur7CrxHhWZQQFExsf9bgJ/3wbUdjy4aThY/GsYHiaS38EKucCZFr1QAfqA==",
+            "version": "3.5.4",
+            "resolved": "https://registry.npmjs.org/@react-types/listbox/-/listbox-3.5.4.tgz",
+            "integrity": "sha512-5otTes0zOwRZwNtqysPD/aW4qFJSxd5znjwoWTLnzDXXOBHXPyR83IJf8ITgvIE5C0y+EFadsWR/BBO3k9Pj7g==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-types/shared": "^3.26.0"
+                "@react-types/shared": "^3.27.0"
             },
             "peerDependencies": {
                 "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-types/menu": {
-            "version": "3.9.13",
-            "resolved": "https://registry.npmjs.org/@react-types/menu/-/menu-3.9.13.tgz",
-            "integrity": "sha512-7SuX6E2tDsqQ+HQdSvIda1ji/+ujmR86dtS9CUu5yWX91P25ufRjZ72EvLRqClWNQsj1Xl4+2zBDLWlceznAjw==",
+            "version": "3.9.14",
+            "resolved": "https://registry.npmjs.org/@react-types/menu/-/menu-3.9.14.tgz",
+            "integrity": "sha512-RJW/S8IPwbRuohJ/A9HJ7W8QaAY816tm7Nv6+H/TLXG76zu2AS5vEgq+0TcCAWvJJwUdLDpJWJMlo0iIoIBtcg==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-types/overlays": "^3.8.11",
-                "@react-types/shared": "^3.26.0"
+                "@react-types/overlays": "^3.8.12",
+                "@react-types/shared": "^3.27.0"
             },
             "peerDependencies": {
                 "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-types/meter": {
-            "version": "3.4.5",
-            "resolved": "https://registry.npmjs.org/@react-types/meter/-/meter-3.4.5.tgz",
-            "integrity": "sha512-04w1lEtvP/c3Ep8ND8hhH2rwjz2MtQ8o8SNLhahen3u0rX3jKOgD4BvHujsyvXXTMjj1Djp74sGzNawb4Ppi9w==",
+            "version": "3.4.6",
+            "resolved": "https://registry.npmjs.org/@react-types/meter/-/meter-3.4.6.tgz",
+            "integrity": "sha512-YczAht1VXy3s4fR6Dq0ibGsjulGHzS/A/K4tOruSNTL6EkYH9ktHX62Xk/OhCiKHxV315EbZ136WJaCeO4BgHw==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-types/progress": "^3.5.8"
+                "@react-types/progress": "^3.5.9"
             },
             "peerDependencies": {
                 "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-types/numberfield": {
-            "version": "3.8.7",
-            "resolved": "https://registry.npmjs.org/@react-types/numberfield/-/numberfield-3.8.7.tgz",
-            "integrity": "sha512-KccMPi39cLoVkB2T0V7HW6nsxQVAwt89WWCltPZJVGzsebv/k0xTQlPVAgrUake4kDLoE687e3Fr/Oe3+1bDhw==",
+            "version": "3.8.8",
+            "resolved": "https://registry.npmjs.org/@react-types/numberfield/-/numberfield-3.8.8.tgz",
+            "integrity": "sha512-825JPppxDaWh0Zxb0Q+wSslgRQYOtQPCAuhszPuWEy6d2F/M+hLR+qQqvQm9+LfMbdwiTg6QK5wxdWFCp2t7jw==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-types/shared": "^3.26.0"
+                "@react-types/shared": "^3.27.0"
             },
             "peerDependencies": {
                 "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-types/overlays": {
-            "version": "3.8.11",
-            "resolved": "https://registry.npmjs.org/@react-types/overlays/-/overlays-3.8.11.tgz",
-            "integrity": "sha512-aw7T0rwVI3EuyG5AOaEIk8j7dZJQ9m34XAztXJVZ/W2+4pDDkLDbJ/EAPnuo2xGYRGhowuNDn4tDju01eHYi+w==",
+            "version": "3.8.12",
+            "resolved": "https://registry.npmjs.org/@react-types/overlays/-/overlays-3.8.12.tgz",
+            "integrity": "sha512-ZvR1t0YV7/6j+6OD8VozKYjvsXT92+C/2LOIKozy7YUNS5KI4MkXbRZzJvkuRECVZOmx8JXKTUzhghWJM/3QuQ==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-types/shared": "^3.26.0"
+                "@react-types/shared": "^3.27.0"
             },
             "peerDependencies": {
                 "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-types/progress": {
-            "version": "3.5.8",
-            "resolved": "https://registry.npmjs.org/@react-types/progress/-/progress-3.5.8.tgz",
-            "integrity": "sha512-PR0rN5mWevfblR/zs30NdZr+82Gka/ba7UHmYOW9/lkKlWeD7PHgl1iacpd/3zl/jUF22evAQbBHmk1mS6Mpqw==",
+            "version": "3.5.9",
+            "resolved": "https://registry.npmjs.org/@react-types/progress/-/progress-3.5.9.tgz",
+            "integrity": "sha512-zFxOzx3G8XUmHgpm037Hcayls5bqzXVa182E3iM7YWTmrjxJPKZ58XL0WWBgpTd+mJD7fTpnFdAZqSmFbtDOdA==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-types/shared": "^3.26.0"
+                "@react-types/shared": "^3.27.0"
             },
             "peerDependencies": {
                 "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-types/provider": {
-            "version": "3.8.5",
-            "resolved": "https://registry.npmjs.org/@react-types/provider/-/provider-3.8.5.tgz",
-            "integrity": "sha512-qK+FPNmuy5esgty8S2brOCtgB5s3IJquhhYHWV78eXJuYnJ+uDaNpJak26/OcR2ssd8iOEgNARSW0lTaut8rNQ==",
+            "version": "3.8.6",
+            "resolved": "https://registry.npmjs.org/@react-types/provider/-/provider-3.8.6.tgz",
+            "integrity": "sha512-FxqruhQugDIcpQcbXrIuuFRcxunn1dqSy2yDBDQQNOFfdL3fo6IZCflzKoGdWCXtFpbQGXApeQAGLi+/8qL0+g==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-types/shared": "^3.26.0"
+                "@react-types/shared": "^3.27.0"
             },
             "peerDependencies": {
                 "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-types/radio": {
-            "version": "3.8.5",
-            "resolved": "https://registry.npmjs.org/@react-types/radio/-/radio-3.8.5.tgz",
-            "integrity": "sha512-gSImTPid6rsbJmwCkTliBIU/npYgJHOFaI3PNJo7Y0QTAnFelCtYeFtBiWrFodSArSv7ASqpLLUEj9hZu/rxIg==",
+            "version": "3.8.6",
+            "resolved": "https://registry.npmjs.org/@react-types/radio/-/radio-3.8.6.tgz",
+            "integrity": "sha512-woTQYdRFjPzuml4qcIf+2zmycRuM5w3fDS5vk6CQmComVUjOFPtD28zX3Z9kc9lSNzaBQz9ONZfFqkZ1gqfICA==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-types/shared": "^3.26.0"
+                "@react-types/shared": "^3.27.0"
             },
             "peerDependencies": {
                 "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-types/searchfield": {
-            "version": "3.5.10",
-            "resolved": "https://registry.npmjs.org/@react-types/searchfield/-/searchfield-3.5.10.tgz",
-            "integrity": "sha512-7wW4pJzbReawoGPu8a4l+CODTCDN088EN/ysUzl622ewim57PjArjix+lpO4+aEtJqS9HKpq8UEbjwo9axpcUA==",
+            "version": "3.5.11",
+            "resolved": "https://registry.npmjs.org/@react-types/searchfield/-/searchfield-3.5.11.tgz",
+            "integrity": "sha512-MX8d9pgvxZxmgDwI0tiDaf6ijOY8XcRj0HM8Ocfttlk7PEFJK44p51WsUC+fPX1GmZni2JpFkx/haPOSLUECdw==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-types/shared": "^3.26.0",
-                "@react-types/textfield": "^3.10.0"
+                "@react-types/shared": "^3.27.0",
+                "@react-types/textfield": "^3.11.0"
             },
             "peerDependencies": {
                 "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-types/select": {
-            "version": "3.9.8",
-            "resolved": "https://registry.npmjs.org/@react-types/select/-/select-3.9.8.tgz",
-            "integrity": "sha512-RGsYj2oFjXpLnfcvWMBQnkcDuKkwT43xwYWZGI214/gp/B64tJiIUgTM5wFTRAeGDX23EePkhCQF+9ctnqFd6g==",
+            "version": "3.9.9",
+            "resolved": "https://registry.npmjs.org/@react-types/select/-/select-3.9.9.tgz",
+            "integrity": "sha512-/hCd0o+ztn29FKCmVec+v7t4JpOzz56o+KrG7NDq2pcRWqUR9kNwCjrPhSbJIIEDm4ubtrfPu41ysIuDvRd2Bg==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-types/shared": "^3.26.0"
+                "@react-types/shared": "^3.27.0"
             },
             "peerDependencies": {
                 "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-types/shared": {
-            "version": "3.26.0",
-            "resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.26.0.tgz",
-            "integrity": "sha512-6FuPqvhmjjlpEDLTiYx29IJCbCNWPlsyO+ZUmCUXzhUv2ttShOXfw8CmeHWHftT/b2KweAWuzqSlfeXPR76jpw==",
+            "version": "3.27.0",
+            "resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.27.0.tgz",
+            "integrity": "sha512-gvznmLhi6JPEf0bsq7SwRYTHAKKq/wcmKqFez9sRdbED+SPMUmK5omfZ6w3EwUFQHbYUa4zPBYedQ7Knv70RMw==",
             "license": "Apache-2.0",
             "peerDependencies": {
                 "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-types/slider": {
-            "version": "3.7.7",
-            "resolved": "https://registry.npmjs.org/@react-types/slider/-/slider-3.7.7.tgz",
-            "integrity": "sha512-lYTR9zXQV2fSEm/G3gwDENWiki1IXd/oorsgf0zu1DBi2SQDbOsLsGUXiwvD24Xy6OkUuhAqjLPPexezo7+u9g==",
+            "version": "3.7.8",
+            "resolved": "https://registry.npmjs.org/@react-types/slider/-/slider-3.7.8.tgz",
+            "integrity": "sha512-utW1o9KT70hqFwu1zqMtyEWmP0kSATk4yx+Fm/peSR4iZa+BasRqH83yzir5GKc8OfqfE1kmEsSlO98/k986+w==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-types/shared": "^3.26.0"
+                "@react-types/shared": "^3.27.0"
             },
             "peerDependencies": {
                 "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-types/statuslight": {
-            "version": "3.3.13",
-            "resolved": "https://registry.npmjs.org/@react-types/statuslight/-/statuslight-3.3.13.tgz",
-            "integrity": "sha512-qf6bGjXGHhDqoSqIZfvmaBTX9e0eDVJt+kpE0f14u0x3Hcoh7Svi6UV5vi1Wj0di+KlzAi5FlrK6Li6VM9mhPg==",
+            "version": "3.3.14",
+            "resolved": "https://registry.npmjs.org/@react-types/statuslight/-/statuslight-3.3.14.tgz",
+            "integrity": "sha512-XozxXDy3DjSkG3xkJQ6L6l5D2lpe/gWPG3gmNGox6uhk7hNRn0UBSBXp9jFnGkrEznIwLqKz988wImzPPTzs1g==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-types/shared": "^3.26.0"
+                "@react-types/shared": "^3.27.0"
             },
             "peerDependencies": {
                 "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-types/switch": {
-            "version": "3.5.7",
-            "resolved": "https://registry.npmjs.org/@react-types/switch/-/switch-3.5.7.tgz",
-            "integrity": "sha512-1IKiq510rPTHumEZuhxuazuXBa2Cuxz6wBIlwf3NCVmgWEvU+uk1ETG0sH2yymjwCqhtJDKXi+qi9HSgPEDwAg==",
+            "version": "3.5.8",
+            "resolved": "https://registry.npmjs.org/@react-types/switch/-/switch-3.5.8.tgz",
+            "integrity": "sha512-sL7jmh8llF8BxzY4HXkSU4bwU8YU6gx45P85D0AdYXgRHxU9Cp7BQPOMF4pJoQ8TTej05MymY5q7xvJVmxUTAQ==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-types/shared": "^3.26.0"
+                "@react-types/shared": "^3.27.0"
             },
             "peerDependencies": {
                 "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-types/table": {
-            "version": "3.10.3",
-            "resolved": "https://registry.npmjs.org/@react-types/table/-/table-3.10.3.tgz",
-            "integrity": "sha512-Ac+W+m/zgRzlTU8Z2GEg26HkuJFswF9S6w26r+R3MHwr8z2duGPvv37XRtE1yf3dbpRBgHEAO141xqS2TqGwNg==",
+            "version": "3.10.4",
+            "resolved": "https://registry.npmjs.org/@react-types/table/-/table-3.10.4.tgz",
+            "integrity": "sha512-d0tLz/whxVteqr1rophtuuxqyknHHfTKeXrCgDjt8pAyd9U8GPDbfcFSfYPUhWdELRt7aLVyQw6VblZHioVEgQ==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-types/grid": "^3.2.10",
-                "@react-types/shared": "^3.26.0"
+                "@react-types/grid": "^3.2.11",
+                "@react-types/shared": "^3.27.0"
             },
             "peerDependencies": {
                 "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-types/tabs": {
-            "version": "3.3.11",
-            "resolved": "https://registry.npmjs.org/@react-types/tabs/-/tabs-3.3.11.tgz",
-            "integrity": "sha512-BjF2TqBhZaIcC4lc82R5pDJd1F7kstj1K0Nokhz99AGYn8C0ITdp6lR+DPVY9JZRxKgP9R2EKfWGI90Lo7NQdA==",
+            "version": "3.3.12",
+            "resolved": "https://registry.npmjs.org/@react-types/tabs/-/tabs-3.3.12.tgz",
+            "integrity": "sha512-E9O9G+wf9kaQ8UbDEDliW/oxYlJnh7oDCW1zaMOySwnG4yeCh7Wu02EOCvlQW4xvgn/i+lbEWgirf7L+yj5nRg==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-types/shared": "^3.26.0"
+                "@react-types/shared": "^3.27.0"
             },
             "peerDependencies": {
                 "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-types/text": {
-            "version": "3.3.13",
-            "resolved": "https://registry.npmjs.org/@react-types/text/-/text-3.3.13.tgz",
-            "integrity": "sha512-u6tOXshU8PNsSgsMUj+ejmN21m5skoxkckLGwHmkieL0gvDgjnoHhLlw7TpgiAca2zQ7hAp5Zcn2TGFMgyJi5g==",
+            "version": "3.3.14",
+            "resolved": "https://registry.npmjs.org/@react-types/text/-/text-3.3.14.tgz",
+            "integrity": "sha512-4mQ39USntpO41fqtdWK0f5VJPulefZwVBYr00EPJ6wRFD0dyC37X0VyxAYfuCCwgERelvCXVvGdHxhGQe5kLLQ==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-types/shared": "^3.26.0"
+                "@react-types/shared": "^3.27.0"
             },
             "peerDependencies": {
                 "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-types/textfield": {
-            "version": "3.10.0",
-            "resolved": "https://registry.npmjs.org/@react-types/textfield/-/textfield-3.10.0.tgz",
-            "integrity": "sha512-ShU3d6kLJGQjPXccVFjM3KOXdj3uyhYROqH9YgSIEVxgA9W6LRflvk/IVBamD9pJYTPbwmVzuP0wQkTDupfZ1w==",
+            "version": "3.11.0",
+            "resolved": "https://registry.npmjs.org/@react-types/textfield/-/textfield-3.11.0.tgz",
+            "integrity": "sha512-YORBgr6wlu2xfvr4MqjKFHGpj+z8LBzk14FbWDbYnnhGnv0I10pj+m2KeOHgDNFHrfkDdDOQmMIKn1UCqeUuEg==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-types/shared": "^3.26.0"
+                "@react-types/shared": "^3.27.0"
             },
             "peerDependencies": {
                 "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-types/tooltip": {
-            "version": "3.4.13",
-            "resolved": "https://registry.npmjs.org/@react-types/tooltip/-/tooltip-3.4.13.tgz",
-            "integrity": "sha512-KPekFC17RTT8kZlk7ZYubueZnfsGTDOpLw7itzolKOXGddTXsrJGBzSB4Bb060PBVllaDO0MOrhPap8OmrIl1Q==",
+            "version": "3.4.14",
+            "resolved": "https://registry.npmjs.org/@react-types/tooltip/-/tooltip-3.4.14.tgz",
+            "integrity": "sha512-J7CeYL2yPeKIasx1rPaEefyCHGEx2DOCx+7bM3XcKGmCxvNdVQLjimNJOt8IHlUA0nFJQOjmSW/mz9P0f2/kUw==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-types/overlays": "^3.8.11",
-                "@react-types/shared": "^3.26.0"
+                "@react-types/overlays": "^3.8.12",
+                "@react-types/shared": "^3.27.0"
             },
             "peerDependencies": {
                 "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-types/view": {
-            "version": "3.4.13",
-            "resolved": "https://registry.npmjs.org/@react-types/view/-/view-3.4.13.tgz",
-            "integrity": "sha512-JvPBax8JDRExWjTbgf8hpzxnq7f70TWkQUYW50nre109zJRb0/p+v2ddMTrylI4YrizJzcMvgVgORx1+AuZUCA==",
+            "version": "3.4.14",
+            "resolved": "https://registry.npmjs.org/@react-types/view/-/view-3.4.14.tgz",
+            "integrity": "sha512-FNLKtGyRYaDhnvBCRvv3mPIAq+zyH+nKyIowXCecIrA6lzR1gFhVBRKDH+llnyd5VQdWpFsJfkEfGlq3ut6WBw==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-types/shared": "^3.26.0"
+                "@react-types/shared": "^3.27.0"
             },
             "peerDependencies": {
                 "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/@react-types/well": {
-            "version": "3.3.13",
-            "resolved": "https://registry.npmjs.org/@react-types/well/-/well-3.3.13.tgz",
-            "integrity": "sha512-O2AFQMKE3ZfQ1jygX0KJC1lLh3pnOcYeb23Q7myXJutl1rHC1gkIqEm+iLbdEdPT/QeQVxmXne7JIoaLIxU7gA==",
+            "version": "3.3.14",
+            "resolved": "https://registry.npmjs.org/@react-types/well/-/well-3.3.14.tgz",
+            "integrity": "sha512-thcg9lKyLdODmy8qUcJbYbdViPRkiiKE8EeD8H8SeASChcRnUjvvxO8yB9Z/PVdrRJQ5cgczDgbXPK5QU6apmg==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@react-types/shared": "^3.26.0"
+                "@react-types/shared": "^3.27.0"
             },
             "peerDependencies": {
                 "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
@@ -9642,12 +9684,12 @@
             }
         },
         "node_modules/@smithy/abort-controller": {
-            "version": "4.0.0",
-            "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.0.0.tgz",
-            "integrity": "sha512-xFNL1ZfluscKiVI0qlPEnu7pL1UgNNIzQdjTPkaO7JCJtIkbArPYNtqbxohuNaQdksJ01Tn1wLbDA5oIp62P8w==",
+            "version": "4.0.1",
+            "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.0.1.tgz",
+            "integrity": "sha512-fiUIYgIgRjMWznk6iLJz35K2YxSLHzLBA/RC6lBrKfQ8fHbPfvk7Pk9UvpKoHgJjI18MnbPuEju53zcVy6KF1g==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@smithy/types": "^4.0.0",
+                "@smithy/types": "^4.1.0",
                 "tslib": "^2.6.2"
             },
             "engines": {
@@ -9655,15 +9697,15 @@
             }
         },
         "node_modules/@smithy/config-resolver": {
-            "version": "4.0.0",
-            "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.0.0.tgz",
-            "integrity": "sha512-29pIDlUY/a9+ChJPAarPiD9cU8fBtBh0wFnmnhj7j5AhgMzc+uyXdfzmziH6xx2jzw54waSP3HfnFkTANZuPYA==",
+            "version": "4.0.1",
+            "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.0.1.tgz",
+            "integrity": "sha512-Igfg8lKu3dRVkTSEm98QpZUvKEOa71jDX4vKRcvJVyRc3UgN3j7vFMf0s7xLQhYmKa8kyJGQgUJDOV5V3neVlQ==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@smithy/node-config-provider": "^4.0.0",
-                "@smithy/types": "^4.0.0",
+                "@smithy/node-config-provider": "^4.0.1",
+                "@smithy/types": "^4.1.0",
                 "@smithy/util-config-provider": "^4.0.0",
-                "@smithy/util-middleware": "^4.0.0",
+                "@smithy/util-middleware": "^4.0.1",
                 "tslib": "^2.6.2"
             },
             "engines": {
@@ -9671,17 +9713,17 @@
             }
         },
         "node_modules/@smithy/core": {
-            "version": "3.0.0",
-            "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.0.0.tgz",
-            "integrity": "sha512-pKaas7RWvPljJ8uByCeBa10rtbVJCy4N/Fr7OSPxFezcyG0SQuXWnESZqzXj7m2+A+kPzG6fKyP4wrKidl2Ikg==",
+            "version": "3.1.2",
+            "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.1.2.tgz",
+            "integrity": "sha512-htwQXkbdF13uwwDevz9BEzL5ABK+1sJpVQXywwGSH973AVOvisHNfpcB8A8761G6XgHoS2kHPqc9DqHJ2gp+/Q==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@smithy/middleware-serde": "^4.0.0",
-                "@smithy/protocol-http": "^5.0.0",
-                "@smithy/types": "^4.0.0",
+                "@smithy/middleware-serde": "^4.0.2",
+                "@smithy/protocol-http": "^5.0.1",
+                "@smithy/types": "^4.1.0",
                 "@smithy/util-body-length-browser": "^4.0.0",
-                "@smithy/util-middleware": "^4.0.0",
-                "@smithy/util-stream": "^4.0.0",
+                "@smithy/util-middleware": "^4.0.1",
+                "@smithy/util-stream": "^4.0.2",
                 "@smithy/util-utf8": "^4.0.0",
                 "tslib": "^2.6.2"
             },
@@ -9690,15 +9732,15 @@
             }
         },
         "node_modules/@smithy/credential-provider-imds": {
-            "version": "4.0.0",
-            "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.0.0.tgz",
-            "integrity": "sha512-+hTShyZHiq2AVFOxJja3k6O17DKU6TaZbwr2y1OH5HQtUw2a+7O3mMR+10LVmc39ef72SAj+uFX0IW9rJGaLQQ==",
+            "version": "4.0.1",
+            "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.0.1.tgz",
+            "integrity": "sha512-l/qdInaDq1Zpznpmev/+52QomsJNZ3JkTl5yrTl02V6NBgJOQ4LY0SFw/8zsMwj3tLe8vqiIuwF6nxaEwgf6mg==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@smithy/node-config-provider": "^4.0.0",
-                "@smithy/property-provider": "^4.0.0",
-                "@smithy/types": "^4.0.0",
-                "@smithy/url-parser": "^4.0.0",
+                "@smithy/node-config-provider": "^4.0.1",
+                "@smithy/property-provider": "^4.0.1",
+                "@smithy/types": "^4.1.0",
+                "@smithy/url-parser": "^4.0.1",
                 "tslib": "^2.6.2"
             },
             "engines": {
@@ -9742,14 +9784,14 @@
             }
         },
         "node_modules/@smithy/fetch-http-handler": {
-            "version": "5.0.0",
-            "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.0.0.tgz",
-            "integrity": "sha512-jUEq+4056uqsDLRqQb1fm48rrSMBYcBxVvODfiP37ORcV5n9xWJQsINWcIffyYxWTM5K0Y/GOfhSQGDtWpAPpQ==",
+            "version": "5.0.1",
+            "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.0.1.tgz",
+            "integrity": "sha512-3aS+fP28urrMW2KTjb6z9iFow6jO8n3MFfineGbndvzGZit3taZhKWtTorf+Gp5RpFDDafeHlhfsGlDCXvUnJA==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@smithy/protocol-http": "^5.0.0",
-                "@smithy/querystring-builder": "^4.0.0",
-                "@smithy/types": "^4.0.0",
+                "@smithy/protocol-http": "^5.0.1",
+                "@smithy/querystring-builder": "^4.0.1",
+                "@smithy/types": "^4.1.0",
                 "@smithy/util-base64": "^4.0.0",
                 "tslib": "^2.6.2"
             },
@@ -9758,12 +9800,12 @@
             }
         },
         "node_modules/@smithy/hash-node": {
-            "version": "4.0.0",
-            "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.0.0.tgz",
-            "integrity": "sha512-25OxGYGnG3JPEOTk4iFE03bfmoC6GXUQ4L13z4cNdsS3mkncH22AGSDRfKwwEqutNUxXQZWVy9f72Fm59C9qlg==",
+            "version": "4.0.1",
+            "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.0.1.tgz",
+            "integrity": "sha512-TJ6oZS+3r2Xu4emVse1YPB3Dq3d8RkZDKcPr71Nj/lJsdAP1c7oFzYqEn1IBc915TsgLl2xIJNuxCz+gLbLE0w==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@smithy/types": "^4.0.0",
+                "@smithy/types": "^4.1.0",
                 "@smithy/util-buffer-from": "^4.0.0",
                 "@smithy/util-utf8": "^4.0.0",
                 "tslib": "^2.6.2"
@@ -9773,12 +9815,12 @@
             }
         },
         "node_modules/@smithy/invalid-dependency": {
-            "version": "4.0.0",
-            "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.0.0.tgz",
-            "integrity": "sha512-0GTyet02HX/sPctEhOExY+3HI7hwkVwOoJg0XnItTJ+Xw7JMuL9FOxALTmKVIV6+wg0kF6veLeg72hVSbD9UCw==",
+            "version": "4.0.1",
+            "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.0.1.tgz",
+            "integrity": "sha512-gdudFPf4QRQ5pzj7HEnu6FhKRi61BfH/Gk5Yf6O0KiSbr1LlVhgjThcvjdu658VE6Nve8vaIWB8/fodmS1rBPQ==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@smithy/types": "^4.0.0",
+                "@smithy/types": "^4.1.0",
                 "tslib": "^2.6.2"
             },
             "engines": {
@@ -9798,13 +9840,13 @@
             }
         },
         "node_modules/@smithy/middleware-content-length": {
-            "version": "4.0.0",
-            "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.0.0.tgz",
-            "integrity": "sha512-nM1RJqLwkSCidumGK8WwNEZ0a0D/4LkwqdPna+QmHrdPoAK6WGLyZFosdMpsAW1OIbDLWGa+r37Mo4Vth4S4kQ==",
+            "version": "4.0.1",
+            "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.0.1.tgz",
+            "integrity": "sha512-OGXo7w5EkB5pPiac7KNzVtfCW2vKBTZNuCctn++TTSOMpe6RZO/n6WEC1AxJINn3+vWLKW49uad3lo/u0WJ9oQ==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@smithy/protocol-http": "^5.0.0",
-                "@smithy/types": "^4.0.0",
+                "@smithy/protocol-http": "^5.0.1",
+                "@smithy/types": "^4.1.0",
                 "tslib": "^2.6.2"
             },
             "engines": {
@@ -9812,18 +9854,18 @@
             }
         },
         "node_modules/@smithy/middleware-endpoint": {
-            "version": "4.0.0",
-            "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.0.0.tgz",
-            "integrity": "sha512-/f6z5SqUurmqemhBZNhM0c+C7QW0AY/zJpic//sbdu26q98HSPAI/xvzStjYq+UhtWeAe/jaX6gamdL/2r3W1g==",
+            "version": "4.0.3",
+            "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.0.3.tgz",
+            "integrity": "sha512-YdbmWhQF5kIxZjWqPIgboVfi8i5XgiYMM7GGKFMTvBei4XjNQfNv8sukT50ITvgnWKKKpOtp0C0h7qixLgb77Q==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@smithy/core": "^3.0.0",
-                "@smithy/middleware-serde": "^4.0.0",
-                "@smithy/node-config-provider": "^4.0.0",
-                "@smithy/shared-ini-file-loader": "^4.0.0",
-                "@smithy/types": "^4.0.0",
-                "@smithy/url-parser": "^4.0.0",
-                "@smithy/util-middleware": "^4.0.0",
+                "@smithy/core": "^3.1.2",
+                "@smithy/middleware-serde": "^4.0.2",
+                "@smithy/node-config-provider": "^4.0.1",
+                "@smithy/shared-ini-file-loader": "^4.0.1",
+                "@smithy/types": "^4.1.0",
+                "@smithy/url-parser": "^4.0.1",
+                "@smithy/util-middleware": "^4.0.1",
                 "tslib": "^2.6.2"
             },
             "engines": {
@@ -9831,18 +9873,18 @@
             }
         },
         "node_modules/@smithy/middleware-retry": {
-            "version": "4.0.0",
-            "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.0.0.tgz",
-            "integrity": "sha512-K6tsFp3Ik44H3694a+LWoXLV8mqy8zn6/vTw2feU72MaIzi51EHMVNNxxpL6e2GI6oxw8FFRGWgGn8+wQRrHZQ==",
+            "version": "4.0.4",
+            "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.0.4.tgz",
+            "integrity": "sha512-wmxyUBGHaYUqul0wZiset4M39SMtDBOtUr2KpDuftKNN74Do9Y36Go6Eqzj9tL0mIPpr31ulB5UUtxcsCeGXsQ==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@smithy/node-config-provider": "^4.0.0",
-                "@smithy/protocol-http": "^5.0.0",
-                "@smithy/service-error-classification": "^4.0.0",
-                "@smithy/smithy-client": "^4.0.0",
-                "@smithy/types": "^4.0.0",
-                "@smithy/util-middleware": "^4.0.0",
-                "@smithy/util-retry": "^4.0.0",
+                "@smithy/node-config-provider": "^4.0.1",
+                "@smithy/protocol-http": "^5.0.1",
+                "@smithy/service-error-classification": "^4.0.1",
+                "@smithy/smithy-client": "^4.1.3",
+                "@smithy/types": "^4.1.0",
+                "@smithy/util-middleware": "^4.0.1",
+                "@smithy/util-retry": "^4.0.1",
                 "tslib": "^2.6.2",
                 "uuid": "^9.0.1"
             },
@@ -9864,12 +9906,12 @@
             }
         },
         "node_modules/@smithy/middleware-serde": {
-            "version": "4.0.0",
-            "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.0.0.tgz",
-            "integrity": "sha512-aW4Zo8Cm988RCvhysErzqrQ4YPKgZFhajvgPoZnsWIDaZfT419J17Ahr13Lul3kqGad2dCz7YOrXd7r+UAEj/w==",
+            "version": "4.0.2",
+            "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.0.2.tgz",
+            "integrity": "sha512-Sdr5lOagCn5tt+zKsaW+U2/iwr6bI9p08wOkCp6/eL6iMbgdtc2R5Ety66rf87PeohR0ExI84Txz9GYv5ou3iQ==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@smithy/types": "^4.0.0",
+                "@smithy/types": "^4.1.0",
                 "tslib": "^2.6.2"
             },
             "engines": {
@@ -9877,12 +9919,12 @@
             }
         },
         "node_modules/@smithy/middleware-stack": {
-            "version": "4.0.0",
-            "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.0.0.tgz",
-            "integrity": "sha512-4NFaX88RmgVrCyJv/3RsSdqMwxzI/EQa8nvhUDVxmLUMRS2JUdHnliD6IwKuqIwIzz+E1aZK3EhSHUM4HXp3ww==",
+            "version": "4.0.1",
+            "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.0.1.tgz",
+            "integrity": "sha512-dHwDmrtR/ln8UTHpaIavRSzeIk5+YZTBtLnKwDW3G2t6nAupCiQUvNzNoHBpik63fwUaJPtlnMzXbQrNFWssIA==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@smithy/types": "^4.0.0",
+                "@smithy/types": "^4.1.0",
                 "tslib": "^2.6.2"
             },
             "engines": {
@@ -9890,14 +9932,14 @@
             }
         },
         "node_modules/@smithy/node-config-provider": {
-            "version": "4.0.0",
-            "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.0.0.tgz",
-            "integrity": "sha512-Crp9rg1ewjqgM2i7pWSpNhfbBa0usyKGDVQLEXTOpu6trFqq3BFLLCgbCE1S18h6mxqKnOqUONq3nWOxUk75XA==",
+            "version": "4.0.1",
+            "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.0.1.tgz",
+            "integrity": "sha512-8mRTjvCtVET8+rxvmzRNRR0hH2JjV0DFOmwXPrISmTIJEfnCBugpYYGAsCj8t41qd+RB5gbheSQ/6aKZCQvFLQ==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@smithy/property-provider": "^4.0.0",
-                "@smithy/shared-ini-file-loader": "^4.0.0",
-                "@smithy/types": "^4.0.0",
+                "@smithy/property-provider": "^4.0.1",
+                "@smithy/shared-ini-file-loader": "^4.0.1",
+                "@smithy/types": "^4.1.0",
                 "tslib": "^2.6.2"
             },
             "engines": {
@@ -9905,15 +9947,15 @@
             }
         },
         "node_modules/@smithy/node-http-handler": {
-            "version": "4.0.0",
-            "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.0.0.tgz",
-            "integrity": "sha512-WvumtEaFyxaI95zmj6eYlF/vCFCKNyru3P/UUHCUS9BjvajUtNckH2cY3bBfi+qqMPX5gha4g26lcOlE/wPz/Q==",
+            "version": "4.0.2",
+            "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.0.2.tgz",
+            "integrity": "sha512-X66H9aah9hisLLSnGuzRYba6vckuFtGE+a5DcHLliI/YlqKrGoxhisD5XbX44KyoeRzoNlGr94eTsMVHFAzPOw==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@smithy/abort-controller": "^4.0.0",
-                "@smithy/protocol-http": "^5.0.0",
-                "@smithy/querystring-builder": "^4.0.0",
-                "@smithy/types": "^4.0.0",
+                "@smithy/abort-controller": "^4.0.1",
+                "@smithy/protocol-http": "^5.0.1",
+                "@smithy/querystring-builder": "^4.0.1",
+                "@smithy/types": "^4.1.0",
                 "tslib": "^2.6.2"
             },
             "engines": {
@@ -9921,12 +9963,12 @@
             }
         },
         "node_modules/@smithy/property-provider": {
-            "version": "4.0.0",
-            "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.0.0.tgz",
-            "integrity": "sha512-AJSvY1k3SdM0stGrIjL8/FIjXO7X9I7KkznXDmr76RGz+yvaDHLsLm2hSHyzAlmwEQnHaafSU2dwaV0JcnR/4w==",
+            "version": "4.0.1",
+            "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.0.1.tgz",
+            "integrity": "sha512-o+VRiwC2cgmk/WFV0jaETGOtX16VNPp2bSQEzu0whbReqE1BMqsP2ami2Vi3cbGVdKu1kq9gQkDAGKbt0WOHAQ==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@smithy/types": "^4.0.0",
+                "@smithy/types": "^4.1.0",
                 "tslib": "^2.6.2"
             },
             "engines": {
@@ -9934,12 +9976,12 @@
             }
         },
         "node_modules/@smithy/protocol-http": {
-            "version": "5.0.0",
-            "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.0.0.tgz",
-            "integrity": "sha512-laAcIHWq9GQ5VdAS71DUrCj5HUHZ/89Ee+HRTLhFR5/E3toBlnZfPG+kqBajwfEB5aSdRuKslfzl5Dzrn3pr8A==",
+            "version": "5.0.1",
+            "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.0.1.tgz",
+            "integrity": "sha512-TE4cpj49jJNB/oHyh/cRVEgNZaoPaxd4vteJNB0yGidOCVR0jCw/hjPVsT8Q8FRmj8Bd3bFZt8Dh7xGCT+xMBQ==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@smithy/types": "^4.0.0",
+                "@smithy/types": "^4.1.0",
                 "tslib": "^2.6.2"
             },
             "engines": {
@@ -9947,12 +9989,12 @@
             }
         },
         "node_modules/@smithy/querystring-builder": {
-            "version": "4.0.0",
-            "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.0.0.tgz",
-            "integrity": "sha512-kMqPDRf+/hwm+Dmk8AQCaYTJxNWWpNdJJteeMm0jwDbmRDqSqHQ7oLEVzvOnbWJu1poVtOhv6v7jsbyx9JASsw==",
+            "version": "4.0.1",
+            "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.0.1.tgz",
+            "integrity": "sha512-wU87iWZoCbcqrwszsOewEIuq+SU2mSoBE2CcsLwE0I19m0B2gOJr1MVjxWcDQYOzHbR1xCk7AcOBbGFUYOKvdg==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@smithy/types": "^4.0.0",
+                "@smithy/types": "^4.1.0",
                 "@smithy/util-uri-escape": "^4.0.0",
                 "tslib": "^2.6.2"
             },
@@ -9961,12 +10003,12 @@
             }
         },
         "node_modules/@smithy/querystring-parser": {
-            "version": "4.0.0",
-            "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.0.0.tgz",
-            "integrity": "sha512-SbogL1PNEmm28ya0eK2S0EZEbYwe0qpaqSGrODm+uYS6dQ7pekPLVNXjBRuuLIAT26ZF2wTsp6X7AVRBNZd8qw==",
+            "version": "4.0.1",
+            "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.0.1.tgz",
+            "integrity": "sha512-Ma2XC7VS9aV77+clSFylVUnPZRindhB7BbmYiNOdr+CHt/kZNJoPP0cd3QxCnCFyPXC4eybmyE98phEHkqZ5Jw==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@smithy/types": "^4.0.0",
+                "@smithy/types": "^4.1.0",
                 "tslib": "^2.6.2"
             },
             "engines": {
@@ -9974,24 +10016,24 @@
             }
         },
         "node_modules/@smithy/service-error-classification": {
-            "version": "4.0.0",
-            "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.0.0.tgz",
-            "integrity": "sha512-hIZreT6aXSG0PK/psT1S+kfeGTnYnRRlf7rU3yDmH/crSVjTbS/5h5w2J7eO2ODrQb3xfhJcYxQBREdwsZk6TA==",
+            "version": "4.0.1",
+            "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.0.1.tgz",
+            "integrity": "sha512-3JNjBfOWpj/mYfjXJHB4Txc/7E4LVq32bwzE7m28GN79+M1f76XHflUaSUkhOriprPDzev9cX/M+dEB80DNDKA==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@smithy/types": "^4.0.0"
+                "@smithy/types": "^4.1.0"
             },
             "engines": {
                 "node": ">=18.0.0"
             }
         },
         "node_modules/@smithy/shared-ini-file-loader": {
-            "version": "4.0.0",
-            "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.0.0.tgz",
-            "integrity": "sha512-Ktupe8msp2GPaKKVfiz3NNUNnslJiGGRoVh3BDpm/RChkQ5INQpqmTc2taE0XChNYumNynLfb3keekIPaiaZeg==",
+            "version": "4.0.1",
+            "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.0.1.tgz",
+            "integrity": "sha512-hC8F6qTBbuHRI/uqDgqqi6J0R4GtEZcgrZPhFQnMhfJs3MnUTGSnR1NSJCJs5VWlMydu0kJz15M640fJlRsIOw==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@smithy/types": "^4.0.0",
+                "@smithy/types": "^4.1.0",
                 "tslib": "^2.6.2"
             },
             "engines": {
@@ -9999,16 +10041,16 @@
             }
         },
         "node_modules/@smithy/signature-v4": {
-            "version": "5.0.0",
-            "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.0.0.tgz",
-            "integrity": "sha512-zqcOR1sZTuoA6K3PBNwzu4YgT1pmIwz47tYpgaJjBTfGUIMtcjUaXKtuSKEScdv+0wx45/PbXz0//hk80fky3w==",
+            "version": "5.0.1",
+            "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.0.1.tgz",
+            "integrity": "sha512-nCe6fQ+ppm1bQuw5iKoeJ0MJfz2os7Ic3GBjOkLOPtavbD1ONoyE3ygjBfz2ythFWm4YnRm6OxW+8p/m9uCoIA==",
             "license": "Apache-2.0",
             "dependencies": {
                 "@smithy/is-array-buffer": "^4.0.0",
-                "@smithy/protocol-http": "^5.0.0",
-                "@smithy/types": "^4.0.0",
+                "@smithy/protocol-http": "^5.0.1",
+                "@smithy/types": "^4.1.0",
                 "@smithy/util-hex-encoding": "^4.0.0",
-                "@smithy/util-middleware": "^4.0.0",
+                "@smithy/util-middleware": "^4.0.1",
                 "@smithy/util-uri-escape": "^4.0.0",
                 "@smithy/util-utf8": "^4.0.0",
                 "tslib": "^2.6.2"
@@ -10018,17 +10060,17 @@
             }
         },
         "node_modules/@smithy/smithy-client": {
-            "version": "4.0.0",
-            "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.0.0.tgz",
-            "integrity": "sha512-AgcZ6B+JuqArYioAbaYrCpTCjYsD3/1hPSXntbN2ipsfc4hE+72RFZevUPYgsKxpy3G+QxuLfqm11i3+oX4oSA==",
+            "version": "4.1.3",
+            "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.1.3.tgz",
+            "integrity": "sha512-A2Hz85pu8BJJaYFdX8yb1yocqigyqBzn+OVaVgm+Kwi/DkN8vhN2kbDVEfADo6jXf5hPKquMLGA3UINA64UZ7A==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@smithy/core": "^3.0.0",
-                "@smithy/middleware-endpoint": "^4.0.0",
-                "@smithy/middleware-stack": "^4.0.0",
-                "@smithy/protocol-http": "^5.0.0",
-                "@smithy/types": "^4.0.0",
-                "@smithy/util-stream": "^4.0.0",
+                "@smithy/core": "^3.1.2",
+                "@smithy/middleware-endpoint": "^4.0.3",
+                "@smithy/middleware-stack": "^4.0.1",
+                "@smithy/protocol-http": "^5.0.1",
+                "@smithy/types": "^4.1.0",
+                "@smithy/util-stream": "^4.0.2",
                 "tslib": "^2.6.2"
             },
             "engines": {
@@ -10036,9 +10078,9 @@
             }
         },
         "node_modules/@smithy/types": {
-            "version": "4.0.0",
-            "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.0.0.tgz",
-            "integrity": "sha512-aNwIGSOgDOhtTRY/rrn2aeuQeKw/IFrQ998yK5l6Ah853WeWIEmFPs/EO4OpfADEdcK+igWnZytm/oUgkLgUYg==",
+            "version": "4.1.0",
+            "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.1.0.tgz",
+            "integrity": "sha512-enhjdwp4D7CXmwLtD6zbcDMbo6/T6WtuuKCY49Xxc6OMOmUWlBEBDREsxxgV2LIdeQPW756+f97GzcgAwp3iLw==",
             "license": "Apache-2.0",
             "dependencies": {
                 "tslib": "^2.6.2"
@@ -10048,13 +10090,13 @@
             }
         },
         "node_modules/@smithy/url-parser": {
-            "version": "4.0.0",
-            "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.0.0.tgz",
-            "integrity": "sha512-2iPpuLoH0hCKpLtqVgilHtpPKsmHihbkwBm3h3RPuEctdmuiOlFRZ2ZI8IHSwl0o4ff5IdyyJ0yu/2tS9KpUug==",
+            "version": "4.0.1",
+            "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.0.1.tgz",
+            "integrity": "sha512-gPXcIEUtw7VlK8f/QcruNXm7q+T5hhvGu9tl63LsJPZ27exB6dtNwvh2HIi0v7JcXJ5emBxB+CJxwaLEdJfA+g==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@smithy/querystring-parser": "^4.0.0",
-                "@smithy/types": "^4.0.0",
+                "@smithy/querystring-parser": "^4.0.1",
+                "@smithy/types": "^4.1.0",
                 "tslib": "^2.6.2"
             },
             "engines": {
@@ -10125,14 +10167,14 @@
             }
         },
         "node_modules/@smithy/util-defaults-mode-browser": {
-            "version": "4.0.0",
-            "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.0.0.tgz",
-            "integrity": "sha512-7wqsXkzaJkpSqV+Ca95pN9yQutXvhaKeCxGGmjWnRGXY1fW/yR7wr1ouNnUYCJuTS8MvmB61xp5Qdj8YMgIA2Q==",
+            "version": "4.0.4",
+            "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.0.4.tgz",
+            "integrity": "sha512-Ej1bV5sbrIfH++KnWxjjzFNq9nyP3RIUq2c9Iqq7SmMO/idUR24sqvKH2LUQFTSPy/K7G4sB2m8n7YYlEAfZaw==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@smithy/property-provider": "^4.0.0",
-                "@smithy/smithy-client": "^4.0.0",
-                "@smithy/types": "^4.0.0",
+                "@smithy/property-provider": "^4.0.1",
+                "@smithy/smithy-client": "^4.1.3",
+                "@smithy/types": "^4.1.0",
                 "bowser": "^2.11.0",
                 "tslib": "^2.6.2"
             },
@@ -10141,17 +10183,17 @@
             }
         },
         "node_modules/@smithy/util-defaults-mode-node": {
-            "version": "4.0.0",
-            "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.0.0.tgz",
-            "integrity": "sha512-P8VK885kiRT6TEtvcQvz+L/+xIhrDhCmM664ToUtrshFSBhwGYaJWlQNAH9fXlMhwnNvR+tmh1KngKJIgQP6bw==",
+            "version": "4.0.4",
+            "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.0.4.tgz",
+            "integrity": "sha512-HE1I7gxa6yP7ZgXPCFfZSDmVmMtY7SHqzFF55gM/GPegzZKaQWZZ+nYn9C2Cc3JltCMyWe63VPR3tSFDEvuGjw==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@smithy/config-resolver": "^4.0.0",
-                "@smithy/credential-provider-imds": "^4.0.0",
-                "@smithy/node-config-provider": "^4.0.0",
-                "@smithy/property-provider": "^4.0.0",
-                "@smithy/smithy-client": "^4.0.0",
-                "@smithy/types": "^4.0.0",
+                "@smithy/config-resolver": "^4.0.1",
+                "@smithy/credential-provider-imds": "^4.0.1",
+                "@smithy/node-config-provider": "^4.0.1",
+                "@smithy/property-provider": "^4.0.1",
+                "@smithy/smithy-client": "^4.1.3",
+                "@smithy/types": "^4.1.0",
                 "tslib": "^2.6.2"
             },
             "engines": {
@@ -10159,13 +10201,13 @@
             }
         },
         "node_modules/@smithy/util-endpoints": {
-            "version": "3.0.0",
-            "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.0.0.tgz",
-            "integrity": "sha512-kyOKbkg77lsIVN2jC08uEWm3s16eK1YdVDyi/nKeBDbUnjR30dmTEga79E5tiu5OEgTAdngNswA9V+L6xa65sA==",
+            "version": "3.0.1",
+            "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.0.1.tgz",
+            "integrity": "sha512-zVdUENQpdtn9jbpD9SCFK4+aSiavRb9BxEtw9ZGUR1TYo6bBHbIoi7VkrFQ0/RwZlzx0wRBaRmPclj8iAoJCLA==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@smithy/node-config-provider": "^4.0.0",
-                "@smithy/types": "^4.0.0",
+                "@smithy/node-config-provider": "^4.0.1",
+                "@smithy/types": "^4.1.0",
                 "tslib": "^2.6.2"
             },
             "engines": {
@@ -10185,12 +10227,12 @@
             }
         },
         "node_modules/@smithy/util-middleware": {
-            "version": "4.0.0",
-            "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.0.0.tgz",
-            "integrity": "sha512-ncuvK6ekpDqtASHg7jx3d3nrkD2BsTzUmeVgvtepuHGxtySY8qUlb4SiNRdxHYcv3pL2SwdXs70RwKBU0edW5w==",
+            "version": "4.0.1",
+            "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.0.1.tgz",
+            "integrity": "sha512-HiLAvlcqhbzhuiOa0Lyct5IIlyIz0PQO5dnMlmQ/ubYM46dPInB+3yQGkfxsk6Q24Y0n3/JmcA1v5iEhmOF5mA==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@smithy/types": "^4.0.0",
+                "@smithy/types": "^4.1.0",
                 "tslib": "^2.6.2"
             },
             "engines": {
@@ -10198,13 +10240,13 @@
             }
         },
         "node_modules/@smithy/util-retry": {
-            "version": "4.0.0",
-            "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.0.0.tgz",
-            "integrity": "sha512-64WFoC19NVuHh3HQO2QbGw+n6GzQ6VH/drxwXLOU3GDLKxUUzIR9XNm9aTVqh8/7R+y+DgITiv5LpX5XdOy73A==",
+            "version": "4.0.1",
+            "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.0.1.tgz",
+            "integrity": "sha512-WmRHqNVwn3kI3rKk1LsKcVgPBG6iLTBGC1iYOV3GQegwJ3E8yjzHytPt26VNzOWr1qu0xE03nK0Ug8S7T7oufw==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@smithy/service-error-classification": "^4.0.0",
-                "@smithy/types": "^4.0.0",
+                "@smithy/service-error-classification": "^4.0.1",
+                "@smithy/types": "^4.1.0",
                 "tslib": "^2.6.2"
             },
             "engines": {
@@ -10212,14 +10254,14 @@
             }
         },
         "node_modules/@smithy/util-stream": {
-            "version": "4.0.0",
-            "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.0.0.tgz",
-            "integrity": "sha512-ctcLq8Ogi2FQuGy2RxJXGGrozhFEb4p9FawB5SpTNAkNQWbNHcwrGcVSVI3FtdQtkNAINLiEdMnrx+UN/mafvw==",
+            "version": "4.0.2",
+            "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.0.2.tgz",
+            "integrity": "sha512-0eZ4G5fRzIoewtHtwaYyl8g2C+osYOT4KClXgfdNEDAgkbe2TYPqcnw4GAWabqkZCax2ihRGPe9LZnsPdIUIHA==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@smithy/fetch-http-handler": "^5.0.0",
-                "@smithy/node-http-handler": "^4.0.0",
-                "@smithy/types": "^4.0.0",
+                "@smithy/fetch-http-handler": "^5.0.1",
+                "@smithy/node-http-handler": "^4.0.2",
+                "@smithy/types": "^4.1.0",
                 "@smithy/util-base64": "^4.0.0",
                 "@smithy/util-buffer-from": "^4.0.0",
                 "@smithy/util-hex-encoding": "^4.0.0",
@@ -10256,13 +10298,13 @@
             }
         },
         "node_modules/@smithy/util-waiter": {
-            "version": "4.0.1",
-            "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-4.0.1.tgz",
-            "integrity": "sha512-PiLI5OMSDOw0IyA+lB8Ta0CIgaJuIyXE97Khvrk1G1ab71WbMTruzLKP9j3nS6QJScGirkUVN9sTOhZHU8q3OQ==",
+            "version": "4.0.2",
+            "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-4.0.2.tgz",
+            "integrity": "sha512-piUTHyp2Axx3p/kc2CIJkYSv0BAaheBQmbACZgQSSfWUumWNW+R1lL+H9PDBxKJkvOeEX+hKYEFiwO8xagL8AQ==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@smithy/abort-controller": "^4.0.0",
-                "@smithy/types": "^4.0.0",
+                "@smithy/abort-controller": "^4.0.1",
+                "@smithy/types": "^4.1.0",
                 "tslib": "^2.6.2"
             },
             "engines": {
@@ -10276,13 +10318,13 @@
             "license": "MIT"
         },
         "node_modules/@spectrum-icons/ui": {
-            "version": "3.6.11",
-            "resolved": "https://registry.npmjs.org/@spectrum-icons/ui/-/ui-3.6.11.tgz",
-            "integrity": "sha512-5qC5F3Lf947gPv/nkwbmxr6syTha++Oyn0KWAecFrg5BQ/Yapgh+EEp02GOcdE2NggNPCOW3PLpO4HrbCCPELw==",
+            "version": "3.6.12",
+            "resolved": "https://registry.npmjs.org/@spectrum-icons/ui/-/ui-3.6.12.tgz",
+            "integrity": "sha512-T4xlR99YAc2SJh7P3BMYTpFRspd+Kxw+o/CD7FMwTwVNvBMIr86T3RDseF0yl0pcByLRNS49GXUXxVki1X+ntg==",
             "license": "Apache-2.0",
             "dependencies": {
                 "@adobe/react-spectrum-ui": "1.2.1",
-                "@react-spectrum/icon": "^3.8.0",
+                "@react-spectrum/icon": "^3.8.1",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -10301,13 +10343,13 @@
             }
         },
         "node_modules/@spectrum-icons/workflow": {
-            "version": "4.2.16",
-            "resolved": "https://registry.npmjs.org/@spectrum-icons/workflow/-/workflow-4.2.16.tgz",
-            "integrity": "sha512-/VdS/waRvLiSzzb+4J7EzVpGgEbjDKQqYVYrKeTjyzumM0WX2Ylfa1qQajCpfYOEIFMzZTt7lZ8/O8qgVRArLA==",
+            "version": "4.2.17",
+            "resolved": "https://registry.npmjs.org/@spectrum-icons/workflow/-/workflow-4.2.17.tgz",
+            "integrity": "sha512-3+VIxnAISQHsqukSaoQ11C/eNrUjrFoQEWbU/TBcy59JiJtxPGvEY9WfSo94rx3lh8r4Zuca/MbaKBmZ6mtrPg==",
             "license": "Apache-2.0",
             "dependencies": {
                 "@adobe/react-spectrum-workflow": "2.3.5",
-                "@react-spectrum/icon": "^3.8.0",
+                "@react-spectrum/icon": "^3.8.1",
                 "@swc/helpers": "^0.5.0"
             },
             "peerDependencies": {
@@ -10326,9 +10368,9 @@
             }
         },
         "node_modules/@storybook/addon-actions": {
-            "version": "8.4.7",
-            "resolved": "https://registry.npmjs.org/@storybook/addon-actions/-/addon-actions-8.4.7.tgz",
-            "integrity": "sha512-mjtD5JxcPuW74T6h7nqMxWTvDneFtokg88p6kQ5OnC1M259iAXb//yiSZgu/quunMHPCXSiqn4FNOSgASTSbsA==",
+            "version": "8.5.2",
+            "resolved": "https://registry.npmjs.org/@storybook/addon-actions/-/addon-actions-8.5.2.tgz",
+            "integrity": "sha512-g0gLesVSFgstUq5QphsLeC1vEdwNHgqo2TE0m+STM47832xbxBwmK6uvBeqi416xZvnt1TTKaaBr4uCRRQ64Ww==",
             "dev": true,
             "license": "MIT",
             "dependencies": {
@@ -10343,7 +10385,7 @@
                 "url": "https://opencollective.com/storybook"
             },
             "peerDependencies": {
-                "storybook": "^8.4.7"
+                "storybook": "^8.5.2"
             }
         },
         "node_modules/@storybook/addon-actions/node_modules/@types/uuid": {
@@ -10368,9 +10410,9 @@
             }
         },
         "node_modules/@storybook/addon-backgrounds": {
-            "version": "8.4.7",
-            "resolved": "https://registry.npmjs.org/@storybook/addon-backgrounds/-/addon-backgrounds-8.4.7.tgz",
-            "integrity": "sha512-I4/aErqtFiazcoWyKafOAm3bLpxTj6eQuH/woSbk1Yx+EzN+Dbrgx1Updy8//bsNtKkcrXETITreqHC+a57DHQ==",
+            "version": "8.5.2",
+            "resolved": "https://registry.npmjs.org/@storybook/addon-backgrounds/-/addon-backgrounds-8.5.2.tgz",
+            "integrity": "sha512-l9WkI4QHfINeFQkW9K0joaM7WweKktwIIyUPEvyoupHT4n9ccJHAlWjH4SBmzwI1j1Zt0G3t+bq8mVk/YK6Fsg==",
             "dev": true,
             "license": "MIT",
             "dependencies": {
@@ -10383,13 +10425,13 @@
                 "url": "https://opencollective.com/storybook"
             },
             "peerDependencies": {
-                "storybook": "^8.4.7"
+                "storybook": "^8.5.2"
             }
         },
         "node_modules/@storybook/addon-controls": {
-            "version": "8.4.7",
-            "resolved": "https://registry.npmjs.org/@storybook/addon-controls/-/addon-controls-8.4.7.tgz",
-            "integrity": "sha512-377uo5IsJgXLnQLJixa47+11V+7Wn9KcDEw+96aGCBCfLbWNH8S08tJHHnSu+jXg9zoqCAC23MetntVp6LetHA==",
+            "version": "8.5.2",
+            "resolved": "https://registry.npmjs.org/@storybook/addon-controls/-/addon-controls-8.5.2.tgz",
+            "integrity": "sha512-wkzw2vRff4zkzdvC/GOlB2PlV0i973u8igSLeg34TWNEAa4bipwVHnFfIojRuP9eN1bZL/0tjuU5pKnbTqH7aQ==",
             "dev": true,
             "license": "MIT",
             "dependencies": {
@@ -10402,20 +10444,20 @@
                 "url": "https://opencollective.com/storybook"
             },
             "peerDependencies": {
-                "storybook": "^8.4.7"
+                "storybook": "^8.5.2"
             }
         },
         "node_modules/@storybook/addon-docs": {
-            "version": "8.4.7",
-            "resolved": "https://registry.npmjs.org/@storybook/addon-docs/-/addon-docs-8.4.7.tgz",
-            "integrity": "sha512-NwWaiTDT5puCBSUOVuf6ME7Zsbwz7Y79WF5tMZBx/sLQ60vpmJVQsap6NSjvK1Ravhc21EsIXqemAcBjAWu80w==",
+            "version": "8.5.2",
+            "resolved": "https://registry.npmjs.org/@storybook/addon-docs/-/addon-docs-8.5.2.tgz",
+            "integrity": "sha512-pRLJ/Qb/3XHpjS7ZAMaOZYtqxOuI8wPxVKYQ6n5rfMSj2jFwt5tdDsEJdhj2t5lsY8HrzEZi8ExuW5I5RoUoIQ==",
             "dev": true,
             "license": "MIT",
             "dependencies": {
                 "@mdx-js/react": "^3.0.0",
-                "@storybook/blocks": "8.4.7",
-                "@storybook/csf-plugin": "8.4.7",
-                "@storybook/react-dom-shim": "8.4.7",
+                "@storybook/blocks": "8.5.2",
+                "@storybook/csf-plugin": "8.5.2",
+                "@storybook/react-dom-shim": "8.5.2",
                 "react": "^16.8.0 || ^17.0.0 || ^18.0.0",
                 "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0",
                 "ts-dedent": "^2.0.0"
@@ -10425,25 +10467,25 @@
                 "url": "https://opencollective.com/storybook"
             },
             "peerDependencies": {
-                "storybook": "^8.4.7"
+                "storybook": "^8.5.2"
             }
         },
         "node_modules/@storybook/addon-essentials": {
-            "version": "8.4.7",
-            "resolved": "https://registry.npmjs.org/@storybook/addon-essentials/-/addon-essentials-8.4.7.tgz",
-            "integrity": "sha512-+BtZHCBrYtQKILtejKxh0CDRGIgTl9PumfBOKRaihYb4FX1IjSAxoV/oo/IfEjlkF5f87vouShWsRa8EUauFDw==",
+            "version": "8.5.2",
+            "resolved": "https://registry.npmjs.org/@storybook/addon-essentials/-/addon-essentials-8.5.2.tgz",
+            "integrity": "sha512-MfojJKxDg0bnjOE0MfLSaPweAud1Esjaf1D9M8EYnpeFnKGZApcGJNRpHCDiHrS5BMr8hHa58RDVc7ObFTI4Dw==",
             "dev": true,
             "license": "MIT",
             "dependencies": {
-                "@storybook/addon-actions": "8.4.7",
-                "@storybook/addon-backgrounds": "8.4.7",
-                "@storybook/addon-controls": "8.4.7",
-                "@storybook/addon-docs": "8.4.7",
-                "@storybook/addon-highlight": "8.4.7",
-                "@storybook/addon-measure": "8.4.7",
-                "@storybook/addon-outline": "8.4.7",
-                "@storybook/addon-toolbars": "8.4.7",
-                "@storybook/addon-viewport": "8.4.7",
+                "@storybook/addon-actions": "8.5.2",
+                "@storybook/addon-backgrounds": "8.5.2",
+                "@storybook/addon-controls": "8.5.2",
+                "@storybook/addon-docs": "8.5.2",
+                "@storybook/addon-highlight": "8.5.2",
+                "@storybook/addon-measure": "8.5.2",
+                "@storybook/addon-outline": "8.5.2",
+                "@storybook/addon-toolbars": "8.5.2",
+                "@storybook/addon-viewport": "8.5.2",
                 "ts-dedent": "^2.0.0"
             },
             "funding": {
@@ -10451,13 +10493,13 @@
                 "url": "https://opencollective.com/storybook"
             },
             "peerDependencies": {
-                "storybook": "^8.4.7"
+                "storybook": "^8.5.2"
             }
         },
         "node_modules/@storybook/addon-highlight": {
-            "version": "8.4.7",
-            "resolved": "https://registry.npmjs.org/@storybook/addon-highlight/-/addon-highlight-8.4.7.tgz",
-            "integrity": "sha512-whQIDBd3PfVwcUCrRXvCUHWClXe9mQ7XkTPCdPo4B/tZ6Z9c6zD8JUHT76ddyHivixFLowMnA8PxMU6kCMAiNw==",
+            "version": "8.5.2",
+            "resolved": "https://registry.npmjs.org/@storybook/addon-highlight/-/addon-highlight-8.5.2.tgz",
+            "integrity": "sha512-QjJfY+8e1bi6FeGfVlgxzv/I8DUyC83lZq8zfTY7nDUCVdmKi8VzmW0KgDo5PaEOFKs8x6LKJa+s5O0gFQaJMw==",
             "dev": true,
             "license": "MIT",
             "dependencies": {
@@ -10468,19 +10510,19 @@
                 "url": "https://opencollective.com/storybook"
             },
             "peerDependencies": {
-                "storybook": "^8.4.7"
+                "storybook": "^8.5.2"
             }
         },
         "node_modules/@storybook/addon-interactions": {
-            "version": "8.4.7",
-            "resolved": "https://registry.npmjs.org/@storybook/addon-interactions/-/addon-interactions-8.4.7.tgz",
-            "integrity": "sha512-fnufT3ym8ht3HHUIRVXAH47iOJW/QOb0VSM+j269gDuvyDcY03D1civCu1v+eZLGaXPKJ8vtjr0L8zKQ/4P0JQ==",
+            "version": "8.5.2",
+            "resolved": "https://registry.npmjs.org/@storybook/addon-interactions/-/addon-interactions-8.5.2.tgz",
+            "integrity": "sha512-Gn9Egk2OS0BkkHd671Y0pIqBr4noAOLUfnpxhHE8r0Tt7FmJFeVSN+dqK7hQeUmKL5jdSY25FTYROg65JmtGOA==",
             "dev": true,
             "license": "MIT",
             "dependencies": {
                 "@storybook/global": "^5.0.0",
-                "@storybook/instrumenter": "8.4.7",
-                "@storybook/test": "8.4.7",
+                "@storybook/instrumenter": "8.5.2",
+                "@storybook/test": "8.5.2",
                 "polished": "^4.2.2",
                 "ts-dedent": "^2.2.0"
             },
@@ -10489,13 +10531,13 @@
                 "url": "https://opencollective.com/storybook"
             },
             "peerDependencies": {
-                "storybook": "^8.4.7"
+                "storybook": "^8.5.2"
             }
         },
         "node_modules/@storybook/addon-measure": {
-            "version": "8.4.7",
-            "resolved": "https://registry.npmjs.org/@storybook/addon-measure/-/addon-measure-8.4.7.tgz",
-            "integrity": "sha512-QfvqYWDSI5F68mKvafEmZic3SMiK7zZM8VA0kTXx55hF/+vx61Mm0HccApUT96xCXIgmwQwDvn9gS4TkX81Dmw==",
+            "version": "8.5.2",
+            "resolved": "https://registry.npmjs.org/@storybook/addon-measure/-/addon-measure-8.5.2.tgz",
+            "integrity": "sha512-g7Kvrx8dqzeYWetpWYVVu4HaRzLAZVlOAlZYNfCH/aJHcFKp/p5zhPXnZh8aorxeCLHW1QSKcliaA4BNPEvTeg==",
             "dev": true,
             "license": "MIT",
             "dependencies": {
@@ -10507,30 +10549,27 @@
                 "url": "https://opencollective.com/storybook"
             },
             "peerDependencies": {
-                "storybook": "^8.4.7"
+                "storybook": "^8.5.2"
             }
         },
         "node_modules/@storybook/addon-onboarding": {
-            "version": "8.4.7",
-            "resolved": "https://registry.npmjs.org/@storybook/addon-onboarding/-/addon-onboarding-8.4.7.tgz",
-            "integrity": "sha512-FdC2NV60VNYeMxf6DVe0qV9ucSBAzMh1//C0Qqwq8CcjthMbmKlVZ7DqbVsbIHKnFaSCaUC88eR5olAfMaauCQ==",
+            "version": "8.5.2",
+            "resolved": "https://registry.npmjs.org/@storybook/addon-onboarding/-/addon-onboarding-8.5.2.tgz",
+            "integrity": "sha512-IViKQdBTuF2KSOrhyyq2soT0Je90AZbAAM5SLrVF7Q4H/Pc2lbf1JX8WwAOW2RKH2o7/U2Mvl0SXqNNcwLZC1A==",
             "dev": true,
             "license": "MIT",
-            "dependencies": {
-                "react-confetti": "^6.1.0"
-            },
             "funding": {
                 "type": "opencollective",
                 "url": "https://opencollective.com/storybook"
             },
             "peerDependencies": {
-                "storybook": "^8.4.7"
+                "storybook": "^8.5.2"
             }
         },
         "node_modules/@storybook/addon-outline": {
-            "version": "8.4.7",
-            "resolved": "https://registry.npmjs.org/@storybook/addon-outline/-/addon-outline-8.4.7.tgz",
-            "integrity": "sha512-6LYRqUZxSodmAIl8icr585Oi8pmzbZ90aloZJIpve+dBAzo7ydYrSQxxoQEVltXbKf3VeVcrs64ouAYqjisMYA==",
+            "version": "8.5.2",
+            "resolved": "https://registry.npmjs.org/@storybook/addon-outline/-/addon-outline-8.5.2.tgz",
+            "integrity": "sha512-laMVLT1xluSqMa2mMzmS1kdKcjX0HI9Fw+7pM3r4drtGWtxpyBT32YFqKfWFIBhcd364ti2tDUz9FlygGQ1rKw==",
             "dev": true,
             "license": "MIT",
             "dependencies": {
@@ -10542,7 +10581,7 @@
                 "url": "https://opencollective.com/storybook"
             },
             "peerDependencies": {
-                "storybook": "^8.4.7"
+                "storybook": "^8.5.2"
             }
         },
         "node_modules/@storybook/addon-styling-webpack": {
@@ -10559,9 +10598,9 @@
             }
         },
         "node_modules/@storybook/addon-toolbars": {
-            "version": "8.4.7",
-            "resolved": "https://registry.npmjs.org/@storybook/addon-toolbars/-/addon-toolbars-8.4.7.tgz",
-            "integrity": "sha512-OSfdv5UZs+NdGB+nZmbafGUWimiweJ/56gShlw8Neo/4jOJl1R3rnRqqY7MYx8E4GwoX+i3GF5C3iWFNQqlDcw==",
+            "version": "8.5.2",
+            "resolved": "https://registry.npmjs.org/@storybook/addon-toolbars/-/addon-toolbars-8.5.2.tgz",
+            "integrity": "sha512-gHQtVCiq7HRqdYQLOmX8nhtV1Lqz4tOCj4BVodwwf8fUcHyNor+2FvGlQjngV2pIeCtxiM/qmG63UpTBp57ZMA==",
             "dev": true,
             "license": "MIT",
             "funding": {
@@ -10569,13 +10608,13 @@
                 "url": "https://opencollective.com/storybook"
             },
             "peerDependencies": {
-                "storybook": "^8.4.7"
+                "storybook": "^8.5.2"
             }
         },
         "node_modules/@storybook/addon-viewport": {
-            "version": "8.4.7",
-            "resolved": "https://registry.npmjs.org/@storybook/addon-viewport/-/addon-viewport-8.4.7.tgz",
-            "integrity": "sha512-hvczh/jjuXXcOogih09a663sRDDSATXwbE866al1DXgbDFraYD/LxX/QDb38W9hdjU9+Qhx8VFIcNWoMQns5HQ==",
+            "version": "8.5.2",
+            "resolved": "https://registry.npmjs.org/@storybook/addon-viewport/-/addon-viewport-8.5.2.tgz",
+            "integrity": "sha512-W+7nrMQmxHcUNGsXjmb/fak1mD0a5vf4y1hBhSM7/131t8KBsvEu4ral8LTUhc4ZzuU1eIUM0Qth7SjqHqm5bA==",
             "dev": true,
             "license": "MIT",
             "dependencies": {
@@ -10586,7 +10625,7 @@
                 "url": "https://opencollective.com/storybook"
             },
             "peerDependencies": {
-                "storybook": "^8.4.7"
+                "storybook": "^8.5.2"
             }
         },
         "node_modules/@storybook/addon-webpack5-compiler-swc": {
@@ -10604,13 +10643,13 @@
             }
         },
         "node_modules/@storybook/blocks": {
-            "version": "8.4.7",
-            "resolved": "https://registry.npmjs.org/@storybook/blocks/-/blocks-8.4.7.tgz",
-            "integrity": "sha512-+QH7+JwXXXIyP3fRCxz/7E2VZepAanXJM7G8nbR3wWsqWgrRp4Wra6MvybxAYCxU7aNfJX5c+RW84SNikFpcIA==",
+            "version": "8.5.2",
+            "resolved": "https://registry.npmjs.org/@storybook/blocks/-/blocks-8.5.2.tgz",
+            "integrity": "sha512-C6Bz/YTG5ZuyAzglqgqozYUWaS39j1PnkVuMNots6S3Fp8ZJ6iZOlQ+rpumiuvnbfD5rkEZG+614RWNyNlFy7g==",
             "dev": true,
             "license": "MIT",
             "dependencies": {
-                "@storybook/csf": "^0.1.11",
+                "@storybook/csf": "0.1.12",
                 "@storybook/icons": "^1.2.12",
                 "ts-dedent": "^2.0.0"
             },
@@ -10621,7 +10660,7 @@
             "peerDependencies": {
                 "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta",
                 "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta",
-                "storybook": "^8.4.7"
+                "storybook": "^8.5.2"
             },
             "peerDependenciesMeta": {
                 "react": {
@@ -10633,14 +10672,13 @@
             }
         },
         "node_modules/@storybook/builder-webpack5": {
-            "version": "8.4.7",
-            "resolved": "https://registry.npmjs.org/@storybook/builder-webpack5/-/builder-webpack5-8.4.7.tgz",
-            "integrity": "sha512-O8LpsQ+4g2x5kh7rI9+jEUdX8k1a5egBQU1lbudmHchqsV0IKiVqBD9LL5Gj3wpit4vB8coSW4ZWTFBw8FQb4Q==",
+            "version": "8.5.2",
+            "resolved": "https://registry.npmjs.org/@storybook/builder-webpack5/-/builder-webpack5-8.5.2.tgz",
+            "integrity": "sha512-P4zpavhy9cL1GtITlFp1amTgNSfaQyi60jJwi7joUj0z4RRyBr8YpGi5il9PlaxiY2HROsCdKJftTNzWn058yA==",
             "dev": true,
             "license": "MIT",
             "dependencies": {
-                "@storybook/core-webpack": "8.4.7",
-                "@types/node": "^22.0.0",
+                "@storybook/core-webpack": "8.5.2",
                 "@types/semver": "^7.3.4",
                 "browser-assert": "^1.2.1",
                 "case-sensitive-paths-webpack-plugin": "^2.4.0",
@@ -10670,7 +10708,7 @@
                 "url": "https://opencollective.com/storybook"
             },
             "peerDependencies": {
-                "storybook": "^8.4.7"
+                "storybook": "^8.5.2"
             },
             "peerDependenciesMeta": {
                 "typescript": {
@@ -10880,9 +10918,9 @@
             }
         },
         "node_modules/@storybook/components": {
-            "version": "8.4.7",
-            "resolved": "https://registry.npmjs.org/@storybook/components/-/components-8.4.7.tgz",
-            "integrity": "sha512-uyJIcoyeMWKAvjrG9tJBUCKxr2WZk+PomgrgrUwejkIfXMO76i6jw9BwLa0NZjYdlthDv30r9FfbYZyeNPmF0g==",
+            "version": "8.5.2",
+            "resolved": "https://registry.npmjs.org/@storybook/components/-/components-8.5.2.tgz",
+            "integrity": "sha512-o5vNN30sGLTJBeGk5SKyekR4RfTpBTGs2LDjXGAmpl2MRhzd62ix8g+KIXSR0rQ55TCvKUl5VR2i99ttlRcEKw==",
             "dev": true,
             "license": "MIT",
             "funding": {
@@ -10894,13 +10932,13 @@
             }
         },
         "node_modules/@storybook/core": {
-            "version": "8.4.7",
-            "resolved": "https://registry.npmjs.org/@storybook/core/-/core-8.4.7.tgz",
-            "integrity": "sha512-7Z8Z0A+1YnhrrSXoKKwFFI4gnsLbWzr8fnDCU6+6HlDukFYh8GHRcZ9zKfqmy6U3hw2h8H5DrHsxWfyaYUUOoA==",
+            "version": "8.5.2",
+            "resolved": "https://registry.npmjs.org/@storybook/core/-/core-8.5.2.tgz",
+            "integrity": "sha512-rCOpXZo2XbdKVnZiv8oC9FId/gLkStpKGGL7hhdg/RyjcyUyTfhsvaf7LXKZH2A0n/UpwFxhF3idRfhgc1XiSg==",
             "dev": true,
             "license": "MIT",
             "dependencies": {
-                "@storybook/csf": "^0.1.11",
+                "@storybook/csf": "0.1.12",
                 "better-opn": "^3.0.2",
                 "browser-assert": "^1.2.1",
                 "esbuild": "^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 || ^0.22.0 || ^0.23.0 || ^0.24.0",
@@ -10926,13 +10964,12 @@
             }
         },
         "node_modules/@storybook/core-webpack": {
-            "version": "8.4.7",
-            "resolved": "https://registry.npmjs.org/@storybook/core-webpack/-/core-webpack-8.4.7.tgz",
-            "integrity": "sha512-Tj+CjQLpFyBJxhhMms+vbPT3+gTRAiQlrhY3L1IEVwBa3wtRMS0qjozH26d1hK4G6mUIEdwu13L54HMU/w33Sg==",
+            "version": "8.5.2",
+            "resolved": "https://registry.npmjs.org/@storybook/core-webpack/-/core-webpack-8.5.2.tgz",
+            "integrity": "sha512-r+s3zNojxl370CCCmvj0A+N27fW6zjRODQ7jsHWGSQzTDIz5Vj68rJBIOffr/27nN9r/JtbXbwxuO2UfqMqcqA==",
             "dev": true,
             "license": "MIT",
             "dependencies": {
-                "@types/node": "^22.0.0",
                 "ts-dedent": "^2.0.0"
             },
             "funding": {
@@ -10940,7 +10977,7 @@
                 "url": "https://opencollective.com/storybook"
             },
             "peerDependencies": {
-                "storybook": "^8.4.7"
+                "storybook": "^8.5.2"
             }
         },
         "node_modules/@storybook/core/node_modules/semver": {
@@ -10957,9 +10994,9 @@
             }
         },
         "node_modules/@storybook/csf": {
-            "version": "0.1.13",
-            "resolved": "https://registry.npmjs.org/@storybook/csf/-/csf-0.1.13.tgz",
-            "integrity": "sha512-7xOOwCLGB3ebM87eemep89MYRFTko+D8qE7EdAAq74lgdqRR5cOUtYWJLjO2dLtP94nqoOdHJo6MdLLKzg412Q==",
+            "version": "0.1.12",
+            "resolved": "https://registry.npmjs.org/@storybook/csf/-/csf-0.1.12.tgz",
+            "integrity": "sha512-9/exVhabisyIVL0VxTCxo01Tdm8wefIXKXfltAPTSr8cbLn5JAxGQ6QV3mjdecLGEOucfoVhAKtJfVHxEK1iqw==",
             "dev": true,
             "license": "MIT",
             "dependencies": {
@@ -10967,9 +11004,9 @@
             }
         },
         "node_modules/@storybook/csf-plugin": {
-            "version": "8.4.7",
-            "resolved": "https://registry.npmjs.org/@storybook/csf-plugin/-/csf-plugin-8.4.7.tgz",
-            "integrity": "sha512-Fgogplu4HImgC+AYDcdGm1rmL6OR1rVdNX1Be9C/NEXwOCpbbBwi0BxTf/2ZxHRk9fCeaPEcOdP5S8QHfltc1g==",
+            "version": "8.5.2",
+            "resolved": "https://registry.npmjs.org/@storybook/csf-plugin/-/csf-plugin-8.5.2.tgz",
+            "integrity": "sha512-EEQ3Vc9qIUbLH8tunzN/GSoyP3zPpNPKegZooYQbgVqA582Pel4Jnpn4uxGaOWtFCLhXMETV05X/7chGZtEujA==",
             "dev": true,
             "license": "MIT",
             "dependencies": {
@@ -10980,7 +11017,7 @@
                 "url": "https://opencollective.com/storybook"
             },
             "peerDependencies": {
-                "storybook": "^8.4.7"
+                "storybook": "^8.5.2"
             }
         },
         "node_modules/@storybook/csf/node_modules/type-fest": {
@@ -11004,9 +11041,9 @@
             "license": "MIT"
         },
         "node_modules/@storybook/icons": {
-            "version": "1.3.0",
-            "resolved": "https://registry.npmjs.org/@storybook/icons/-/icons-1.3.0.tgz",
-            "integrity": "sha512-Nz/UzeYQdUZUhacrPyfkiiysSjydyjgg/p0P9HxB4p/WaJUUjMAcaoaLgy3EXx61zZJ3iD36WPuDkZs5QYrA0A==",
+            "version": "1.3.2",
+            "resolved": "https://registry.npmjs.org/@storybook/icons/-/icons-1.3.2.tgz",
+            "integrity": "sha512-t3xcbCKkPvqyef8urBM0j/nP6sKtnlRkVgC+8JTbTAZQjaTmOjes3byEgzs89p4B/K6cJsg9wLW2k3SknLtYJw==",
             "dev": true,
             "license": "MIT",
             "engines": {
@@ -11018,9 +11055,9 @@
             }
         },
         "node_modules/@storybook/instrumenter": {
-            "version": "8.4.7",
-            "resolved": "https://registry.npmjs.org/@storybook/instrumenter/-/instrumenter-8.4.7.tgz",
-            "integrity": "sha512-k6NSD3jaRCCHAFtqXZ7tw8jAzD/yTEWXGya+REgZqq5RCkmJ+9S4Ytp/6OhQMPtPFX23gAuJJzTQVLcCr+gjRg==",
+            "version": "8.5.2",
+            "resolved": "https://registry.npmjs.org/@storybook/instrumenter/-/instrumenter-8.5.2.tgz",
+            "integrity": "sha512-BbaUw9GXVzRg3Km95t2mRu4W6C1n1erjzll5maBaVe2+lV9MbCvBcdYwGUgjFNlQ/ETgq6vLfLOEtziycq/B6g==",
             "dev": true,
             "license": "MIT",
             "dependencies": {
@@ -11032,13 +11069,13 @@
                 "url": "https://opencollective.com/storybook"
             },
             "peerDependencies": {
-                "storybook": "^8.4.7"
+                "storybook": "^8.5.2"
             }
         },
         "node_modules/@storybook/manager-api": {
-            "version": "8.4.7",
-            "resolved": "https://registry.npmjs.org/@storybook/manager-api/-/manager-api-8.4.7.tgz",
-            "integrity": "sha512-ELqemTviCxAsZ5tqUz39sDmQkvhVAvAgiplYy9Uf15kO0SP2+HKsCMzlrm2ue2FfkUNyqbDayCPPCB0Cdn/mpQ==",
+            "version": "8.5.2",
+            "resolved": "https://registry.npmjs.org/@storybook/manager-api/-/manager-api-8.5.2.tgz",
+            "integrity": "sha512-Cn+oINA6BOO2GmGHinGsOWnEpoBnurlZ9ekMq7H/c1SYMvQWNg5RlELyrhsnyhNd83fqFZy9Asb0RXI8oqz7DQ==",
             "dev": true,
             "license": "MIT",
             "funding": {
@@ -11050,9 +11087,9 @@
             }
         },
         "node_modules/@storybook/node-logger": {
-            "version": "8.4.7",
-            "resolved": "https://registry.npmjs.org/@storybook/node-logger/-/node-logger-8.4.7.tgz",
-            "integrity": "sha512-bsNMy9RgN4jVw5MMHMf0T04dX8a1lLTWq4iZoZkfdbshDKn5Z16brzPVwhFg2IE0YTEZi9XyA1NJLPgUTCcp7g==",
+            "version": "8.5.2",
+            "resolved": "https://registry.npmjs.org/@storybook/node-logger/-/node-logger-8.5.2.tgz",
+            "integrity": "sha512-yL6qs0GwUplDk7OMSsjO0z+SZdJDWI3Foazl5xYYMqPEsO/LN2N1pp9sLypzGiQ9ur0OwvbBODrWBdZ++4iDyg==",
             "dev": true,
             "license": "MIT",
             "funding": {
@@ -11064,16 +11101,15 @@
             }
         },
         "node_modules/@storybook/preset-react-webpack": {
-            "version": "8.4.7",
-            "resolved": "https://registry.npmjs.org/@storybook/preset-react-webpack/-/preset-react-webpack-8.4.7.tgz",
-            "integrity": "sha512-geTSBKyrBagVihil5MF7LkVFynbfHhCinvnbCZZqXW7M1vgcxvatunUENB+iV8eWg/0EJ+8O7scZL+BAxQ/2qg==",
+            "version": "8.5.2",
+            "resolved": "https://registry.npmjs.org/@storybook/preset-react-webpack/-/preset-react-webpack-8.5.2.tgz",
+            "integrity": "sha512-CpRunaOl4tB7Z+1dQEG/LSAdwnCZCaKdfn+Q71k6Pk1vpR6aFlhVbbVP5kgt47vimHDKYKYBQKudP+5IjJNvFA==",
             "dev": true,
             "license": "MIT",
             "dependencies": {
-                "@storybook/core-webpack": "8.4.7",
-                "@storybook/react": "8.4.7",
+                "@storybook/core-webpack": "8.5.2",
+                "@storybook/react": "8.5.2",
                 "@storybook/react-docgen-typescript-plugin": "1.0.6--canary.9.0c3f3b7.0",
-                "@types/node": "^22.0.0",
                 "@types/semver": "^7.3.4",
                 "find-up": "^5.0.0",
                 "magic-string": "^0.30.5",
@@ -11093,7 +11129,7 @@
             "peerDependencies": {
                 "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta",
                 "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta",
-                "storybook": "^8.4.7"
+                "storybook": "^8.5.2"
             },
             "peerDependenciesMeta": {
                 "typescript": {
@@ -11115,9 +11151,9 @@
             }
         },
         "node_modules/@storybook/preview-api": {
-            "version": "8.4.7",
-            "resolved": "https://registry.npmjs.org/@storybook/preview-api/-/preview-api-8.4.7.tgz",
-            "integrity": "sha512-0QVQwHw+OyZGHAJEXo6Knx+6/4er7n2rTDE5RYJ9F2E2Lg42E19pfdLlq2Jhoods2Xrclo3wj6GWR//Ahi39Eg==",
+            "version": "8.5.2",
+            "resolved": "https://registry.npmjs.org/@storybook/preview-api/-/preview-api-8.5.2.tgz",
+            "integrity": "sha512-AOOaBjwnkFU40Fi68fvAnK0gMWPz6o/AmH44yDGsHgbI07UgqxLBKCTpjCGPlyQd5ezEjmGwwFTmcmq5dG8DKA==",
             "dev": true,
             "license": "MIT",
             "funding": {
@@ -11129,18 +11165,18 @@
             }
         },
         "node_modules/@storybook/react": {
-            "version": "8.4.7",
-            "resolved": "https://registry.npmjs.org/@storybook/react/-/react-8.4.7.tgz",
-            "integrity": "sha512-nQ0/7i2DkaCb7dy0NaT95llRVNYWQiPIVuhNfjr1mVhEP7XD090p0g7eqUmsx8vfdHh2BzWEo6CoBFRd3+EXxw==",
+            "version": "8.5.2",
+            "resolved": "https://registry.npmjs.org/@storybook/react/-/react-8.5.2.tgz",
+            "integrity": "sha512-hWzw9ZllfzsaBJdAoEqPQ2GdVNV4c7PkvIWM6z67epaOHqsdsKScbTMe+YAvFMPtLtOO8KblIrtU5PeD4KyMgw==",
             "dev": true,
             "license": "MIT",
             "dependencies": {
-                "@storybook/components": "8.4.7",
+                "@storybook/components": "8.5.2",
                 "@storybook/global": "^5.0.0",
-                "@storybook/manager-api": "8.4.7",
-                "@storybook/preview-api": "8.4.7",
-                "@storybook/react-dom-shim": "8.4.7",
-                "@storybook/theming": "8.4.7"
+                "@storybook/manager-api": "8.5.2",
+                "@storybook/preview-api": "8.5.2",
+                "@storybook/react-dom-shim": "8.5.2",
+                "@storybook/theming": "8.5.2"
             },
             "engines": {
                 "node": ">=18.0.0"
@@ -11150,10 +11186,10 @@
                 "url": "https://opencollective.com/storybook"
             },
             "peerDependencies": {
-                "@storybook/test": "8.4.7",
+                "@storybook/test": "8.5.2",
                 "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta",
                 "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta",
-                "storybook": "^8.4.7",
+                "storybook": "^8.5.2",
                 "typescript": ">= 4.2.x"
             },
             "peerDependenciesMeta": {
@@ -11351,9 +11387,9 @@
             }
         },
         "node_modules/@storybook/react-dom-shim": {
-            "version": "8.4.7",
-            "resolved": "https://registry.npmjs.org/@storybook/react-dom-shim/-/react-dom-shim-8.4.7.tgz",
-            "integrity": "sha512-6bkG2jvKTmWrmVzCgwpTxwIugd7Lu+2btsLAqhQSzDyIj2/uhMNp8xIMr/NBDtLgq3nomt9gefNa9xxLwk/OMg==",
+            "version": "8.5.2",
+            "resolved": "https://registry.npmjs.org/@storybook/react-dom-shim/-/react-dom-shim-8.5.2.tgz",
+            "integrity": "sha512-lt7XoaeWI8iPlWnWzIm/Wam9TpRFhlqP0KZJoKwDyHiCByqkeMrw5MJREyWq626nf34bOW8D6vkuyTzCHGTxKg==",
             "dev": true,
             "license": "MIT",
             "funding": {
@@ -11363,20 +11399,19 @@
             "peerDependencies": {
                 "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta",
                 "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta",
-                "storybook": "^8.4.7"
+                "storybook": "^8.5.2"
             }
         },
         "node_modules/@storybook/react-webpack5": {
-            "version": "8.4.7",
-            "resolved": "https://registry.npmjs.org/@storybook/react-webpack5/-/react-webpack5-8.4.7.tgz",
-            "integrity": "sha512-T9GLqlsP4It4El7cC8rSkBPRWvORAsTDULeWlO36RST2TrYnmBOUytsi22mk7cAAAVhhD6rTrs1YdqWRMpfa1w==",
+            "version": "8.5.2",
+            "resolved": "https://registry.npmjs.org/@storybook/react-webpack5/-/react-webpack5-8.5.2.tgz",
+            "integrity": "sha512-OrHaOSaabqOJ3433B0Ea5gVhsR1WaZRs49Y+S/z33VX9iILlougXVx9zNPoeZzgDSG4xEEn2uimFpjjcLLkWzA==",
             "dev": true,
             "license": "MIT",
             "dependencies": {
-                "@storybook/builder-webpack5": "8.4.7",
-                "@storybook/preset-react-webpack": "8.4.7",
-                "@storybook/react": "8.4.7",
-                "@types/node": "^22.0.0"
+                "@storybook/builder-webpack5": "8.5.2",
+                "@storybook/preset-react-webpack": "8.5.2",
+                "@storybook/react": "8.5.2"
             },
             "engines": {
                 "node": ">=18.0.0"
@@ -11388,7 +11423,7 @@
             "peerDependencies": {
                 "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta",
                 "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta",
-                "storybook": "^8.4.7",
+                "storybook": "^8.5.2",
                 "typescript": ">= 4.2.x"
             },
             "peerDependenciesMeta": {
@@ -11398,15 +11433,15 @@
             }
         },
         "node_modules/@storybook/test": {
-            "version": "8.4.7",
-            "resolved": "https://registry.npmjs.org/@storybook/test/-/test-8.4.7.tgz",
-            "integrity": "sha512-AhvJsu5zl3uG40itSQVuSy5WByp3UVhS6xAnme4FWRwgSxhvZjATJ3AZkkHWOYjnnk+P2/sbz/XuPli1FVCWoQ==",
+            "version": "8.5.2",
+            "resolved": "https://registry.npmjs.org/@storybook/test/-/test-8.5.2.tgz",
+            "integrity": "sha512-F5WfD75m25ZRS19cSxCzHWJ/rH8jWwIjhBlhU+UW+5xjnTS1cJuC1yPT/5Jw0/0Aj9zG1atyfBUYnNHYtsBDYQ==",
             "dev": true,
             "license": "MIT",
             "dependencies": {
-                "@storybook/csf": "^0.1.11",
+                "@storybook/csf": "0.1.12",
                 "@storybook/global": "^5.0.0",
-                "@storybook/instrumenter": "8.4.7",
+                "@storybook/instrumenter": "8.5.2",
                 "@testing-library/dom": "10.4.0",
                 "@testing-library/jest-dom": "6.5.0",
                 "@testing-library/user-event": "14.5.2",
@@ -11418,13 +11453,13 @@
                 "url": "https://opencollective.com/storybook"
             },
             "peerDependencies": {
-                "storybook": "^8.4.7"
+                "storybook": "^8.5.2"
             }
         },
         "node_modules/@storybook/theming": {
-            "version": "8.4.7",
-            "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-8.4.7.tgz",
-            "integrity": "sha512-99rgLEjf7iwfSEmdqlHkSG3AyLcK0sfExcr0jnc6rLiAkBhzuIsvcHjjUwkR210SOCgXqBPW0ZA6uhnuyppHLw==",
+            "version": "8.5.2",
+            "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-8.5.2.tgz",
+            "integrity": "sha512-vro8vJx16rIE0UehawEZbxFFA4/VGYS20PMKP6Y6Fpsce0t2/cF/U9qg3jOzVb/XDwfx+ne3/V+8rjfWx8wwJw==",
             "dev": true,
             "license": "MIT",
             "funding": {
@@ -11436,9 +11471,9 @@
             }
         },
         "node_modules/@swc/core": {
-            "version": "1.10.6",
-            "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.10.6.tgz",
-            "integrity": "sha512-zgXXsI6SAVwr6XsXyMnqlyLoa1lT+r09bAWI1xT3679ejWqI1Vnl14eJG0GjWYXCEMKHCNytfMq3OOQ62C39QQ==",
+            "version": "1.10.11",
+            "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.10.11.tgz",
+            "integrity": "sha512-3zGU5y3S20cAwot9ZcsxVFNsSVaptG+dKdmAxORSE3EX7ixe1Xn5kUwLlgIsM4qrwTUWCJDLNhRS+2HLFivcDg==",
             "dev": true,
             "hasInstallScript": true,
             "license": "Apache-2.0",
@@ -11454,16 +11489,16 @@
                 "url": "https://opencollective.com/swc"
             },
             "optionalDependencies": {
-                "@swc/core-darwin-arm64": "1.10.6",
-                "@swc/core-darwin-x64": "1.10.6",
-                "@swc/core-linux-arm-gnueabihf": "1.10.6",
-                "@swc/core-linux-arm64-gnu": "1.10.6",
-                "@swc/core-linux-arm64-musl": "1.10.6",
-                "@swc/core-linux-x64-gnu": "1.10.6",
-                "@swc/core-linux-x64-musl": "1.10.6",
-                "@swc/core-win32-arm64-msvc": "1.10.6",
-                "@swc/core-win32-ia32-msvc": "1.10.6",
-                "@swc/core-win32-x64-msvc": "1.10.6"
+                "@swc/core-darwin-arm64": "1.10.11",
+                "@swc/core-darwin-x64": "1.10.11",
+                "@swc/core-linux-arm-gnueabihf": "1.10.11",
+                "@swc/core-linux-arm64-gnu": "1.10.11",
+                "@swc/core-linux-arm64-musl": "1.10.11",
+                "@swc/core-linux-x64-gnu": "1.10.11",
+                "@swc/core-linux-x64-musl": "1.10.11",
+                "@swc/core-win32-arm64-msvc": "1.10.11",
+                "@swc/core-win32-ia32-msvc": "1.10.11",
+                "@swc/core-win32-x64-msvc": "1.10.11"
             },
             "peerDependencies": {
                 "@swc/helpers": "*"
@@ -11475,9 +11510,9 @@
             }
         },
         "node_modules/@swc/core-darwin-arm64": {
-            "version": "1.10.6",
-            "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.10.6.tgz",
-            "integrity": "sha512-USbMvT8Rw5PvIfF6HyTm+yW84J9c45emzmHBDIWY76vZHkFsS5MepNi+JLQyBzBBgE7ScwBRBNhRx6VNhkSoww==",
+            "version": "1.10.11",
+            "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.10.11.tgz",
+            "integrity": "sha512-ZpgEaNcx2e5D+Pd0yZGVbpSrEDOEubn7r2JXoNBf0O85lPjUm3HDzGRfLlV/MwxRPAkwm93eLP4l7gYnc50l3g==",
             "cpu": [
                 "arm64"
             ],
@@ -11493,9 +11528,9 @@
             }
         },
         "node_modules/@swc/core-darwin-x64": {
-            "version": "1.10.6",
-            "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.10.6.tgz",
-            "integrity": "sha512-7t2IozcZN4r1p27ei+Kb8IjN4aLoBDn107fPi+aPLcVp2uFgJEUzhCDuZXBNW2057Mx1OHcjzrkaleRpECz3Xg==",
+            "version": "1.10.11",
+            "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.10.11.tgz",
+            "integrity": "sha512-szObinnq2o7spXMDU5pdunmUeLrfV67Q77rV+DyojAiGJI1RSbEQotLOk+ONOLpoapwGUxOijFG4IuX1xiwQ2g==",
             "cpu": [
                 "x64"
             ],
@@ -11511,9 +11546,9 @@
             }
         },
         "node_modules/@swc/core-linux-arm-gnueabihf": {
-            "version": "1.10.6",
-            "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.10.6.tgz",
-            "integrity": "sha512-CPgWT+D0bDp/qhXsLkIJ54LmKU1/zvyGaf/yz8A4iR+YoF6R5CSXENXhNJY8cIrb6+uNWJZzHJ+gefB5V51bpA==",
+            "version": "1.10.11",
+            "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.10.11.tgz",
+            "integrity": "sha512-tVE8aXQwd8JUB9fOGLawFJa76nrpvp3dvErjozMmWSKWqtoeO7HV83aOrVtc8G66cj4Vq7FjTE9pOJeV1FbKRw==",
             "cpu": [
                 "arm"
             ],
@@ -11529,9 +11564,9 @@
             }
         },
         "node_modules/@swc/core-linux-arm64-gnu": {
-            "version": "1.10.6",
-            "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.10.6.tgz",
-            "integrity": "sha512-5qZ6hVnqO/ShETXdGSzvdGUVx372qydlj1YWSYiaxQzTAepEBc8TC1NVUgYtOHOKVRkky1d7p6GQ9lymsd4bHw==",
+            "version": "1.10.11",
+            "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.10.11.tgz",
+            "integrity": "sha512-geFkENU5GMEKO7FqHOaw9HVlpQEW10nICoM6ubFc0hXBv8dwRXU4vQbh9s/isLSFRftw1m4jEEWixAnXSw8bxQ==",
             "cpu": [
                 "arm64"
             ],
@@ -11547,9 +11582,9 @@
             }
         },
         "node_modules/@swc/core-linux-arm64-musl": {
-            "version": "1.10.6",
-            "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.10.6.tgz",
-            "integrity": "sha512-hB2xZFmXCKf2iJF5y2z01PSuLqEoUP3jIX/XlIHN+/AIP7PkSKsValE63LnjlnWPnSEI0IxUyRE3T3FzWE/fQQ==",
+            "version": "1.10.11",
+            "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.10.11.tgz",
+            "integrity": "sha512-2mMscXe/ivq8c4tO3eQSbQDFBvagMJGlalXCspn0DgDImLYTEnt/8KHMUMGVfh0gMJTZ9q4FlGLo7mlnbx99MQ==",
             "cpu": [
                 "arm64"
             ],
@@ -11565,9 +11600,9 @@
             }
         },
         "node_modules/@swc/core-linux-x64-gnu": {
-            "version": "1.10.6",
-            "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.10.6.tgz",
-            "integrity": "sha512-PRGPp0I22+oJ8RMGg8M4hXYxEffH3ayu0WoSDPOjfol1F51Wj1tfTWN4wVa2RibzJjkBwMOT0KGLGb/hSEDDXQ==",
+            "version": "1.10.11",
+            "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.10.11.tgz",
+            "integrity": "sha512-eu2apgDbC4xwsigpl6LS+iyw6a3mL6kB4I+6PZMbFF2nIb1Dh7RGnu70Ai6mMn1o80fTmRSKsCT3CKMfVdeNFg==",
             "cpu": [
                 "x64"
             ],
@@ -11583,9 +11618,9 @@
             }
         },
         "node_modules/@swc/core-linux-x64-musl": {
-            "version": "1.10.6",
-            "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.10.6.tgz",
-            "integrity": "sha512-SoNBxlA86lnoV9vIz/TCyakLkdRhFSHx6tFMKNH8wAhz1kKYbZfDmpYoIzeQqdTh0tpx8e/Zu1zdK4smovsZqQ==",
+            "version": "1.10.11",
+            "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.10.11.tgz",
+            "integrity": "sha512-0n+wPWpDigwqRay4IL2JIvAqSKCXv6nKxPig9M7+epAlEQlqX+8Oq/Ap3yHtuhjNPb7HmnqNJLCXT1Wx+BZo0w==",
             "cpu": [
                 "x64"
             ],
@@ -11601,9 +11636,9 @@
             }
         },
         "node_modules/@swc/core-win32-arm64-msvc": {
-            "version": "1.10.6",
-            "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.10.6.tgz",
-            "integrity": "sha512-6L5Y2E+FVvM+BtoA+mJFjf/SjpFr73w2kHBxINxwH8/PkjAjkePDr5m0ibQhPXV61bTwX49+1otzTY85EsUW9Q==",
+            "version": "1.10.11",
+            "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.10.11.tgz",
+            "integrity": "sha512-7+bMSIoqcbXKosIVd314YjckDRPneA4OpG1cb3/GrkQTEDXmWT3pFBBlJf82hzJfw7b6lfv6rDVEFBX7/PJoLA==",
             "cpu": [
                 "arm64"
             ],
@@ -11619,9 +11654,9 @@
             }
         },
         "node_modules/@swc/core-win32-ia32-msvc": {
-            "version": "1.10.6",
-            "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.10.6.tgz",
-            "integrity": "sha512-kxK3tW8DJwEkAkwy0vhwoBAShRebH1QTe0mvH9tlBQ21rToVZQn+GCV/I44dind80hYPw0Tw2JKFVfoEJyBszg==",
+            "version": "1.10.11",
+            "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.10.11.tgz",
+            "integrity": "sha512-6hkLl4+3KjP/OFTryWxpW7YFN+w4R689TSPwiII4fFgsFNupyEmLWWakKfkGgV2JVA59L4Oi02elHy/O1sbgtw==",
             "cpu": [
                 "ia32"
             ],
@@ -11637,9 +11672,9 @@
             }
         },
         "node_modules/@swc/core-win32-x64-msvc": {
-            "version": "1.10.6",
-            "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.10.6.tgz",
-            "integrity": "sha512-4pJka/+t8XcHee12G/R5VWcilkp5poT2EJhrybpuREkpQ7iC/4WOlOVrohbWQ4AhDQmojYQI/iS+gdF2JFLzTQ==",
+            "version": "1.10.11",
+            "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.10.11.tgz",
+            "integrity": "sha512-kKNE2BGu/La2k2WFHovenqZvGQAHRIU+rd2/6a7D6EiQ6EyimtbhUqjCCZ+N1f5fIAnvM+sMdLiQJq4jdd/oOQ==",
             "cpu": [
                 "x64"
             ],
@@ -13872,9 +13907,9 @@
             }
         },
         "node_modules/@types/animejs": {
-            "version": "3.1.12",
-            "resolved": "https://registry.npmjs.org/@types/animejs/-/animejs-3.1.12.tgz",
-            "integrity": "sha512-fpdH+ZtlO0kqjTOqRaBdsEmvpRNOayI8k4EVkEtitL5l6wducDOXk0rgQgfZqWf/ZX9DzXrHf257S5i9xTcISQ==",
+            "version": "3.1.13",
+            "resolved": "https://registry.npmjs.org/@types/animejs/-/animejs-3.1.13.tgz",
+            "integrity": "sha512-yWg9l1z7CAv/TKpty4/vupEh24jDGUZXv4r26StRkpUPQm04ztJaftgpto8vwdFs8SiTq6XfaPKCSI+wjzNMvQ==",
             "dev": true,
             "license": "MIT"
         },
@@ -14061,12 +14096,6 @@
                 "@types/node": "*"
             }
         },
-        "node_modules/@types/cookie": {
-            "version": "0.4.1",
-            "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz",
-            "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==",
-            "license": "MIT"
-        },
         "node_modules/@types/cookie-parser": {
             "version": "1.4.8",
             "resolved": "https://registry.npmjs.org/@types/cookie-parser/-/cookie-parser-1.4.8.tgz",
@@ -14461,9 +14490,9 @@
             }
         },
         "node_modules/@types/express-serve-static-core": {
-            "version": "5.0.4",
-            "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.4.tgz",
-            "integrity": "sha512-5kz9ScmzBdzTgB/3susoCgfqNDzBjvLL4taparufgSvlwjdLy6UyUy9T/tCpYd2GIdIilCatC4iSQS0QSYHt0w==",
+            "version": "5.0.6",
+            "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.6.tgz",
+            "integrity": "sha512-3xhRnjJPkULekpSzgtoNYYcTWgEZkp4myc+Saevii5JPnHNvHMRlBSHDbs7Bh1iPPoVTERHEZXyhyLbMEsExsA==",
             "dev": true,
             "license": "MIT",
             "dependencies": {
@@ -14522,9 +14551,9 @@
             "license": "MIT"
         },
         "node_modules/@types/geojson": {
-            "version": "7946.0.15",
-            "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.15.tgz",
-            "integrity": "sha512-9oSxFzDCT2Rj6DfcHF8G++jxBKS7mBqXl5xrRW+Kbvjry6Uduya2iiwqHPhVXpasAVMBYKkEPGgKhd3+/HZ6xA==",
+            "version": "7946.0.16",
+            "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz",
+            "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==",
             "license": "MIT"
         },
         "node_modules/@types/geojson-vt": {
@@ -14690,9 +14719,9 @@
             }
         },
         "node_modules/@types/lodash": {
-            "version": "4.17.14",
-            "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.14.tgz",
-            "integrity": "sha512-jsxagdikDiDBeIRaPYtArcT8my4tN1og7MtMRquFT3XNA6axxyHDRUemqDz/taRDdOUn0GnGHRCuff4q48sW9A==",
+            "version": "4.17.15",
+            "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.15.tgz",
+            "integrity": "sha512-w/P33JFeySuhN6JLkysYUK2gEmy9kHHFN7E8ro0tkfmlDOgxBDzWEZ/J8cWA+fHqFevpswDTFZnDx+R9lbL6xw==",
             "dev": true,
             "license": "MIT"
         },
@@ -14760,15 +14789,15 @@
             "license": "MIT"
         },
         "node_modules/@types/ms": {
-            "version": "0.7.34",
-            "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz",
-            "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==",
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz",
+            "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==",
             "license": "MIT"
         },
         "node_modules/@types/node": {
-            "version": "22.10.5",
-            "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.5.tgz",
-            "integrity": "sha512-F8Q+SeGimwOo86fiovQh8qiXfFEh2/ocYv7tU5pJ3EXMSSxk1Joj5wefpFK2fHTf/N6HKGSxIDBT9f3gCxXPkQ==",
+            "version": "22.12.0",
+            "resolved": "https://registry.npmjs.org/@types/node/-/node-22.12.0.tgz",
+            "integrity": "sha512-Fll2FZ1riMjNmlmJOdAyY5pUbkftXslB5DgEzlIuNaiWhXd00FhWxVC/r4yV/4wBb9JfImTu+jiSvXTkJ7F/gA==",
             "license": "MIT",
             "dependencies": {
                 "undici-types": "~6.20.0"
@@ -14902,9 +14931,9 @@
             "license": "MIT"
         },
         "node_modules/@types/qs": {
-            "version": "6.9.17",
-            "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.17.tgz",
-            "integrity": "sha512-rX4/bPcfmvxHDv0XjfJELTTr+iB+tn032nPILqHm5wbthUUUuVtNGGqzhya9XUxjTP8Fpr0qYgSZZKxGY++svQ==",
+            "version": "6.9.18",
+            "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.18.tgz",
+            "integrity": "sha512-kK7dgTYDyGqS+e2Q4aK9X3D7q234CIZ1Bv0q/7Z5IwRDoADNU81xXJK/YVyLbLTZCoIwUoDoffFeF+p/eIklAA==",
             "dev": true,
             "license": "MIT"
         },
@@ -14999,9 +15028,9 @@
             }
         },
         "node_modules/@types/react-speech-recognition": {
-            "version": "3.9.5",
-            "resolved": "https://registry.npmjs.org/@types/react-speech-recognition/-/react-speech-recognition-3.9.5.tgz",
-            "integrity": "sha512-m3Sg3Xtj/YcEUu+nLPGwI6oq1wcSblsuyAmXgBfW6Nprfmtl+A+kH4ruPzzFKnFkq6WmmRxdsLvt0nLRAAJtBw==",
+            "version": "3.9.6",
+            "resolved": "https://registry.npmjs.org/@types/react-speech-recognition/-/react-speech-recognition-3.9.6.tgz",
+            "integrity": "sha512-cdzwXIZXWyp8zfM2XI7APDW1rZf4Nz73T4SIS2y+cC7zHnZluCdumYKH6HacxgxJH+zemAq2oXbHWXcyW0eT3A==",
             "dev": true,
             "license": "MIT",
             "dependencies": {
@@ -15252,9 +15281,9 @@
             "license": "MIT"
         },
         "node_modules/@types/web": {
-            "version": "0.0.188",
-            "resolved": "https://registry.npmjs.org/@types/web/-/web-0.0.188.tgz",
-            "integrity": "sha512-dKowKMo4P0W6f2U5i66uJ+/MlSHzBnznI0ExP1xnpcF/9/CJQT4VYvIDqz7ocWDvd0pB1QPgjOe4dVSycQrbuA==",
+            "version": "0.0.200",
+            "resolved": "https://registry.npmjs.org/@types/web/-/web-0.0.200.tgz",
+            "integrity": "sha512-83EtN8z1w+K9fqIE0bdI87F6Iofm+4h5jN+TbBvumbHnZrdUL/HJRdqtc0qDE18sYp0PxvWVaG0MDx4ahvl3dQ==",
             "license": "Apache-2.0"
         },
         "node_modules/@types/webgl-ext": {
@@ -15318,9 +15347,9 @@
             }
         },
         "node_modules/@types/ws": {
-            "version": "8.5.13",
-            "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.13.tgz",
-            "integrity": "sha512-osM/gWBTPKgHV8XkTunnegTRIsvF6owmf5w+JtAfOw472dptdm0dlGv4xCt6GwQRcC2XVOvvRE/0bAoQcL2QkA==",
+            "version": "8.5.14",
+            "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.14.tgz",
+            "integrity": "sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw==",
             "dev": true,
             "license": "MIT",
             "dependencies": {
@@ -15360,17 +15389,17 @@
             "license": "MIT"
         },
         "node_modules/@typescript-eslint/eslint-plugin": {
-            "version": "8.19.1",
-            "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.19.1.tgz",
-            "integrity": "sha512-tJzcVyvvb9h/PB96g30MpxACd9IrunT7GF9wfA9/0TJ1LxGOJx1TdPzSbBBnNED7K9Ka8ybJsnEpiXPktolTLg==",
+            "version": "8.22.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.22.0.tgz",
+            "integrity": "sha512-4Uta6REnz/xEJMvwf72wdUnC3rr4jAQf5jnTkeRQ9b6soxLxhDEbS/pfMPoJLDfFPNVRdryqWUIV/2GZzDJFZw==",
             "dev": true,
             "license": "MIT",
             "dependencies": {
                 "@eslint-community/regexpp": "^4.10.0",
-                "@typescript-eslint/scope-manager": "8.19.1",
-                "@typescript-eslint/type-utils": "8.19.1",
-                "@typescript-eslint/utils": "8.19.1",
-                "@typescript-eslint/visitor-keys": "8.19.1",
+                "@typescript-eslint/scope-manager": "8.22.0",
+                "@typescript-eslint/type-utils": "8.22.0",
+                "@typescript-eslint/utils": "8.22.0",
+                "@typescript-eslint/visitor-keys": "8.22.0",
                 "graphemer": "^1.4.0",
                 "ignore": "^5.3.1",
                 "natural-compare": "^1.4.0",
@@ -15390,16 +15419,16 @@
             }
         },
         "node_modules/@typescript-eslint/parser": {
-            "version": "8.19.1",
-            "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.19.1.tgz",
-            "integrity": "sha512-67gbfv8rAwawjYx3fYArwldTQKoYfezNUT4D5ioWetr/xCrxXxvleo3uuiFuKfejipvq+og7mjz3b0G2bVyUCw==",
+            "version": "8.22.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.22.0.tgz",
+            "integrity": "sha512-MqtmbdNEdoNxTPzpWiWnqNac54h8JDAmkWtJExBVVnSrSmi9z+sZUt0LfKqk9rjqmKOIeRhO4fHHJ1nQIjduIQ==",
             "dev": true,
             "license": "MIT",
             "dependencies": {
-                "@typescript-eslint/scope-manager": "8.19.1",
-                "@typescript-eslint/types": "8.19.1",
-                "@typescript-eslint/typescript-estree": "8.19.1",
-                "@typescript-eslint/visitor-keys": "8.19.1",
+                "@typescript-eslint/scope-manager": "8.22.0",
+                "@typescript-eslint/types": "8.22.0",
+                "@typescript-eslint/typescript-estree": "8.22.0",
+                "@typescript-eslint/visitor-keys": "8.22.0",
                 "debug": "^4.3.4"
             },
             "engines": {
@@ -15415,14 +15444,14 @@
             }
         },
         "node_modules/@typescript-eslint/scope-manager": {
-            "version": "8.19.1",
-            "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.19.1.tgz",
-            "integrity": "sha512-60L9KIuN/xgmsINzonOcMDSB8p82h95hoBfSBtXuO4jlR1R9L1xSkmVZKgCPVfavDlXihh4ARNjXhh1gGnLC7Q==",
+            "version": "8.22.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.22.0.tgz",
+            "integrity": "sha512-/lwVV0UYgkj7wPSw0o8URy6YI64QmcOdwHuGuxWIYznO6d45ER0wXUbksr9pYdViAofpUCNJx/tAzNukgvaaiQ==",
             "dev": true,
             "license": "MIT",
             "dependencies": {
-                "@typescript-eslint/types": "8.19.1",
-                "@typescript-eslint/visitor-keys": "8.19.1"
+                "@typescript-eslint/types": "8.22.0",
+                "@typescript-eslint/visitor-keys": "8.22.0"
             },
             "engines": {
                 "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -15433,14 +15462,14 @@
             }
         },
         "node_modules/@typescript-eslint/type-utils": {
-            "version": "8.19.1",
-            "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.19.1.tgz",
-            "integrity": "sha512-Rp7k9lhDKBMRJB/nM9Ksp1zs4796wVNyihG9/TU9R6KCJDNkQbc2EOKjrBtLYh3396ZdpXLtr/MkaSEmNMtykw==",
+            "version": "8.22.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.22.0.tgz",
+            "integrity": "sha512-NzE3aB62fDEaGjaAYZE4LH7I1MUwHooQ98Byq0G0y3kkibPJQIXVUspzlFOmOfHhiDLwKzMlWxaNv+/qcZurJA==",
             "dev": true,
             "license": "MIT",
             "dependencies": {
-                "@typescript-eslint/typescript-estree": "8.19.1",
-                "@typescript-eslint/utils": "8.19.1",
+                "@typescript-eslint/typescript-estree": "8.22.0",
+                "@typescript-eslint/utils": "8.22.0",
                 "debug": "^4.3.4",
                 "ts-api-utils": "^2.0.0"
             },
@@ -15457,9 +15486,9 @@
             }
         },
         "node_modules/@typescript-eslint/types": {
-            "version": "8.19.1",
-            "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.19.1.tgz",
-            "integrity": "sha512-JBVHMLj7B1K1v1051ZaMMgLW4Q/jre5qGK0Ew6UgXz1Rqh+/xPzV1aW581OM00X6iOfyr1be+QyW8LOUf19BbA==",
+            "version": "8.22.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.22.0.tgz",
+            "integrity": "sha512-0S4M4baNzp612zwpD4YOieP3VowOARgK2EkN/GBn95hpyF8E2fbMT55sRHWBq+Huaqk3b3XK+rxxlM8sPgGM6A==",
             "dev": true,
             "license": "MIT",
             "engines": {
@@ -15471,14 +15500,14 @@
             }
         },
         "node_modules/@typescript-eslint/typescript-estree": {
-            "version": "8.19.1",
-            "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.19.1.tgz",
-            "integrity": "sha512-jk/TZwSMJlxlNnqhy0Eod1PNEvCkpY6MXOXE/WLlblZ6ibb32i2We4uByoKPv1d0OD2xebDv4hbs3fm11SMw8Q==",
+            "version": "8.22.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.22.0.tgz",
+            "integrity": "sha512-SJX99NAS2ugGOzpyhMza/tX+zDwjvwAtQFLsBo3GQxiGcvaKlqGBkmZ+Y1IdiSi9h4Q0Lr5ey+Cp9CGWNY/F/w==",
             "dev": true,
             "license": "MIT",
             "dependencies": {
-                "@typescript-eslint/types": "8.19.1",
-                "@typescript-eslint/visitor-keys": "8.19.1",
+                "@typescript-eslint/types": "8.22.0",
+                "@typescript-eslint/visitor-keys": "8.22.0",
                 "debug": "^4.3.4",
                 "fast-glob": "^3.3.2",
                 "is-glob": "^4.0.3",
@@ -15527,16 +15556,16 @@
             }
         },
         "node_modules/@typescript-eslint/utils": {
-            "version": "8.19.1",
-            "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.19.1.tgz",
-            "integrity": "sha512-IxG5gLO0Ne+KaUc8iW1A+XuKLd63o4wlbI1Zp692n1xojCl/THvgIKXJXBZixTh5dd5+yTJ/VXH7GJaaw21qXA==",
+            "version": "8.22.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.22.0.tgz",
+            "integrity": "sha512-T8oc1MbF8L+Bk2msAvCUzjxVB2Z2f+vXYfcucE2wOmYs7ZUwco5Ep0fYZw8quNwOiw9K8GYVL+Kgc2pETNTLOg==",
             "dev": true,
             "license": "MIT",
             "dependencies": {
                 "@eslint-community/eslint-utils": "^4.4.0",
-                "@typescript-eslint/scope-manager": "8.19.1",
-                "@typescript-eslint/types": "8.19.1",
-                "@typescript-eslint/typescript-estree": "8.19.1"
+                "@typescript-eslint/scope-manager": "8.22.0",
+                "@typescript-eslint/types": "8.22.0",
+                "@typescript-eslint/typescript-estree": "8.22.0"
             },
             "engines": {
                 "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -15551,13 +15580,13 @@
             }
         },
         "node_modules/@typescript-eslint/visitor-keys": {
-            "version": "8.19.1",
-            "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.19.1.tgz",
-            "integrity": "sha512-fzmjU8CHK853V/avYZAvuVut3ZTfwN5YtMaoi+X9Y9MA9keaWNHC3zEQ9zvyX/7Hj+5JkNyK1l7TOR2hevHB6Q==",
+            "version": "8.22.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.22.0.tgz",
+            "integrity": "sha512-AWpYAXnUgvLNabGTy3uBylkgZoosva/miNd1I8Bz3SjotmQPbVqhO4Cczo8AsZ44XVErEBPr/CRSgaj8sG7g0w==",
             "dev": true,
             "license": "MIT",
             "dependencies": {
-                "@typescript-eslint/types": "8.19.1",
+                "@typescript-eslint/types": "8.22.0",
                 "eslint-visitor-keys": "^4.2.0"
             },
             "engines": {
@@ -15569,9 +15598,9 @@
             }
         },
         "node_modules/@ungap/structured-clone": {
-            "version": "1.2.1",
-            "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.1.tgz",
-            "integrity": "sha512-fEzPV3hSkSMltkw152tJKNARhOupqbH96MZWyRjNaYZOMIzbrTeQDG+MTc6Mr2pgzFQzFxAfmhGDNP5QK++2ZA==",
+            "version": "1.3.0",
+            "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz",
+            "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==",
             "license": "ISC"
         },
         "node_modules/@vitest/expect": {
@@ -15949,12 +15978,6 @@
             "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==",
             "license": "Apache-2.0"
         },
-        "node_modules/abbrev": {
-            "version": "1.1.1",
-            "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
-            "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
-            "license": "ISC"
-        },
         "node_modules/abort-controller": {
             "version": "3.0.0",
             "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
@@ -16178,12 +16201,6 @@
                 "node": ">= 8"
             }
         },
-        "node_modules/aproba": {
-            "version": "2.0.0",
-            "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz",
-            "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==",
-            "license": "ISC"
-        },
         "node_modules/archiver": {
             "version": "7.0.1",
             "resolved": "https://registry.npmjs.org/archiver/-/archiver-7.0.1.tgz",
@@ -16220,34 +16237,6 @@
                 "node": ">= 14"
             }
         },
-        "node_modules/are-we-there-yet": {
-            "version": "2.0.0",
-            "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz",
-            "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==",
-            "deprecated": "This package is no longer supported.",
-            "license": "ISC",
-            "dependencies": {
-                "delegates": "^1.0.0",
-                "readable-stream": "^3.6.0"
-            },
-            "engines": {
-                "node": ">=10"
-            }
-        },
-        "node_modules/are-we-there-yet/node_modules/readable-stream": {
-            "version": "3.6.2",
-            "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
-            "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
-            "license": "MIT",
-            "dependencies": {
-                "inherits": "^2.0.3",
-                "string_decoder": "^1.1.1",
-                "util-deprecate": "^1.0.1"
-            },
-            "engines": {
-                "node": ">= 6"
-            }
-        },
         "node_modules/arg": {
             "version": "4.1.3",
             "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
@@ -16543,6 +16532,16 @@
             "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==",
             "license": "MIT"
         },
+        "node_modules/async-function": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz",
+            "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==",
+            "dev": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">= 0.4"
+            }
+        },
         "node_modules/asynckit": {
             "version": "0.4.0",
             "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
@@ -16729,49 +16728,67 @@
             "license": "MIT"
         },
         "node_modules/bare-events": {
-            "version": "2.5.3",
-            "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.5.3.tgz",
-            "integrity": "sha512-pCO3aoRJ0MBiRMu8B7vUga0qL3L7gO1+SW7ku6qlSsMLwuhaawnuvZDyzJY/kyC63Un0XAB0OPUcfF1eTO/V+Q==",
+            "version": "2.5.4",
+            "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.5.4.tgz",
+            "integrity": "sha512-+gFfDkR8pj4/TrWCGUGWmJIkBwuxPS5F+a5yWjOHQt2hHvNZd5YLzadjmDUtFmMM4y429bnKLa8bYBMHcYdnQA==",
             "license": "Apache-2.0",
             "optional": true
         },
         "node_modules/bare-fs": {
-            "version": "2.3.5",
-            "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-2.3.5.tgz",
-            "integrity": "sha512-SlE9eTxifPDJrT6YgemQ1WGFleevzwY+XAP1Xqgl56HtcrisC2CHCZ2tq6dBpcH2TnNxwUEUGhweo+lrQtYuiw==",
+            "version": "4.0.1",
+            "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.0.1.tgz",
+            "integrity": "sha512-ilQs4fm/l9eMfWY2dY0WCIUplSUp7U0CT1vrqMg1MUdeZl4fypu5UP0XcDBK5WBQPJAKP1b7XEodISmekH/CEg==",
             "license": "Apache-2.0",
             "optional": true,
             "dependencies": {
                 "bare-events": "^2.0.0",
-                "bare-path": "^2.0.0",
+                "bare-path": "^3.0.0",
                 "bare-stream": "^2.0.0"
+            },
+            "engines": {
+                "bare": ">=1.7.0"
             }
         },
         "node_modules/bare-os": {
-            "version": "2.4.4",
-            "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-2.4.4.tgz",
-            "integrity": "sha512-z3UiI2yi1mK0sXeRdc4O1Kk8aOa/e+FNWZcTiPB/dfTWyLypuE99LibgRaQki914Jq//yAWylcAt+mknKdixRQ==",
+            "version": "3.4.0",
+            "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-3.4.0.tgz",
+            "integrity": "sha512-9Ous7UlnKbe3fMi7Y+qh0DwAup6A1JkYgPnjvMDNOlmnxNRQvQ/7Nst+OnUQKzk0iAT0m9BisbDVp9gCv8+ETA==",
             "license": "Apache-2.0",
-            "optional": true
+            "optional": true,
+            "engines": {
+                "bare": ">=1.6.0"
+            }
         },
         "node_modules/bare-path": {
-            "version": "2.1.3",
-            "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-2.1.3.tgz",
-            "integrity": "sha512-lh/eITfU8hrj9Ru5quUp0Io1kJWIk1bTjzo7JH1P5dWmQ2EL4hFUlfI8FonAhSlgIfhn63p84CDY/x+PisgcXA==",
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.0.0.tgz",
+            "integrity": "sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==",
             "license": "Apache-2.0",
             "optional": true,
             "dependencies": {
-                "bare-os": "^2.1.0"
+                "bare-os": "^3.0.1"
             }
         },
         "node_modules/bare-stream": {
-            "version": "2.6.1",
-            "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.6.1.tgz",
-            "integrity": "sha512-eVZbtKM+4uehzrsj49KtCy3Pbg7kO1pJ3SKZ1SFrIH/0pnj9scuGGgUlNDf/7qS8WKtGdiJY5Kyhs/ivYPTB/g==",
+            "version": "2.6.4",
+            "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.6.4.tgz",
+            "integrity": "sha512-G6i3A74FjNq4nVrrSTUz5h3vgXzBJnjmWAVlBWaZETkgu+LgKd7AiyOml3EDJY1AHlIbBHKDXE+TUT53Ff8OaA==",
             "license": "Apache-2.0",
             "optional": true,
             "dependencies": {
                 "streamx": "^2.21.0"
+            },
+            "peerDependencies": {
+                "bare-buffer": "*",
+                "bare-events": "*"
+            },
+            "peerDependenciesMeta": {
+                "bare-buffer": {
+                    "optional": true
+                },
+                "bare-events": {
+                    "optional": true
+                }
             }
         },
         "node_modules/Base64": {
@@ -17000,18 +17017,61 @@
                 "react": ">= 16.12.0"
             }
         },
+        "node_modules/bl": {
+            "version": "4.1.0",
+            "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
+            "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
+            "license": "MIT",
+            "dependencies": {
+                "buffer": "^5.5.0",
+                "inherits": "^2.0.4",
+                "readable-stream": "^3.4.0"
+            }
+        },
+        "node_modules/bl/node_modules/buffer": {
+            "version": "5.7.1",
+            "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
+            "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
+            "funding": [
+                {
+                    "type": "github",
+                    "url": "https://github.com/sponsors/feross"
+                },
+                {
+                    "type": "patreon",
+                    "url": "https://www.patreon.com/feross"
+                },
+                {
+                    "type": "consulting",
+                    "url": "https://feross.org/support"
+                }
+            ],
+            "license": "MIT",
+            "dependencies": {
+                "base64-js": "^1.3.1",
+                "ieee754": "^1.1.13"
+            }
+        },
+        "node_modules/bl/node_modules/readable-stream": {
+            "version": "3.6.2",
+            "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+            "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+            "license": "MIT",
+            "dependencies": {
+                "inherits": "^2.0.3",
+                "string_decoder": "^1.1.1",
+                "util-deprecate": "^1.0.1"
+            },
+            "engines": {
+                "node": ">= 6"
+            }
+        },
         "node_modules/bluebird": {
             "version": "3.7.2",
             "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
             "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==",
             "license": "MIT"
         },
-        "node_modules/bmp-js": {
-            "version": "0.1.0",
-            "resolved": "https://registry.npmjs.org/bmp-js/-/bmp-js-0.1.0.tgz",
-            "integrity": "sha512-vHdS19CnY3hwiNdkaqk93DvjVLfbEcI8mys4UjuWrlX1haDmroo8o4xCzh4wD6DGV6HxRCyauwhHRqMTfERtjw==",
-            "license": "MIT"
-        },
         "node_modules/bmp-ts": {
             "version": "1.0.9",
             "resolved": "https://registry.npmjs.org/bmp-ts/-/bmp-ts-1.0.9.tgz",
@@ -17418,9 +17478,9 @@
             }
         },
         "node_modules/caniuse-lite": {
-            "version": "1.0.30001690",
-            "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001690.tgz",
-            "integrity": "sha512-5ExiE3qQN6oF8Clf8ifIDcMRCRE/dMGcETG/XGMD8/XiXm6HXQgQTh1yZYLXXpSOsEUlJm1Xr7kGULZTuGtP/w==",
+            "version": "1.0.30001696",
+            "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001696.tgz",
+            "integrity": "sha512-pDCPkvzfa39ehJtJ+OwGT/2yvT2SbjfHhiIW2LWOAcMQ7BzwxT/XuyUp4OTOd0XFWA6BKw0JalnBHgSi5DGJBQ==",
             "funding": [
                 {
                     "type": "opencollective",
@@ -17438,18 +17498,17 @@
             "license": "CC-BY-4.0"
         },
         "node_modules/canvas": {
-            "version": "2.11.2",
-            "resolved": "https://registry.npmjs.org/canvas/-/canvas-2.11.2.tgz",
-            "integrity": "sha512-ItanGBMrmRV7Py2Z+Xhs7cT+FNt5K0vPL4p9EZ/UX/Mu7hFbkxSjKF2KVtPwX7UYWp7dRKnrTvReflgrItJbdw==",
+            "version": "3.1.0",
+            "resolved": "https://registry.npmjs.org/canvas/-/canvas-3.1.0.tgz",
+            "integrity": "sha512-tTj3CqqukVJ9NgSahykNwtGda7V33VLObwrHfzT0vqJXu7J4d4C/7kQQW3fOEGDfZZoILPut5H00gOjyttPGyg==",
             "hasInstallScript": true,
             "license": "MIT",
             "dependencies": {
-                "@mapbox/node-pre-gyp": "^1.0.0",
-                "nan": "^2.17.0",
-                "simple-get": "^3.0.3"
+                "node-addon-api": "^7.0.0",
+                "prebuild-install": "^7.1.1"
             },
             "engines": {
-                "node": ">=6"
+                "node": "^18.12.0 || >= 20.9.0"
             }
         },
         "node_modules/case-sensitive-paths-webpack-plugin": {
@@ -17699,18 +17758,15 @@
             }
         },
         "node_modules/chownr": {
-            "version": "2.0.0",
-            "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
-            "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==",
-            "license": "ISC",
-            "engines": {
-                "node": ">=10"
-            }
+            "version": "1.1.4",
+            "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
+            "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
+            "license": "ISC"
         },
         "node_modules/chromatic": {
-            "version": "11.22.0",
-            "resolved": "https://registry.npmjs.org/chromatic/-/chromatic-11.22.0.tgz",
-            "integrity": "sha512-u1kAPR9lj9aFzsCp0iWPXBbsKgcxFU7iJO6mFbgNHGVg+YPBqiJMuvgB8EQHdNbHjk5amFnGnIz/Ww8fK3t9Hw==",
+            "version": "11.25.1",
+            "resolved": "https://registry.npmjs.org/chromatic/-/chromatic-11.25.1.tgz",
+            "integrity": "sha512-D0NdcGOSy84hqgNnSY7FM4TzB77RymRTowjm4hb1CV4wbk1djKTV4SJbbYVCzHFD+n/NOg/wtZ9Y7sjiRdy8dA==",
             "dev": true,
             "license": "MIT",
             "bin": {
@@ -17741,27 +17797,18 @@
             }
         },
         "node_modules/chromium-bidi": {
-            "version": "0.11.0",
-            "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.11.0.tgz",
-            "integrity": "sha512-6CJWHkNRoyZyjV9Rwv2lYONZf1Xm0IuDyNq97nwSsxxP3wf5Bwy15K5rOvVKMtJ127jJBmxFUanSAOjgFRxgrA==",
+            "version": "1.1.0",
+            "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-1.1.0.tgz",
+            "integrity": "sha512-HislCEczCuamWm3+55Lig9XKmMF13K+BGKum9rwtDAzgUAHT4h5jNwhDmD4U20VoVUG8ujnv9UZ89qiIf5uF8w==",
             "license": "Apache-2.0",
             "dependencies": {
                 "mitt": "3.0.1",
-                "zod": "3.23.8"
+                "zod": "3.24.1"
             },
             "peerDependencies": {
                 "devtools-protocol": "*"
             }
         },
-        "node_modules/chromium-bidi/node_modules/zod": {
-            "version": "3.23.8",
-            "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz",
-            "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==",
-            "license": "MIT",
-            "funding": {
-                "url": "https://github.com/sponsors/colinhacks"
-            }
-        },
         "node_modules/ci-info": {
             "version": "3.9.0",
             "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz",
@@ -17996,9 +18043,9 @@
             }
         },
         "node_modules/cohere-ai": {
-            "version": "7.15.2",
-            "resolved": "https://registry.npmjs.org/cohere-ai/-/cohere-ai-7.15.2.tgz",
-            "integrity": "sha512-KGDbZnTbbQkv+zlU9K3cpy+FLIiBIDqaF7yJZCu45wUNRicsHQgWAnmkLPXn0KRfWskfMfzRY548Yov/Va10Zw==",
+            "version": "7.15.4",
+            "resolved": "https://registry.npmjs.org/cohere-ai/-/cohere-ai-7.15.4.tgz",
+            "integrity": "sha512-jLe7rVQJPAsvsxcKI/bBLynlgDkMWPEYW5a25V5yX9OWYRQ+t++OAqxfsDt4703yGdi/hok0hT3GkPO0jGKjhw==",
             "dependencies": {
                 "@aws-sdk/client-sagemaker": "^3.583.0",
                 "@aws-sdk/credential-providers": "^3.583.0",
@@ -18071,15 +18118,6 @@
                 "simple-swizzle": "^0.2.2"
             }
         },
-        "node_modules/color-support": {
-            "version": "1.1.3",
-            "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz",
-            "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==",
-            "license": "ISC",
-            "bin": {
-                "color-support": "bin.js"
-            }
-        },
         "node_modules/colorette": {
             "version": "2.0.20",
             "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
@@ -18317,12 +18355,6 @@
                 "mongodb": ">= 5.1.0 < 7"
             }
         },
-        "node_modules/console-control-strings": {
-            "version": "1.1.0",
-            "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
-            "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==",
-            "license": "ISC"
-        },
         "node_modules/constantinople": {
             "version": "4.0.1",
             "resolved": "https://registry.npmjs.org/constantinople/-/constantinople-4.0.1.tgz",
@@ -18728,12 +18760,13 @@
             }
         },
         "node_modules/cssstyle": {
-            "version": "4.1.0",
-            "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.1.0.tgz",
-            "integrity": "sha512-h66W1URKpBS5YMI/V8PyXvTMFT8SupJ1IzoIV8IeBC/ji8WVmrO8dGlTi+2dh6whmdk6BiKJLD/ZBkhWbcg6nA==",
+            "version": "4.2.1",
+            "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.2.1.tgz",
+            "integrity": "sha512-9+vem03dMXG7gDmZ62uqmRiMRNtinIZ9ZyuF6BdxzfOD+FdN5hretzynkn0ReS2DO2GSw76RWHs0UmJPI2zUjw==",
             "license": "MIT",
             "dependencies": {
-                "rrweb-cssom": "^0.7.1"
+                "@asamuzakjp/css-color": "^2.8.2",
+                "rrweb-cssom": "^0.8.0"
             },
             "engines": {
                 "node": ">=18"
@@ -18787,9 +18820,9 @@
             "license": "MIT"
         },
         "node_modules/cytoscape": {
-            "version": "3.30.4",
-            "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.30.4.tgz",
-            "integrity": "sha512-OxtlZwQl1WbwMmLiyPSEBuzeTIQnwZhJYYWFzZ2PhEHVFwpeaqNIkUzSiso00D98qk60l8Gwon2RP304d3BJ1A==",
+            "version": "3.31.0",
+            "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.31.0.tgz",
+            "integrity": "sha512-zDGn1K/tfZwEnoGOcHc0H4XazqAAXAuDpcYw9mUnUjATjqljyCNGJv8uEvbvxGaGHaVshxMecyl6oc6uKzRfbw==",
             "license": "MIT",
             "engines": {
                 "node": ">=0.10"
@@ -19464,9 +19497,9 @@
             }
         },
         "node_modules/decimal.js": {
-            "version": "10.4.3",
-            "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz",
-            "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==",
+            "version": "10.5.0",
+            "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.5.0.tgz",
+            "integrity": "sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==",
             "license": "MIT"
         },
         "node_modules/decimal.js-light": {
@@ -19703,12 +19736,6 @@
                 "node": ">=0.4.0"
             }
         },
-        "node_modules/delegates": {
-            "version": "1.0.0",
-            "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
-            "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==",
-            "license": "MIT"
-        },
         "node_modules/depcheck": {
             "version": "1.4.7",
             "resolved": "https://registry.npmjs.org/depcheck/-/depcheck-1.4.7.tgz",
@@ -19831,9 +19858,9 @@
             }
         },
         "node_modules/devtools-protocol": {
-            "version": "0.0.1367902",
-            "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1367902.tgz",
-            "integrity": "sha512-XxtPuC3PGakY6PD7dG66/o8KwJ/LkH2/EKe19Dcw58w53dv4/vSQEkn/SzuyhHE2q4zPgCkxQBxus3VV4ql+Pg==",
+            "version": "0.0.1380148",
+            "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1380148.tgz",
+            "integrity": "sha512-1CJABgqLxbYxVI+uJY/UDUHJtJ0KZTSjNYJYKqd9FRoXT33WDakDHNxRapMEgzeJ/C3rcs01+avshMnPmKQbvA==",
             "license": "BSD-3-Clause"
         },
         "node_modules/dezalgo": {
@@ -20089,9 +20116,9 @@
             "license": "MIT"
         },
         "node_modules/electron-to-chromium": {
-            "version": "1.5.79",
-            "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.79.tgz",
-            "integrity": "sha512-nYOxJNxQ9Om4EC88BE4pPoNI8xwSFf8pU/BAeOl4Hh/b/i6V4biTAzwV7pXi3ARKeoYO5JZKMIXTryXSVer5RA==",
+            "version": "1.5.88",
+            "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.88.tgz",
+            "integrity": "sha512-K3C2qf1o+bGzbilTDCTBhTQcMS9KW60yTAaTeeXsfvQuTDDwlokLam/AdqlqcSy9u4UainDgsHV23ksXAOgamw==",
             "license": "ISC"
         },
         "node_modules/emoji-regex": {
@@ -20186,12 +20213,11 @@
             }
         },
         "node_modules/engine.io": {
-            "version": "6.6.2",
-            "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.2.tgz",
-            "integrity": "sha512-gmNvsYi9C8iErnZdVcJnvCpSKbWTt1E8+JZo8b+daLninywUWi5NQ5STSHZ9rFjFO7imNcvb8Pc5pe/wMR5xEw==",
+            "version": "6.6.4",
+            "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.4.tgz",
+            "integrity": "sha512-ZCkIjSYNDyGn0R6ewHDtXgns/Zre/NT6Agvq1/WobF7JXgFff4SeDroKiCO3fNJreU9YG429Sc81o4w5ok/W5g==",
             "license": "MIT",
             "dependencies": {
-                "@types/cookie": "^0.4.1",
                 "@types/cors": "^2.8.12",
                 "@types/node": ">=10.0.0",
                 "accepts": "~1.3.4",
@@ -20207,9 +20233,9 @@
             }
         },
         "node_modules/engine.io-client": {
-            "version": "6.6.2",
-            "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.2.tgz",
-            "integrity": "sha512-TAr+NKeoVTjEVW8P3iHguO1LO6RlUz9O5Y8o7EY0fU+gY1NYqas7NN3slpFtbXEsLMHk0h90fJMfKjRkQ0qUIw==",
+            "version": "6.6.3",
+            "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.3.tgz",
+            "integrity": "sha512-T0iLjnyNWahNyv/lcjS2y4oE358tVS/SYQNxYXGAJ9/GLgH4VCvOQ/mhTjqU88mLZCQgiG8RIegFHYCdVC+j5w==",
             "license": "MIT",
             "dependencies": {
                 "@socket.io/component-emitter": "~3.1.0",
@@ -20478,9 +20504,9 @@
             "license": "MIT"
         },
         "node_modules/es-object-atoms": {
-            "version": "1.0.0",
-            "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz",
-            "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==",
+            "version": "1.1.1",
+            "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
+            "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
             "license": "MIT",
             "dependencies": {
                 "es-errors": "^1.3.0"
@@ -20652,18 +20678,18 @@
             }
         },
         "node_modules/eslint": {
-            "version": "9.17.0",
-            "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.17.0.tgz",
-            "integrity": "sha512-evtlNcpJg+cZLcnVKwsai8fExnqjGPicK7gnUtlNuzu+Fv9bI0aLpND5T44VLQtoMEnI57LoXO9XAkIXwohKrA==",
+            "version": "9.19.0",
+            "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.19.0.tgz",
+            "integrity": "sha512-ug92j0LepKlbbEv6hD911THhoRHmbdXt2gX+VDABAW/Ir7D3nqKdv5Pf5vtlyY6HQMTEP2skXY43ueqTCWssEA==",
             "license": "MIT",
             "dependencies": {
                 "@eslint-community/eslint-utils": "^4.2.0",
                 "@eslint-community/regexpp": "^4.12.1",
                 "@eslint/config-array": "^0.19.0",
-                "@eslint/core": "^0.9.0",
+                "@eslint/core": "^0.10.0",
                 "@eslint/eslintrc": "^3.2.0",
-                "@eslint/js": "9.17.0",
-                "@eslint/plugin-kit": "^0.2.3",
+                "@eslint/js": "9.19.0",
+                "@eslint/plugin-kit": "^0.2.5",
                 "@humanfs/node": "^0.16.6",
                 "@humanwhocodes/module-importer": "^1.0.1",
                 "@humanwhocodes/retry": "^0.4.1",
@@ -20711,9 +20737,9 @@
             }
         },
         "node_modules/eslint-plugin-react": {
-            "version": "7.37.3",
-            "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.3.tgz",
-            "integrity": "sha512-DomWuTQPFYZwF/7c9W2fkKkStqZmBd3uugfqBYLdkZ3Hii23WzZuOLUskGxB8qkSKqftxEeGL1TB2kMhrce0jA==",
+            "version": "7.37.4",
+            "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.4.tgz",
+            "integrity": "sha512-BGP0jRmfYyvOyvMoRX/uoUeW+GqNj9y16bPQzqAHf3AYII/tDs+jMN0dBVkl88/OZwNGwrVFxE7riHsXVfy/LQ==",
             "dev": true,
             "license": "MIT",
             "dependencies": {
@@ -21045,6 +21071,15 @@
             "integrity": "sha512-g/aje2noHivrRSLbAUtBPWFbxKdKhgj/xr1vATDdUXPOFYJlQ62Ft0oy+72V6XLIpDJfHs6gXLbBLAolqOXYRw==",
             "license": "MIT"
         },
+        "node_modules/expand-template": {
+            "version": "2.0.3",
+            "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz",
+            "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==",
+            "license": "(MIT OR WTFPL)",
+            "engines": {
+                "node": ">=6"
+            }
+        },
         "node_modules/expand-tilde": {
             "version": "2.0.2",
             "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz",
@@ -21359,9 +21394,9 @@
             "license": "MIT"
         },
         "node_modules/fast-uri": {
-            "version": "3.0.5",
-            "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.5.tgz",
-            "integrity": "sha512-5JnBCWpFlMo0a3ciDy/JckMzzv1U9coZrIhedq+HXxxUfDTAiS0LA8OKVao4G9BxmCVck/jtA5r3KAtRWEyD8Q==",
+            "version": "3.0.6",
+            "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz",
+            "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==",
             "funding": [
                 {
                     "type": "github",
@@ -21799,12 +21834,18 @@
             }
         },
         "node_modules/for-each": {
-            "version": "0.3.3",
-            "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
-            "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==",
+            "version": "0.3.4",
+            "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.4.tgz",
+            "integrity": "sha512-kKaIINnFpzW6ffJNDjjyjrk21BkDx38c0xa/klsT8VzLCaMEefv4ZTacrcVR4DmgTeBra++jMDAfS/tS799YDw==",
             "license": "MIT",
             "dependencies": {
-                "is-callable": "^1.1.3"
+                "is-callable": "^1.2.7"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
             }
         },
         "node_modules/for-each-property": {
@@ -22069,6 +22110,12 @@
                 "node": ">= 0.6"
             }
         },
+        "node_modules/fs-constants": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
+            "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==",
+            "license": "MIT"
+        },
         "node_modules/fs-extra": {
             "version": "10.1.0",
             "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
@@ -22083,36 +22130,6 @@
                 "node": ">=12"
             }
         },
-        "node_modules/fs-minipass": {
-            "version": "2.1.0",
-            "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
-            "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
-            "license": "ISC",
-            "dependencies": {
-                "minipass": "^3.0.0"
-            },
-            "engines": {
-                "node": ">= 8"
-            }
-        },
-        "node_modules/fs-minipass/node_modules/minipass": {
-            "version": "3.3.6",
-            "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
-            "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
-            "license": "ISC",
-            "dependencies": {
-                "yallist": "^4.0.0"
-            },
-            "engines": {
-                "node": ">=8"
-            }
-        },
-        "node_modules/fs-minipass/node_modules/yallist": {
-            "version": "4.0.0",
-            "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
-            "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
-            "license": "ISC"
-        },
         "node_modules/fs-monkey": {
             "version": "1.0.6",
             "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.6.tgz",
@@ -22227,74 +22244,6 @@
             "integrity": "sha512-vAcPiyomt1ioKAsAL2uxSABHJ4Ju/e4UeDM+g1OlR0vV4YhLGMNsdLNvZTpEDY4JCSt0E4hASCNM5t2ETtsbyg==",
             "license": "ISC"
         },
-        "node_modules/gauge": {
-            "version": "3.0.2",
-            "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz",
-            "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==",
-            "deprecated": "This package is no longer supported.",
-            "license": "ISC",
-            "dependencies": {
-                "aproba": "^1.0.3 || ^2.0.0",
-                "color-support": "^1.1.2",
-                "console-control-strings": "^1.0.0",
-                "has-unicode": "^2.0.1",
-                "object-assign": "^4.1.1",
-                "signal-exit": "^3.0.0",
-                "string-width": "^4.2.3",
-                "strip-ansi": "^6.0.1",
-                "wide-align": "^1.1.2"
-            },
-            "engines": {
-                "node": ">=10"
-            }
-        },
-        "node_modules/gauge/node_modules/ansi-regex": {
-            "version": "5.0.1",
-            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
-            "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
-            "license": "MIT",
-            "engines": {
-                "node": ">=8"
-            }
-        },
-        "node_modules/gauge/node_modules/emoji-regex": {
-            "version": "8.0.0",
-            "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
-            "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
-            "license": "MIT"
-        },
-        "node_modules/gauge/node_modules/signal-exit": {
-            "version": "3.0.7",
-            "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
-            "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
-            "license": "ISC"
-        },
-        "node_modules/gauge/node_modules/string-width": {
-            "version": "4.2.3",
-            "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
-            "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
-            "license": "MIT",
-            "dependencies": {
-                "emoji-regex": "^8.0.0",
-                "is-fullwidth-code-point": "^3.0.0",
-                "strip-ansi": "^6.0.1"
-            },
-            "engines": {
-                "node": ">=8"
-            }
-        },
-        "node_modules/gauge/node_modules/strip-ansi": {
-            "version": "6.0.1",
-            "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
-            "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
-            "license": "MIT",
-            "dependencies": {
-                "ansi-regex": "^5.0.1"
-            },
-            "engines": {
-                "node": ">=8"
-            }
-        },
         "node_modules/gaxios": {
             "version": "6.7.1",
             "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.7.1.tgz",
@@ -22591,6 +22540,12 @@
                 "omggif": "^1.0.10"
             }
         },
+        "node_modules/github-from-package": {
+            "version": "0.0.0",
+            "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz",
+            "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==",
+            "license": "MIT"
+        },
         "node_modules/gl-matrix": {
             "version": "3.4.3",
             "resolved": "https://registry.npmjs.org/gl-matrix/-/gl-matrix-3.4.3.tgz",
@@ -22728,9 +22683,9 @@
             "license": "MIT"
         },
         "node_modules/google-auth-library": {
-            "version": "9.15.0",
-            "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.15.0.tgz",
-            "integrity": "sha512-7ccSEJFDFO7exFbO6NRyC+xH8/mZ1GZGG2xxx9iHxZWcjUjJpjWxIMw3cofAKcueZ6DATiukmmprD7yavQHOyQ==",
+            "version": "9.15.1",
+            "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.15.1.tgz",
+            "integrity": "sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng==",
             "license": "Apache-2.0",
             "dependencies": {
                 "base64-js": "^1.3.0",
@@ -23018,12 +22973,6 @@
                 "url": "https://github.com/sponsors/ljharb"
             }
         },
-        "node_modules/has-unicode": {
-            "version": "2.0.1",
-            "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
-            "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==",
-            "license": "ISC"
-        },
         "node_modules/hasown": {
             "version": "2.0.2",
             "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
@@ -23575,9 +23524,9 @@
             }
         },
         "node_modules/http-parser-js": {
-            "version": "0.5.8",
-            "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz",
-            "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==",
+            "version": "0.5.9",
+            "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.9.tgz",
+            "integrity": "sha512-n1XsPy3rXVxlqxVioEWdC+0+M+SQw0DpJynwtOPo1X+ZlvdzTLtDBIJJlDQTnwZIFJrZSzSGmIOUdP8tu+SgLw==",
             "dev": true,
             "license": "MIT"
         },
@@ -23722,12 +23671,6 @@
                 "postcss": "^8.1.0"
             }
         },
-        "node_modules/idb-keyval": {
-            "version": "6.2.1",
-            "resolved": "https://registry.npmjs.org/idb-keyval/-/idb-keyval-6.2.1.tgz",
-            "integrity": "sha512-8Sb3veuYCyrZL+VBt9LJfZjLUPWVvqn8tG28VqYNFCo43KHcKuq+b4EiXGeuaLAQWL2YmyDgMp2aSpH9JHsEQg==",
-            "license": "Apache-2.0"
-        },
         "node_modules/ieee754": {
             "version": "1.2.1",
             "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
@@ -24334,14 +24277,14 @@
             }
         },
         "node_modules/intl-messageformat": {
-            "version": "10.7.11",
-            "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.7.11.tgz",
-            "integrity": "sha512-IB2N1tmI24k2EFH3PWjU7ivJsnWyLwOWOva0jnXFa29WzB6fb0JZ5EMQGu+XN5lDtjHYFo0/UooP67zBwUg7rQ==",
+            "version": "10.7.14",
+            "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.7.14.tgz",
+            "integrity": "sha512-mMGnE4E1otdEutV5vLUdCxRJygHB5ozUBxsPB5qhitewssrS/qGruq9bmvIRkkGsNeK5ZWLfYRld18UHGTIifQ==",
             "license": "BSD-3-Clause",
             "dependencies": {
                 "@formatjs/ecma402-abstract": "2.3.2",
                 "@formatjs/fast-memoize": "2.2.6",
-                "@formatjs/icu-messageformat-parser": "2.9.8",
+                "@formatjs/icu-messageformat-parser": "2.11.0",
                 "tslib": "2"
             }
         },
@@ -24447,12 +24390,13 @@
             "license": "MIT"
         },
         "node_modules/is-async-function": {
-            "version": "2.1.0",
-            "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.0.tgz",
-            "integrity": "sha512-GExz9MtyhlZyXYLxzlJRj5WUCE661zhDa1Yna52CN57AJsymh+DvXXjyveSioqSRdxvUrdKdvqB1b5cVKsNpWQ==",
+            "version": "2.1.1",
+            "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz",
+            "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==",
             "dev": true,
             "license": "MIT",
             "dependencies": {
+                "async-function": "^1.0.0",
                 "call-bound": "^1.0.3",
                 "get-proto": "^1.0.1",
                 "has-tostringtag": "^1.0.2",
@@ -24604,12 +24548,6 @@
                 "url": "https://github.com/sponsors/sindresorhus"
             }
         },
-        "node_modules/is-electron": {
-            "version": "2.2.2",
-            "resolved": "https://registry.npmjs.org/is-electron/-/is-electron-2.2.2.tgz",
-            "integrity": "sha512-FO/Rhvz5tuw4MCWkpMzHFKWD2LsfHzIb7i6MdPYZ/KW7AlxawyLkqdy+jPZP1WubqEADE3O4FUENlJHDfQASRg==",
-            "license": "MIT"
-        },
         "node_modules/is-expression": {
             "version": "4.0.0",
             "resolved": "https://registry.npmjs.org/is-expression/-/is-expression-4.0.0.tgz",
@@ -24950,12 +24888,6 @@
                 "url": "https://github.com/sponsors/sindresorhus"
             }
         },
-        "node_modules/is-url": {
-            "version": "1.2.4",
-            "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz",
-            "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==",
-            "license": "MIT"
-        },
         "node_modules/is-utf8": {
             "version": "0.2.1",
             "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz",
@@ -25316,22 +25248,22 @@
             }
         },
         "node_modules/jsdom": {
-            "version": "25.0.1",
-            "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-25.0.1.tgz",
-            "integrity": "sha512-8i7LzZj7BF8uplX+ZyOlIz86V6TAsSs+np6m1kpW9u0JWi4z/1t+FzcK1aek+ybTnAC4KhBL4uXCNT0wcUIeCw==",
+            "version": "26.0.0",
+            "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-26.0.0.tgz",
+            "integrity": "sha512-BZYDGVAIriBWTpIxYzrXjv3E/4u8+/pSG5bQdIYCbNCGOvsPkDQfTVLAIXAf9ETdCpduCVTkDe2NNZ8NIwUVzw==",
             "license": "MIT",
             "dependencies": {
-                "cssstyle": "^4.1.0",
+                "cssstyle": "^4.2.1",
                 "data-urls": "^5.0.0",
                 "decimal.js": "^10.4.3",
-                "form-data": "^4.0.0",
+                "form-data": "^4.0.1",
                 "html-encoding-sniffer": "^4.0.0",
                 "http-proxy-agent": "^7.0.2",
-                "https-proxy-agent": "^7.0.5",
+                "https-proxy-agent": "^7.0.6",
                 "is-potential-custom-element-name": "^1.0.1",
-                "nwsapi": "^2.2.12",
-                "parse5": "^7.1.2",
-                "rrweb-cssom": "^0.7.1",
+                "nwsapi": "^2.2.16",
+                "parse5": "^7.2.1",
+                "rrweb-cssom": "^0.8.0",
                 "saxes": "^6.0.0",
                 "symbol-tree": "^3.2.4",
                 "tough-cookie": "^5.0.0",
@@ -25339,7 +25271,7 @@
                 "webidl-conversions": "^7.0.0",
                 "whatwg-encoding": "^3.1.1",
                 "whatwg-mimetype": "^4.0.0",
-                "whatwg-url": "^14.0.0",
+                "whatwg-url": "^14.1.0",
                 "ws": "^8.18.0",
                 "xml-name-validator": "^5.0.0"
             },
@@ -25347,7 +25279,7 @@
                 "node": ">=18"
             },
             "peerDependencies": {
-                "canvas": "^2.11.2"
+                "canvas": "^3.0.0"
             },
             "peerDependenciesMeta": {
                 "canvas": {
@@ -25586,9 +25518,9 @@
             }
         },
         "node_modules/katex": {
-            "version": "0.16.19",
-            "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.19.tgz",
-            "integrity": "sha512-3IA6DYVhxhBabjSLTNO9S4+OliA3Qvb8pBQXMfC4WxXJgLwZgnfDl0BmB4z6nBMdznBsZ+CGM8DrGZ5hcguDZg==",
+            "version": "0.16.21",
+            "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.21.tgz",
+            "integrity": "sha512-XvqR7FgOHtWupfMiigNzmh+MgUVmDGU2kXZm899ZkPfcuoPuFxyHmXsgATDpFZDAXCI8tvinaVcDo8PIIJSo4A==",
             "funding": [
                 "https://opencollective.com/katex",
                 "https://github.com/sponsors/katex"
@@ -25965,6 +25897,7 @@
             "version": "4.4.2",
             "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
             "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==",
+            "deprecated": "This package is deprecated. Use the optional chaining (?.) operator instead.",
             "license": "MIT"
         },
         "node_modules/lodash.merge": {
@@ -26019,9 +25952,9 @@
             }
         },
         "node_modules/loupe": {
-            "version": "3.1.2",
-            "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.2.tgz",
-            "integrity": "sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg==",
+            "version": "3.1.3",
+            "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.3.tgz",
+            "integrity": "sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==",
             "dev": true,
             "license": "MIT"
         },
@@ -26157,6 +26090,7 @@
             "version": "3.1.0",
             "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
             "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
+            "dev": true,
             "license": "MIT",
             "dependencies": {
                 "semver": "^6.0.0"
@@ -26183,9 +26117,9 @@
             "license": "MIT"
         },
         "node_modules/mapbox-gl": {
-            "version": "3.9.2",
-            "resolved": "https://registry.npmjs.org/mapbox-gl/-/mapbox-gl-3.9.2.tgz",
-            "integrity": "sha512-ADGW4rJOylwcy9+yGf8HM2sLXLh3An+9PZVQzdpXrSbSNt0dI7e49a6NY35Yf2wbd4hoVyR5M3hy7cQ9sFrC2w==",
+            "version": "3.9.4",
+            "resolved": "https://registry.npmjs.org/mapbox-gl/-/mapbox-gl-3.9.4.tgz",
+            "integrity": "sha512-IxfpdyNzjCMzkqj/q5OUamlF1QcS+IFEARteygEgao2B8l8+UF2ahpNRgHT2EpMSE8ma1bq4LKvr+EuJ6gqniw==",
             "license": "SEE LICENSE IN LICENSE.txt",
             "workspaces": [
                 "src/style-spec",
@@ -26552,9 +26486,9 @@
             }
         },
         "node_modules/mdast-util-mdx-jsx": {
-            "version": "3.1.3",
-            "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.1.3.tgz",
-            "integrity": "sha512-bfOjvNt+1AcbPLTFMFWY149nJz0OjmewJs3LQQ5pIyVGxP4CdOqNVJL6kTaM5c68p8q82Xv3nCyFfUnuEcH3UQ==",
+            "version": "3.2.0",
+            "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz",
+            "integrity": "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==",
             "license": "MIT",
             "dependencies": {
                 "@types/estree-jsx": "^1.0.0",
@@ -26773,9 +26707,9 @@
             }
         },
         "node_modules/mermaid/node_modules/stylis": {
-            "version": "4.3.4",
-            "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.4.tgz",
-            "integrity": "sha512-osIBl6BGUmSfDkyH2mB7EFvCJntXDrLhKjHTRj/rK6xLH0yuPrHULDRQzKokSOD4VoorhtKpfcfW1GAntu8now==",
+            "version": "4.3.5",
+            "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.5.tgz",
+            "integrity": "sha512-K7npNOKGRYuhAFFzkzMGfxFDpN6gDwf8hcMiE+uveTVbBgm93HrNP3ZDUpKqzZ4pG7TP6fmb+EMAQPjq9FqqvA==",
             "license": "MIT"
         },
         "node_modules/mermaid/node_modules/uuid": {
@@ -26950,9 +26884,9 @@
             }
         },
         "node_modules/micromark-extension-gfm-table": {
-            "version": "2.1.0",
-            "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.0.tgz",
-            "integrity": "sha512-Ub2ncQv+fwD70/l4ou27b4YzfNaCJOvyX4HxXU15m7mpYY+rjuWzsLIPZHJL253Z643RpbcP1oeIJlQ/SKW67g==",
+            "version": "2.1.1",
+            "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz",
+            "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==",
             "license": "MIT",
             "dependencies": {
                 "devlop": "^1.0.0",
@@ -27335,9 +27269,9 @@
             }
         },
         "node_modules/micromark-util-subtokenize": {
-            "version": "2.0.3",
-            "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.0.3.tgz",
-            "integrity": "sha512-VXJJuNxYWSoYL6AJ6OQECCFGhIU2GGHMw8tahogePBrjkG8aCCas3ibkp7RnVOSTClg2is05/R7maAhF1XyQMg==",
+            "version": "2.0.4",
+            "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.0.4.tgz",
+            "integrity": "sha512-N6hXjrin2GTJDe3MVjf5FuXpm12PGm80BrUAeub9XFXca8JZbP+oIwY4LJSVwFUCL1IPm/WwSVUN7goFHmSGGQ==",
             "funding": [
                 {
                     "type": "GitHub Sponsors",
@@ -27495,37 +27429,6 @@
                 "node": ">=16 || 14 >=14.17"
             }
         },
-        "node_modules/minizlib": {
-            "version": "2.1.2",
-            "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
-            "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
-            "license": "MIT",
-            "dependencies": {
-                "minipass": "^3.0.0",
-                "yallist": "^4.0.0"
-            },
-            "engines": {
-                "node": ">= 8"
-            }
-        },
-        "node_modules/minizlib/node_modules/minipass": {
-            "version": "3.3.6",
-            "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
-            "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
-            "license": "ISC",
-            "dependencies": {
-                "yallist": "^4.0.0"
-            },
-            "engines": {
-                "node": ">=8"
-            }
-        },
-        "node_modules/minizlib/node_modules/yallist": {
-            "version": "4.0.0",
-            "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
-            "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
-            "license": "ISC"
-        },
         "node_modules/mitt": {
             "version": "3.0.1",
             "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz",
@@ -27542,6 +27445,7 @@
             "version": "1.0.4",
             "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
             "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
+            "dev": true,
             "license": "MIT",
             "bin": {
                 "mkdirp": "bin/cmd.js"
@@ -27550,15 +27454,21 @@
                 "node": ">=10"
             }
         },
+        "node_modules/mkdirp-classic": {
+            "version": "0.5.3",
+            "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
+            "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==",
+            "license": "MIT"
+        },
         "node_modules/mlly": {
-            "version": "1.7.3",
-            "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.3.tgz",
-            "integrity": "sha512-xUsx5n/mN0uQf4V548PKQ+YShA4/IW0KI1dZhrNrPCLG+xizETbHTkOa1f8/xut9JRPp8kQuMnz0oqwkTiLo/A==",
+            "version": "1.7.4",
+            "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.4.tgz",
+            "integrity": "sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==",
             "license": "MIT",
             "dependencies": {
                 "acorn": "^8.14.0",
-                "pathe": "^1.1.2",
-                "pkg-types": "^1.2.1",
+                "pathe": "^2.0.1",
+                "pkg-types": "^1.3.0",
                 "ufo": "^1.5.4"
             }
         },
@@ -27638,9 +27548,9 @@
             }
         },
         "node_modules/mocha": {
-            "version": "10.8.2",
-            "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.8.2.tgz",
-            "integrity": "sha512-VZlYo/WE8t1tstuRmqgeyBgCbJc/lEdopaa+axcKzTBJ+UIdlAB9XnmvTCAH4pwR4ElNInaedhEBmZD8iCSVEg==",
+            "version": "11.1.0",
+            "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.1.0.tgz",
+            "integrity": "sha512-8uJR5RTC2NgpY3GrYcgpZrsEd9zKbPDpob1RezyR2upGHRQtHWofmzTMzTMSV6dru3tj5Ukt0+Vnq1qhFEEwAg==",
             "dev": true,
             "license": "MIT",
             "dependencies": {
@@ -27651,7 +27561,7 @@
                 "diff": "^5.2.0",
                 "escape-string-regexp": "^4.0.0",
                 "find-up": "^5.0.0",
-                "glob": "^8.1.0",
+                "glob": "^10.4.5",
                 "he": "^1.2.0",
                 "js-yaml": "^4.1.0",
                 "log-symbols": "^4.1.0",
@@ -27661,8 +27571,8 @@
                 "strip-json-comments": "^3.1.1",
                 "supports-color": "^8.1.1",
                 "workerpool": "^6.5.1",
-                "yargs": "^16.2.0",
-                "yargs-parser": "^20.2.9",
+                "yargs": "^17.7.2",
+                "yargs-parser": "^21.1.1",
                 "yargs-unparser": "^2.0.0"
             },
             "bin": {
@@ -27670,7 +27580,17 @@
                 "mocha": "bin/mocha.js"
             },
             "engines": {
-                "node": ">= 14.0.0"
+                "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+            }
+        },
+        "node_modules/mocha/node_modules/ansi-regex": {
+            "version": "5.0.1",
+            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+            "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+            "dev": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=8"
             }
         },
         "node_modules/mocha/node_modules/argparse": {
@@ -27680,27 +27600,28 @@
             "dev": true,
             "license": "Python-2.0"
         },
-        "node_modules/mocha/node_modules/glob": {
-            "version": "8.1.0",
-            "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz",
-            "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==",
-            "deprecated": "Glob versions prior to v9 are no longer supported",
+        "node_modules/mocha/node_modules/cliui": {
+            "version": "8.0.1",
+            "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
+            "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
             "dev": true,
             "license": "ISC",
             "dependencies": {
-                "fs.realpath": "^1.0.0",
-                "inflight": "^1.0.4",
-                "inherits": "2",
-                "minimatch": "^5.0.1",
-                "once": "^1.3.0"
+                "string-width": "^4.2.0",
+                "strip-ansi": "^6.0.1",
+                "wrap-ansi": "^7.0.0"
             },
             "engines": {
                 "node": ">=12"
-            },
-            "funding": {
-                "url": "https://github.com/sponsors/isaacs"
             }
         },
+        "node_modules/mocha/node_modules/emoji-regex": {
+            "version": "8.0.0",
+            "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+            "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+            "dev": true,
+            "license": "MIT"
+        },
         "node_modules/mocha/node_modules/js-yaml": {
             "version": "4.1.0",
             "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
@@ -27727,6 +27648,34 @@
                 "node": ">=10"
             }
         },
+        "node_modules/mocha/node_modules/string-width": {
+            "version": "4.2.3",
+            "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+            "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "emoji-regex": "^8.0.0",
+                "is-fullwidth-code-point": "^3.0.0",
+                "strip-ansi": "^6.0.1"
+            },
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/mocha/node_modules/strip-ansi": {
+            "version": "6.0.1",
+            "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+            "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "ansi-regex": "^5.0.1"
+            },
+            "engines": {
+                "node": ">=8"
+            }
+        },
         "node_modules/mocha/node_modules/supports-color": {
             "version": "8.1.1",
             "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
@@ -27743,6 +27692,53 @@
                 "url": "https://github.com/chalk/supports-color?sponsor=1"
             }
         },
+        "node_modules/mocha/node_modules/wrap-ansi": {
+            "version": "7.0.0",
+            "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+            "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "ansi-styles": "^4.0.0",
+                "string-width": "^4.1.0",
+                "strip-ansi": "^6.0.0"
+            },
+            "engines": {
+                "node": ">=10"
+            },
+            "funding": {
+                "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+            }
+        },
+        "node_modules/mocha/node_modules/yargs": {
+            "version": "17.7.2",
+            "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
+            "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "cliui": "^8.0.1",
+                "escalade": "^3.1.1",
+                "get-caller-file": "^2.0.5",
+                "require-directory": "^2.1.1",
+                "string-width": "^4.2.3",
+                "y18n": "^5.0.5",
+                "yargs-parser": "^21.1.1"
+            },
+            "engines": {
+                "node": ">=12"
+            }
+        },
+        "node_modules/mocha/node_modules/yargs-parser": {
+            "version": "21.1.1",
+            "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
+            "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
+            "dev": true,
+            "license": "ISC",
+            "engines": {
+                "node": ">=12"
+            }
+        },
         "node_modules/mongodb": {
             "version": "6.12.0",
             "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.12.0.tgz",
@@ -27790,53 +27786,19 @@
             }
         },
         "node_modules/mongodb-connection-string-url": {
-            "version": "3.0.1",
-            "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.1.tgz",
-            "integrity": "sha512-XqMGwRX0Lgn05TDB4PyG2h2kKO/FfWJyCzYQbIhXUxz7ETt0I/FqHjUeqj37irJ+Dl1ZtU82uYyj14u2XsZKfg==",
+            "version": "3.0.2",
+            "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.2.tgz",
+            "integrity": "sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA==",
             "license": "Apache-2.0",
             "dependencies": {
                 "@types/whatwg-url": "^11.0.2",
-                "whatwg-url": "^13.0.0"
-            }
-        },
-        "node_modules/mongodb-connection-string-url/node_modules/punycode": {
-            "version": "2.3.1",
-            "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
-            "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
-            "license": "MIT",
-            "engines": {
-                "node": ">=6"
-            }
-        },
-        "node_modules/mongodb-connection-string-url/node_modules/tr46": {
-            "version": "4.1.1",
-            "resolved": "https://registry.npmjs.org/tr46/-/tr46-4.1.1.tgz",
-            "integrity": "sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==",
-            "license": "MIT",
-            "dependencies": {
-                "punycode": "^2.3.0"
-            },
-            "engines": {
-                "node": ">=14"
-            }
-        },
-        "node_modules/mongodb-connection-string-url/node_modules/whatwg-url": {
-            "version": "13.0.0",
-            "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-13.0.0.tgz",
-            "integrity": "sha512-9WWbymnqj57+XEuqADHrCJ2eSXzn8WXIW/YSGaZtb2WKAInQ6CHfaUUcTyyver0p8BDg5StLQq8h1vtZuwmOig==",
-            "license": "MIT",
-            "dependencies": {
-                "tr46": "^4.1.1",
-                "webidl-conversions": "^7.0.0"
-            },
-            "engines": {
-                "node": ">=16"
+                "whatwg-url": "^14.1.0 || ^13.0.0"
             }
         },
         "node_modules/mongoose": {
-            "version": "8.9.3",
-            "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.9.3.tgz",
-            "integrity": "sha512-G50GNPdMqhoiRAJ/24GYAzg13yxXDD3FOOFeYiFwtHmHpAJem3hxbYIxAhLJGWbYEiUZL0qFMu2LXYkgGAmo+Q==",
+            "version": "8.9.5",
+            "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.9.5.tgz",
+            "integrity": "sha512-SPhOrgBm0nKV3b+IIHGqpUTOmgVL5Z3OO9AwkFEmvOZznXTvplbomstCnPOGAyungtRXE5pJTgKpKcZTdjeESg==",
             "license": "MIT",
             "dependencies": {
                 "bson": "^6.10.1",
@@ -27955,12 +27917,6 @@
             "integrity": "sha512-TvmkNhkv8yct0SVBSy+o8wYzXjE4Zz3PCesbfs8HiCXXdcTuocApFv11UWlNFWKYsP2okqrhb7JNlSm9InBhIw==",
             "license": "MIT"
         },
-        "node_modules/nan": {
-            "version": "2.22.0",
-            "resolved": "https://registry.npmjs.org/nan/-/nan-2.22.0.tgz",
-            "integrity": "sha512-nbajikzWTMwsW+eSsNm3QwlOs7het9gGJU5dDZzRTQGk03vyBOauxgI4VakDzE0PtsGTmXPsXTbbjVhRwR5mpw==",
-            "license": "MIT"
-        },
         "node_modules/nanoid": {
             "version": "3.3.8",
             "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz",
@@ -27979,6 +27935,12 @@
                 "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
             }
         },
+        "node_modules/napi-build-utils": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz",
+            "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==",
+            "license": "MIT"
+        },
         "node_modules/natural-compare": {
             "version": "1.4.0",
             "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
@@ -28054,6 +28016,30 @@
                 "tslib": "^2.0.3"
             }
         },
+        "node_modules/node-abi": {
+            "version": "3.73.0",
+            "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.73.0.tgz",
+            "integrity": "sha512-z8iYzQGBu35ZkTQ9mtR8RqugJZ9RCLn8fv3d7LsgDBzOijGQP3RdKTX4LA7LXw03ZhU5z0l4xfhIMgSES31+cg==",
+            "license": "MIT",
+            "dependencies": {
+                "semver": "^7.3.5"
+            },
+            "engines": {
+                "node": ">=10"
+            }
+        },
+        "node_modules/node-abi/node_modules/semver": {
+            "version": "7.6.3",
+            "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
+            "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
+            "license": "ISC",
+            "bin": {
+                "semver": "bin/semver.js"
+            },
+            "engines": {
+                "node": ">=10"
+            }
+        },
         "node_modules/node-abort-controller": {
             "version": "3.1.1",
             "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz",
@@ -28064,8 +28050,7 @@
             "version": "7.1.1",
             "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz",
             "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==",
-            "license": "MIT",
-            "optional": true
+            "license": "MIT"
         },
         "node_modules/node-domexception": {
             "version": "1.0.0",
@@ -28164,9 +28149,9 @@
             }
         },
         "node_modules/nodemailer": {
-            "version": "6.9.16",
-            "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.16.tgz",
-            "integrity": "sha512-psAuZdTIRN08HKVd/E8ObdV6NO7NTBY3KsC30F7M4H1OnmLCUNaS56FpYxyb26zWLSyYF9Ozch9KYHhHegsiOQ==",
+            "version": "6.10.0",
+            "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.10.0.tgz",
+            "integrity": "sha512-SQ3wZCExjeSatLE/HBaXS5vqUOQk6GtBdIIKxiFdmm01mOQZX/POJkO3SUX1wDiYcwUOJwT23scFSC9fY2H8IA==",
             "license": "MIT-0",
             "engines": {
                 "node": ">=6.0.0"
@@ -28255,21 +28240,6 @@
                 "node": ">=4"
             }
         },
-        "node_modules/nopt": {
-            "version": "5.0.0",
-            "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz",
-            "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==",
-            "license": "ISC",
-            "dependencies": {
-                "abbrev": "1"
-            },
-            "bin": {
-                "nopt": "bin/nopt.js"
-            },
-            "engines": {
-                "node": ">=6"
-            }
-        },
         "node_modules/normalize-path": {
             "version": "3.0.0",
             "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
@@ -30747,19 +30717,6 @@
             "inBundle": true,
             "license": "ISC"
         },
-        "node_modules/npmlog": {
-            "version": "5.0.1",
-            "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz",
-            "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==",
-            "deprecated": "This package is no longer supported.",
-            "license": "ISC",
-            "dependencies": {
-                "are-we-there-yet": "^2.0.0",
-                "console-control-strings": "^1.1.0",
-                "gauge": "^3.0.0",
-                "set-blocking": "^2.0.0"
-            }
-        },
         "node_modules/nth-check": {
             "version": "2.1.1",
             "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
@@ -31033,9 +30990,9 @@
             }
         },
         "node_modules/openai": {
-            "version": "4.77.4",
-            "resolved": "https://registry.npmjs.org/openai/-/openai-4.77.4.tgz",
-            "integrity": "sha512-rShjKsZ/HXm1cSxXt6iFeZxiCohrVShawt0aRRQmbb+z/EXcH4OouyQZP1ShyZMb63LJajpl8aGw3DzEi8Wh9Q==",
+            "version": "4.80.1",
+            "resolved": "https://registry.npmjs.org/openai/-/openai-4.80.1.tgz",
+            "integrity": "sha512-+6+bbXFwbIE88foZsBEt36bPkgZPdyFN82clAXG61gnHb2gXdZApDyRrcAHqEtpYICywpqaNo57kOm9dtnb7Cw==",
             "license": "Apache-2.0",
             "dependencies": {
                 "@types/node": "^18.11.18",
@@ -31050,18 +31007,22 @@
                 "openai": "bin/cli"
             },
             "peerDependencies": {
+                "ws": "^8.18.0",
                 "zod": "^3.23.8"
             },
             "peerDependenciesMeta": {
+                "ws": {
+                    "optional": true
+                },
                 "zod": {
                     "optional": true
                 }
             }
         },
         "node_modules/openai/node_modules/@types/node": {
-            "version": "18.19.70",
-            "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.70.tgz",
-            "integrity": "sha512-RE+K0+KZoEpDUbGGctnGdkrLFwi1eYKTlIHNl2Um98mUkGsm1u2Ff6Ltd0e8DktTtC98uy7rSj+hO8t/QuLoVQ==",
+            "version": "18.19.74",
+            "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.74.tgz",
+            "integrity": "sha512-HMwEkkifei3L605gFdV+/UwtpxP6JSzM+xFk2Ia6DNFSwSVBRh9qp5Tgf4lNFOMfPVuU0WnkcWpXZpgn5ufO4A==",
             "license": "MIT",
             "dependencies": {
                 "undici-types": "~5.26.4"
@@ -31092,13 +31053,13 @@
             "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
             "license": "MIT"
         },
-        "node_modules/opencollective-postinstall": {
-            "version": "2.0.3",
-            "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz",
-            "integrity": "sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==",
+        "node_modules/openai/node_modules/web-streams-polyfill": {
+            "version": "4.0.0-beta.3",
+            "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz",
+            "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==",
             "license": "MIT",
-            "bin": {
-                "opencollective-postinstall": "index.js"
+            "engines": {
+                "node": ">= 14"
             }
         },
         "node_modules/opentype.js": {
@@ -31624,9 +31585,9 @@
             }
         },
         "node_modules/pathe": {
-            "version": "1.1.2",
-            "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz",
-            "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==",
+            "version": "2.0.2",
+            "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.2.tgz",
+            "integrity": "sha512-15Ztpk+nov8DR524R4BF7uEuzESgzUEAV4Ah7CUMNGXdE5ELuvxElxGXndBl32vMSsWa1jpNf22Z+Er3sKwq+w==",
             "license": "MIT"
         },
         "node_modules/pathval": {
@@ -31960,14 +31921,14 @@
             }
         },
         "node_modules/pkg-types": {
-            "version": "1.3.0",
-            "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.0.tgz",
-            "integrity": "sha512-kS7yWjVFCkIw9hqdJBoMxDdzEngmkr5FXeWZZfQ6GoYacjVnsW6l2CcYW/0ThD0vF4LPJgVYnrg4d0uuhwYQbg==",
+            "version": "1.3.1",
+            "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz",
+            "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==",
             "license": "MIT",
             "dependencies": {
                 "confbox": "^0.1.8",
-                "mlly": "^1.7.3",
-                "pathe": "^1.1.2"
+                "mlly": "^1.7.4",
+                "pathe": "^2.0.1"
             }
         },
         "node_modules/please-upgrade-node": {
@@ -32058,9 +32019,9 @@
             }
         },
         "node_modules/postcss": {
-            "version": "8.4.49",
-            "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz",
-            "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==",
+            "version": "8.5.1",
+            "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.1.tgz",
+            "integrity": "sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==",
             "funding": [
                 {
                     "type": "opencollective",
@@ -32077,7 +32038,7 @@
             ],
             "license": "MIT",
             "dependencies": {
-                "nanoid": "^3.3.7",
+                "nanoid": "^3.3.8",
                 "picocolors": "^1.1.1",
                 "source-map-js": "^1.2.1"
             },
@@ -32179,6 +32140,74 @@
                 "url": "https://opencollective.com/preact"
             }
         },
+        "node_modules/prebuild-install": {
+            "version": "7.1.3",
+            "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz",
+            "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==",
+            "license": "MIT",
+            "dependencies": {
+                "detect-libc": "^2.0.0",
+                "expand-template": "^2.0.3",
+                "github-from-package": "0.0.0",
+                "minimist": "^1.2.3",
+                "mkdirp-classic": "^0.5.3",
+                "napi-build-utils": "^2.0.0",
+                "node-abi": "^3.3.0",
+                "pump": "^3.0.0",
+                "rc": "^1.2.7",
+                "simple-get": "^4.0.0",
+                "tar-fs": "^2.0.0",
+                "tunnel-agent": "^0.6.0"
+            },
+            "bin": {
+                "prebuild-install": "bin.js"
+            },
+            "engines": {
+                "node": ">=10"
+            }
+        },
+        "node_modules/prebuild-install/node_modules/readable-stream": {
+            "version": "3.6.2",
+            "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+            "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+            "license": "MIT",
+            "dependencies": {
+                "inherits": "^2.0.3",
+                "string_decoder": "^1.1.1",
+                "util-deprecate": "^1.0.1"
+            },
+            "engines": {
+                "node": ">= 6"
+            }
+        },
+        "node_modules/prebuild-install/node_modules/tar-fs": {
+            "version": "2.1.2",
+            "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.2.tgz",
+            "integrity": "sha512-EsaAXwxmx8UB7FRKqeozqEPop69DXcmYwTQwXvyAPF352HJsPdkVhvTaDPYqfNgruveJIJy3TA2l+2zj8LJIJA==",
+            "license": "MIT",
+            "dependencies": {
+                "chownr": "^1.1.1",
+                "mkdirp-classic": "^0.5.2",
+                "pump": "^3.0.0",
+                "tar-stream": "^2.1.4"
+            }
+        },
+        "node_modules/prebuild-install/node_modules/tar-stream": {
+            "version": "2.2.0",
+            "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
+            "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
+            "license": "MIT",
+            "dependencies": {
+                "bl": "^4.0.3",
+                "end-of-stream": "^1.4.1",
+                "fs-constants": "^1.0.0",
+                "inherits": "^2.0.3",
+                "readable-stream": "^3.1.1"
+            },
+            "engines": {
+                "node": ">=6"
+            }
+        },
         "node_modules/prelude-ls": {
             "version": "1.2.1",
             "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
@@ -32431,9 +32460,9 @@
             }
         },
         "node_modules/prosemirror-view": {
-            "version": "1.37.1",
-            "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.37.1.tgz",
-            "integrity": "sha512-MEAnjOdXU1InxEmhjgmEzQAikaS6lF3hD64MveTPpjOGNTl87iRLA1HupC/DEV6YuK7m4Q9DHFNTjwIVtqz5NA==",
+            "version": "1.37.2",
+            "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.37.2.tgz",
+            "integrity": "sha512-ApcyrfV/cRcaL65on7TQcfWElwLyOgIjnIynfAuV+fIdlpbSvSWRwfuPaH7T5mo4AbO/FID29qOtjiDIKGWyog==",
             "license": "MIT",
             "dependencies": {
                 "prosemirror-model": "^1.20.0",
@@ -32671,17 +32700,17 @@
             }
         },
         "node_modules/puppeteer": {
-            "version": "23.11.1",
-            "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-23.11.1.tgz",
-            "integrity": "sha512-53uIX3KR5en8l7Vd8n5DUv90Ae9QDQsyIthaUFVzwV6yU750RjqRznEtNMBT20VthqAdemnJN+hxVdmMHKt7Zw==",
+            "version": "24.1.1",
+            "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-24.1.1.tgz",
+            "integrity": "sha512-fuhceZ5HZuDXVuaMIRxUuDHfCJLmK0pXh8FlzVQ0/+OApStevxZhU5kAVeYFOEqeCF5OoAyZjcWbdQK27xW/9A==",
             "hasInstallScript": true,
             "license": "Apache-2.0",
             "dependencies": {
-                "@puppeteer/browsers": "2.6.1",
-                "chromium-bidi": "0.11.0",
+                "@puppeteer/browsers": "2.7.0",
+                "chromium-bidi": "1.1.0",
                 "cosmiconfig": "^9.0.0",
-                "devtools-protocol": "0.0.1367902",
-                "puppeteer-core": "23.11.1",
+                "devtools-protocol": "0.0.1380148",
+                "puppeteer-core": "24.1.1",
                 "typed-query-selector": "^2.12.0"
             },
             "bin": {
@@ -32692,15 +32721,15 @@
             }
         },
         "node_modules/puppeteer-core": {
-            "version": "23.11.1",
-            "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-23.11.1.tgz",
-            "integrity": "sha512-3HZ2/7hdDKZvZQ7dhhITOUg4/wOrDRjyK2ZBllRB0ZCOi9u0cwq1ACHDjBB+nX+7+kltHjQvBRdeY7+W0T+7Gg==",
+            "version": "24.1.1",
+            "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.1.1.tgz",
+            "integrity": "sha512-7FF3gq6bpIsbq3I8mfbodXh3DCzXagoz3l2eGv1cXooYU4g0P4mcHQVHuBD4iSZPXNg8WjzlP5kmRwK9UvwF0A==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@puppeteer/browsers": "2.6.1",
-                "chromium-bidi": "0.11.0",
+                "@puppeteer/browsers": "2.7.0",
+                "chromium-bidi": "1.1.0",
                 "debug": "^4.4.0",
-                "devtools-protocol": "0.0.1367902",
+                "devtools-protocol": "0.0.1380148",
                 "typed-query-selector": "^2.12.0",
                 "ws": "^8.18.0"
             },
@@ -32834,12 +32863,6 @@
             ],
             "license": "MIT"
         },
-        "node_modules/queue-tick": {
-            "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz",
-            "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==",
-            "license": "MIT"
-        },
         "node_modules/quick-lru": {
             "version": "5.1.1",
             "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz",
@@ -32984,6 +33007,21 @@
             "integrity": "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw==",
             "license": "ISC"
         },
+        "node_modules/rc": {
+            "version": "1.2.8",
+            "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
+            "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
+            "license": "(BSD-2-Clause OR MIT OR Apache-2.0)",
+            "dependencies": {
+                "deep-extend": "^0.6.0",
+                "ini": "~1.3.0",
+                "minimist": "^1.2.0",
+                "strip-json-comments": "~2.0.1"
+            },
+            "bin": {
+                "rc": "cli.js"
+            }
+        },
         "node_modules/rc-switch": {
             "version": "4.1.0",
             "resolved": "https://registry.npmjs.org/rc-switch/-/rc-switch-4.1.0.tgz",
@@ -33019,6 +33057,15 @@
             "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
             "license": "MIT"
         },
+        "node_modules/rc/node_modules/strip-json-comments": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
+            "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==",
+            "license": "MIT",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
         "node_modules/react": {
             "version": "18.3.1",
             "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
@@ -33032,50 +33079,50 @@
             }
         },
         "node_modules/react-aria": {
-            "version": "3.36.0",
-            "resolved": "https://registry.npmjs.org/react-aria/-/react-aria-3.36.0.tgz",
-            "integrity": "sha512-AK5XyIhAN+e5HDlwlF+YwFrOrVI7RYmZ6kg/o7ZprQjkYqYKapXeUpWscmNm/3H2kDboE5Z4ymUnK6ZhobLqOw==",
+            "version": "3.37.0",
+            "resolved": "https://registry.npmjs.org/react-aria/-/react-aria-3.37.0.tgz",
+            "integrity": "sha512-u3WUEMTcbQFaoHauHO3KhPaBYzEv1o42EdPcLAs05GBw9Q6Axlqwo73UFgMrsc2ElwLAZ4EKpSdWHLo1R5gfiw==",
             "license": "Apache-2.0",
             "dependencies": {
                 "@internationalized/string": "^3.2.5",
-                "@react-aria/breadcrumbs": "^3.5.19",
-                "@react-aria/button": "^3.11.0",
-                "@react-aria/calendar": "^3.6.0",
-                "@react-aria/checkbox": "^3.15.0",
-                "@react-aria/color": "^3.0.2",
-                "@react-aria/combobox": "^3.11.0",
-                "@react-aria/datepicker": "^3.12.0",
-                "@react-aria/dialog": "^3.5.20",
-                "@react-aria/disclosure": "^3.0.0",
-                "@react-aria/dnd": "^3.8.0",
-                "@react-aria/focus": "^3.19.0",
-                "@react-aria/gridlist": "^3.10.0",
-                "@react-aria/i18n": "^3.12.4",
-                "@react-aria/interactions": "^3.22.5",
-                "@react-aria/label": "^3.7.13",
-                "@react-aria/link": "^3.7.7",
-                "@react-aria/listbox": "^3.13.6",
-                "@react-aria/menu": "^3.16.0",
-                "@react-aria/meter": "^3.4.18",
-                "@react-aria/numberfield": "^3.11.9",
-                "@react-aria/overlays": "^3.24.0",
-                "@react-aria/progress": "^3.4.18",
-                "@react-aria/radio": "^3.10.10",
-                "@react-aria/searchfield": "^3.7.11",
-                "@react-aria/select": "^3.15.0",
-                "@react-aria/selection": "^3.21.0",
-                "@react-aria/separator": "^3.4.4",
-                "@react-aria/slider": "^3.7.14",
+                "@react-aria/breadcrumbs": "^3.5.20",
+                "@react-aria/button": "^3.11.1",
+                "@react-aria/calendar": "^3.7.0",
+                "@react-aria/checkbox": "^3.15.1",
+                "@react-aria/color": "^3.0.3",
+                "@react-aria/combobox": "^3.11.1",
+                "@react-aria/datepicker": "^3.13.0",
+                "@react-aria/dialog": "^3.5.21",
+                "@react-aria/disclosure": "^3.0.1",
+                "@react-aria/dnd": "^3.8.1",
+                "@react-aria/focus": "^3.19.1",
+                "@react-aria/gridlist": "^3.10.1",
+                "@react-aria/i18n": "^3.12.5",
+                "@react-aria/interactions": "^3.23.0",
+                "@react-aria/label": "^3.7.14",
+                "@react-aria/link": "^3.7.8",
+                "@react-aria/listbox": "^3.14.0",
+                "@react-aria/menu": "^3.17.0",
+                "@react-aria/meter": "^3.4.19",
+                "@react-aria/numberfield": "^3.11.10",
+                "@react-aria/overlays": "^3.25.0",
+                "@react-aria/progress": "^3.4.19",
+                "@react-aria/radio": "^3.10.11",
+                "@react-aria/searchfield": "^3.8.0",
+                "@react-aria/select": "^3.15.1",
+                "@react-aria/selection": "^3.22.0",
+                "@react-aria/separator": "^3.4.5",
+                "@react-aria/slider": "^3.7.15",
                 "@react-aria/ssr": "^3.9.7",
-                "@react-aria/switch": "^3.6.10",
-                "@react-aria/table": "^3.16.0",
-                "@react-aria/tabs": "^3.9.8",
-                "@react-aria/tag": "^3.4.8",
-                "@react-aria/textfield": "^3.15.0",
-                "@react-aria/tooltip": "^3.7.10",
-                "@react-aria/utils": "^3.26.0",
-                "@react-aria/visually-hidden": "^3.8.18",
-                "@react-types/shared": "^3.26.0"
+                "@react-aria/switch": "^3.6.11",
+                "@react-aria/table": "^3.16.1",
+                "@react-aria/tabs": "^3.9.9",
+                "@react-aria/tag": "^3.4.9",
+                "@react-aria/textfield": "^3.16.0",
+                "@react-aria/tooltip": "^3.7.11",
+                "@react-aria/utils": "^3.27.0",
+                "@react-aria/visually-hidden": "^3.8.19",
+                "@react-types/shared": "^3.27.0"
             },
             "peerDependencies": {
                 "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
@@ -33083,42 +33130,44 @@
             }
         },
         "node_modules/react-aria-components": {
-            "version": "1.5.0",
-            "resolved": "https://registry.npmjs.org/react-aria-components/-/react-aria-components-1.5.0.tgz",
-            "integrity": "sha512-wzf0g6cvWrqAJd4FkisAfFnslx6AJREgOd/NEmVE/RGuDxGTzss4awcwbo98rIVmqbTTFApiygy0SyWGrRZfDA==",
+            "version": "1.6.0",
+            "resolved": "https://registry.npmjs.org/react-aria-components/-/react-aria-components-1.6.0.tgz",
+            "integrity": "sha512-YfG9PUE7XrXtDDAqT4pLTGyYQaiHHTBFdAK/wNgGsypVnQSdzmyYlV3Ty8aHlZJI6hP9RWkbywvosXkU7KcPHg==",
             "license": "Apache-2.0",
             "dependencies": {
-                "@internationalized/date": "^3.6.0",
+                "@internationalized/date": "^3.7.0",
                 "@internationalized/string": "^3.2.5",
-                "@react-aria/collections": "3.0.0-alpha.6",
-                "@react-aria/color": "^3.0.2",
-                "@react-aria/disclosure": "^3.0.0",
-                "@react-aria/dnd": "^3.8.0",
-                "@react-aria/focus": "^3.19.0",
-                "@react-aria/interactions": "^3.22.5",
+                "@react-aria/autocomplete": "3.0.0-alpha.37",
+                "@react-aria/collections": "3.0.0-alpha.7",
+                "@react-aria/color": "^3.0.3",
+                "@react-aria/disclosure": "^3.0.1",
+                "@react-aria/dnd": "^3.8.1",
+                "@react-aria/focus": "^3.19.1",
+                "@react-aria/interactions": "^3.23.0",
                 "@react-aria/live-announcer": "^3.4.1",
-                "@react-aria/menu": "^3.16.0",
-                "@react-aria/toolbar": "3.0.0-beta.11",
-                "@react-aria/tree": "3.0.0-beta.2",
-                "@react-aria/utils": "^3.26.0",
-                "@react-aria/virtualizer": "^4.1.0",
-                "@react-stately/color": "^3.8.1",
-                "@react-stately/disclosure": "^3.0.0",
-                "@react-stately/layout": "^4.1.0",
-                "@react-stately/menu": "^3.9.0",
-                "@react-stately/selection": "^3.18.0",
-                "@react-stately/table": "^3.13.0",
+                "@react-aria/menu": "^3.17.0",
+                "@react-aria/toolbar": "3.0.0-beta.12",
+                "@react-aria/tree": "3.0.0-beta.3",
+                "@react-aria/utils": "^3.27.0",
+                "@react-aria/virtualizer": "^4.1.1",
+                "@react-stately/autocomplete": "3.0.0-alpha.0",
+                "@react-stately/color": "^3.8.2",
+                "@react-stately/disclosure": "^3.0.1",
+                "@react-stately/layout": "^4.1.1",
+                "@react-stately/menu": "^3.9.1",
+                "@react-stately/selection": "^3.19.0",
+                "@react-stately/table": "^3.13.1",
                 "@react-stately/utils": "^3.10.5",
-                "@react-stately/virtualizer": "^4.2.0",
-                "@react-types/color": "^3.0.1",
-                "@react-types/form": "^3.7.8",
-                "@react-types/grid": "^3.2.10",
-                "@react-types/shared": "^3.26.0",
-                "@react-types/table": "^3.10.3",
+                "@react-stately/virtualizer": "^4.2.1",
+                "@react-types/color": "^3.0.2",
+                "@react-types/form": "^3.7.9",
+                "@react-types/grid": "^3.2.11",
+                "@react-types/shared": "^3.27.0",
+                "@react-types/table": "^3.10.4",
                 "@swc/helpers": "^0.5.0",
                 "client-only": "^0.0.1",
-                "react-aria": "^3.36.0",
-                "react-stately": "^3.34.0",
+                "react-aria": "^3.37.0",
+                "react-stately": "^3.35.0",
                 "use-sync-external-store": "^1.2.0"
             },
             "peerDependencies": {
@@ -33167,9 +33216,9 @@
             }
         },
         "node_modules/react-awesome-reveal/node_modules/react-intersection-observer": {
-            "version": "9.14.1",
-            "resolved": "https://registry.npmjs.org/react-intersection-observer/-/react-intersection-observer-9.14.1.tgz",
-            "integrity": "sha512-k1xIUn3sCQi3ugNeF64FJb3zwve5mcetvAUR9JazXeOmtap4IP2evN8rs+yf6SQ7F1QydsOGiqTmt+lySKZ9uA==",
+            "version": "9.15.1",
+            "resolved": "https://registry.npmjs.org/react-intersection-observer/-/react-intersection-observer-9.15.1.tgz",
+            "integrity": "sha512-vGrqYEVWXfH+AGu241uzfUpNK4HAdhCkSAyFdkMb9VWWXs6mxzBLpWCxEy9YcnDNY2g9eO6z7qUtTBdA9hc8pA==",
             "license": "MIT",
             "peerDependencies": {
                 "react": "^17.0.0 || ^18.0.0 || ^19.0.0",
@@ -33266,9 +33315,9 @@
             }
         },
         "node_modules/react-docgen": {
-            "version": "7.1.0",
-            "resolved": "https://registry.npmjs.org/react-docgen/-/react-docgen-7.1.0.tgz",
-            "integrity": "sha512-APPU8HB2uZnpl6Vt/+0AFoVYgSRtfiP6FLrZgPPTDmqSb2R4qZRbgd0A3VzIFxDt5e+Fozjx79WjLWnF69DK8g==",
+            "version": "7.1.1",
+            "resolved": "https://registry.npmjs.org/react-docgen/-/react-docgen-7.1.1.tgz",
+            "integrity": "sha512-hlSJDQ2synMPKFZOsKo9Hi8WWZTC7POR8EmWvTSjow+VDgKzkmjQvFm2fk0tmRw+f0vTOIYKlarR0iL4996pdg==",
             "dev": true,
             "license": "MIT",
             "dependencies": {
@@ -33428,9 +33477,9 @@
             }
         },
         "node_modules/react-map-gl": {
-            "version": "7.1.8",
-            "resolved": "https://registry.npmjs.org/react-map-gl/-/react-map-gl-7.1.8.tgz",
-            "integrity": "sha512-zwF16XMOdOKH4py5ehS1bgQIChqW8UN3b1bXps+JnADbYLSbOoUPQ3tNw0EZ2OTBWArR5aaQlhlEqg4lE47T8A==",
+            "version": "7.1.9",
+            "resolved": "https://registry.npmjs.org/react-map-gl/-/react-map-gl-7.1.9.tgz",
+            "integrity": "sha512-KsCc8Gyn05wVGlHZoopaiiCr0RCAQ6LDISo5sEy1/pV/d7RlozkF946tiX7IgyijJQMRujHol5QdwUPESjh73w==",
             "license": "MIT",
             "dependencies": {
                 "@maplibre/maplibre-gl-style-spec": "^19.2.1",
@@ -33438,7 +33487,7 @@
             },
             "peerDependencies": {
                 "mapbox-gl": ">=1.13.0",
-                "maplibre-gl": ">=1.13.0",
+                "maplibre-gl": ">=1.13.0 <5.0.0",
                 "react": ">=16.3.0",
                 "react-dom": ">=16.3.0"
             },
@@ -33507,9 +33556,9 @@
             }
         },
         "node_modules/react-select": {
-            "version": "5.9.0",
-            "resolved": "https://registry.npmjs.org/react-select/-/react-select-5.9.0.tgz",
-            "integrity": "sha512-nwRKGanVHGjdccsnzhFte/PULziueZxGD8LL2WojON78Mvnq7LdAMEtu2frrwld1fr3geixg3iiMBIc/LLAZpw==",
+            "version": "5.10.0",
+            "resolved": "https://registry.npmjs.org/react-select/-/react-select-5.10.0.tgz",
+            "integrity": "sha512-k96gw+i6N3ExgDwPIg0lUPmexl1ygPe6u5BdQFNBhkpbwroIgCNXdubtIzHfThYXYYTubwOBafoMnn7ruEP1xA==",
             "license": "MIT",
             "dependencies": {
                 "@babel/runtime": "^7.12.0",
@@ -33561,45 +33610,45 @@
             }
         },
         "node_modules/react-stately": {
-            "version": "3.34.0",
-            "resolved": "https://registry.npmjs.org/react-stately/-/react-stately-3.34.0.tgz",
-            "integrity": "sha512-0N9tZ8qQ/CxpJH7ao0O6gr+8955e7VrOskg9N+TIxkFknPetwOCtgppMYhnTfteBV8WfM/vv4OC1NbkgYTqXJA==",
-            "license": "Apache-2.0",
-            "dependencies": {
-                "@react-stately/calendar": "^3.6.0",
-                "@react-stately/checkbox": "^3.6.10",
-                "@react-stately/collections": "^3.12.0",
-                "@react-stately/color": "^3.8.1",
-                "@react-stately/combobox": "^3.10.1",
-                "@react-stately/data": "^3.12.0",
-                "@react-stately/datepicker": "^3.11.0",
-                "@react-stately/disclosure": "^3.0.0",
-                "@react-stately/dnd": "^3.5.0",
-                "@react-stately/form": "^3.1.0",
-                "@react-stately/list": "^3.11.1",
-                "@react-stately/menu": "^3.9.0",
-                "@react-stately/numberfield": "^3.9.8",
-                "@react-stately/overlays": "^3.6.12",
-                "@react-stately/radio": "^3.10.9",
-                "@react-stately/searchfield": "^3.5.8",
-                "@react-stately/select": "^3.6.9",
-                "@react-stately/selection": "^3.18.0",
-                "@react-stately/slider": "^3.6.0",
-                "@react-stately/table": "^3.13.0",
-                "@react-stately/tabs": "^3.7.0",
-                "@react-stately/toggle": "^3.8.0",
-                "@react-stately/tooltip": "^3.5.0",
-                "@react-stately/tree": "^3.8.6",
-                "@react-types/shared": "^3.26.0"
+            "version": "3.35.0",
+            "resolved": "https://registry.npmjs.org/react-stately/-/react-stately-3.35.0.tgz",
+            "integrity": "sha512-1BH21J/TOHpyZe7c+f1BU2bnRWaBDTjLH0WdBuzNfPOXu7RBG3ebPIRvqd7UkPaVfIcol2QJnxe8S0a314JWKA==",
+            "license": "Apache-2.0",
+            "dependencies": {
+                "@react-stately/calendar": "^3.7.0",
+                "@react-stately/checkbox": "^3.6.11",
+                "@react-stately/collections": "^3.12.1",
+                "@react-stately/color": "^3.8.2",
+                "@react-stately/combobox": "^3.10.2",
+                "@react-stately/data": "^3.12.1",
+                "@react-stately/datepicker": "^3.12.0",
+                "@react-stately/disclosure": "^3.0.1",
+                "@react-stately/dnd": "^3.5.1",
+                "@react-stately/form": "^3.1.1",
+                "@react-stately/list": "^3.11.2",
+                "@react-stately/menu": "^3.9.1",
+                "@react-stately/numberfield": "^3.9.9",
+                "@react-stately/overlays": "^3.6.13",
+                "@react-stately/radio": "^3.10.10",
+                "@react-stately/searchfield": "^3.5.9",
+                "@react-stately/select": "^3.6.10",
+                "@react-stately/selection": "^3.19.0",
+                "@react-stately/slider": "^3.6.1",
+                "@react-stately/table": "^3.13.1",
+                "@react-stately/tabs": "^3.7.1",
+                "@react-stately/toggle": "^3.8.1",
+                "@react-stately/tooltip": "^3.5.1",
+                "@react-stately/tree": "^3.8.7",
+                "@react-types/shared": "^3.27.0"
             },
             "peerDependencies": {
                 "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
             }
         },
         "node_modules/react-textarea-autosize": {
-            "version": "8.5.6",
-            "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.5.6.tgz",
-            "integrity": "sha512-aT3ioKXMa8f6zHYGebhbdMD2L00tKeRX1zuVuDx9YQK/JLLRSaSxq3ugECEmUB9z2kvk6bFSIoRHLkkUv0RJiw==",
+            "version": "8.5.7",
+            "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.5.7.tgz",
+            "integrity": "sha512-2MqJ3p0Jh69yt9ktFIaZmORHXw4c4bxSIhCeWiFwmJ9EYKgLmuNII3e9c9b2UO+ijl4StnpZdqpxNIhTdHvqtQ==",
             "license": "MIT",
             "dependencies": {
                 "@babel/runtime": "^7.20.13",
@@ -33811,16 +33860,16 @@
             }
         },
         "node_modules/recharts": {
-            "version": "2.15.0",
-            "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.15.0.tgz",
-            "integrity": "sha512-cIvMxDfpAmqAmVgc4yb7pgm/O1tmmkl/CjrvXuW+62/+7jj/iF9Ykm+hb/UJt42TREHMyd3gb+pkgoa2MxgDIw==",
+            "version": "2.15.1",
+            "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.15.1.tgz",
+            "integrity": "sha512-v8PUTUlyiDe56qUj82w/EDVuzEFXwEHp9/xOowGAZwfLjB9uAy3GllQVIYMWF6nU+qibx85WF75zD7AjqoT54Q==",
             "license": "MIT",
             "dependencies": {
                 "clsx": "^2.0.0",
                 "eventemitter3": "^4.0.1",
                 "lodash": "^4.17.21",
                 "react-is": "^18.3.1",
-                "react-smooth": "^4.0.0",
+                "react-smooth": "^4.0.4",
                 "recharts-scale": "^0.4.4",
                 "tiny-invariant": "^1.3.1",
                 "victory-vendor": "^36.6.8"
@@ -34588,9 +34637,9 @@
             }
         },
         "node_modules/rimraf/node_modules/glob": {
-            "version": "11.0.0",
-            "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.0.tgz",
-            "integrity": "sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g==",
+            "version": "11.0.1",
+            "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.1.tgz",
+            "integrity": "sha512-zrQDm8XPnYEKawJScsnM0QzobJxlT/kHOOlRTio8IH/GrmxRE5fjllkzdaHclIuNjUQTJYH2xHNIGfdpJkDJUw==",
             "license": "ISC",
             "dependencies": {
                 "foreground-child": "^3.1.0",
@@ -34690,9 +34739,9 @@
             }
         },
         "node_modules/rrweb-cssom": {
-            "version": "0.7.1",
-            "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.7.1.tgz",
-            "integrity": "sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==",
+            "version": "0.8.0",
+            "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz",
+            "integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==",
             "license": "MIT"
         },
         "node_modules/run-applescript": {
@@ -34819,9 +34868,9 @@
             "license": "MIT"
         },
         "node_modules/sass": {
-            "version": "1.83.1",
-            "resolved": "https://registry.npmjs.org/sass/-/sass-1.83.1.tgz",
-            "integrity": "sha512-EVJbDaEs4Rr3F0glJzFSOvtg2/oy2V/YrGFPqPY24UqcLDWcI9ZY5sN+qyO3c/QCZwzgfirvhXvINiJCE/OLcA==",
+            "version": "1.83.4",
+            "resolved": "https://registry.npmjs.org/sass/-/sass-1.83.4.tgz",
+            "integrity": "sha512-B1bozCeNQiOgDcLd33e2Cs2U60wZwjUUXzh900ZyQF5qUasvMdDZYbQ566LJu7cqR+sAHlAfO6RMkaID5s6qpA==",
             "license": "MIT",
             "dependencies": {
                 "chokidar": "^4.0.0",
@@ -34894,12 +34943,12 @@
             }
         },
         "node_modules/sass/node_modules/readdirp": {
-            "version": "4.0.2",
-            "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.2.tgz",
-            "integrity": "sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==",
+            "version": "4.1.1",
+            "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.1.tgz",
+            "integrity": "sha512-h80JrZu/MHUZCyHu5ciuoI0+WxsCxzxJTILn6Fs8rxSnFPh+UVHYfeIxK1nVGugMqkfC4vJcBOYbkfkwYK0+gw==",
             "license": "MIT",
             "engines": {
-                "node": ">= 14.16.0"
+                "node": ">= 14.18.0"
             },
             "funding": {
                 "type": "individual",
@@ -35254,12 +35303,6 @@
                 "node": ">= 0.8.0"
             }
         },
-        "node_modules/set-blocking": {
-            "version": "2.0.0",
-            "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
-            "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==",
-            "license": "ISC"
-        },
         "node_modules/set-function-length": {
             "version": "1.2.2",
             "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
@@ -35573,40 +35616,30 @@
             "license": "MIT"
         },
         "node_modules/simple-get": {
-            "version": "3.1.1",
-            "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz",
-            "integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==",
+            "version": "4.0.1",
+            "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz",
+            "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==",
+            "funding": [
+                {
+                    "type": "github",
+                    "url": "https://github.com/sponsors/feross"
+                },
+                {
+                    "type": "patreon",
+                    "url": "https://www.patreon.com/feross"
+                },
+                {
+                    "type": "consulting",
+                    "url": "https://feross.org/support"
+                }
+            ],
             "license": "MIT",
             "dependencies": {
-                "decompress-response": "^4.2.0",
+                "decompress-response": "^6.0.0",
                 "once": "^1.3.1",
                 "simple-concat": "^1.0.0"
             }
         },
-        "node_modules/simple-get/node_modules/decompress-response": {
-            "version": "4.2.1",
-            "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz",
-            "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==",
-            "license": "MIT",
-            "dependencies": {
-                "mimic-response": "^2.0.0"
-            },
-            "engines": {
-                "node": ">=8"
-            }
-        },
-        "node_modules/simple-get/node_modules/mimic-response": {
-            "version": "2.1.0",
-            "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz",
-            "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==",
-            "license": "MIT",
-            "engines": {
-                "node": ">=8"
-            },
-            "funding": {
-                "url": "https://github.com/sponsors/sindresorhus"
-            }
-        },
         "node_modules/simple-swizzle": {
             "version": "0.2.2",
             "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
@@ -36154,13 +36187,13 @@
             }
         },
         "node_modules/storybook": {
-            "version": "8.4.7",
-            "resolved": "https://registry.npmjs.org/storybook/-/storybook-8.4.7.tgz",
-            "integrity": "sha512-RP/nMJxiWyFc8EVMH5gp20ID032Wvk+Yr3lmKidoegto5Iy+2dVQnUoElZb2zpbVXNHWakGuAkfI0dY1Hfp/vw==",
+            "version": "8.5.2",
+            "resolved": "https://registry.npmjs.org/storybook/-/storybook-8.5.2.tgz",
+            "integrity": "sha512-pf84emQ7Pd5jBdT2gzlNs4kRaSI3pq0Lh8lSfV+YqIVXztXIHU+Lqyhek2Lhjb7btzA1tExrhJrgQUsIji7i7A==",
             "dev": true,
             "license": "MIT",
             "dependencies": {
-                "@storybook/core": "8.4.7"
+                "@storybook/core": "8.5.2"
             },
             "bin": {
                 "getstorybook": "bin/index.cjs",
@@ -36238,13 +36271,12 @@
             }
         },
         "node_modules/streamx": {
-            "version": "2.21.1",
-            "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.21.1.tgz",
-            "integrity": "sha512-PhP9wUnFLa+91CPy3N6tiQsK+gnYyUNuk15S3YG/zjYE7RuPeCjJngqnzpC31ow0lzBHQ+QGO4cNJnd0djYUsw==",
+            "version": "2.22.0",
+            "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.22.0.tgz",
+            "integrity": "sha512-sLh1evHOzBy/iWRiR6d1zRcLao4gGZr3C1kzNz4fopCOKJb6xD9ub8Mpi9Mr1R6id5o43S+d93fI48UC5uM9aw==",
             "license": "MIT",
             "dependencies": {
                 "fast-fifo": "^1.3.2",
-                "queue-tick": "^1.0.1",
                 "text-decoder": "^1.1.0"
             },
             "optionalDependencies": {
@@ -36823,35 +36855,18 @@
                 "node": ">=6"
             }
         },
-        "node_modules/tar": {
-            "version": "6.2.1",
-            "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz",
-            "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==",
-            "license": "ISC",
-            "dependencies": {
-                "chownr": "^2.0.0",
-                "fs-minipass": "^2.0.0",
-                "minipass": "^5.0.0",
-                "minizlib": "^2.1.1",
-                "mkdirp": "^1.0.3",
-                "yallist": "^4.0.0"
-            },
-            "engines": {
-                "node": ">=10"
-            }
-        },
         "node_modules/tar-fs": {
-            "version": "3.0.6",
-            "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.6.tgz",
-            "integrity": "sha512-iokBDQQkUyeXhgPYaZxmczGPhnhXZ0CmrqI+MOb/WFGS9DW5wnfrLgtjUJBvz50vQ3qfRwJ62QVoCFu8mPVu5w==",
+            "version": "3.0.8",
+            "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.8.tgz",
+            "integrity": "sha512-ZoROL70jptorGAlgAYiLoBLItEKw/fUxg9BSYK/dF/GAGYFJOJJJMvjPAKDJraCXFwadD456FCuvLWgfhMsPwg==",
             "license": "MIT",
             "dependencies": {
                 "pump": "^3.0.0",
                 "tar-stream": "^3.1.5"
             },
             "optionalDependencies": {
-                "bare-fs": "^2.1.1",
-                "bare-path": "^2.1.0"
+                "bare-fs": "^4.0.1",
+                "bare-path": "^3.0.0"
             }
         },
         "node_modules/tar-stream": {
@@ -36865,21 +36880,6 @@
                 "streamx": "^2.15.0"
             }
         },
-        "node_modules/tar/node_modules/minipass": {
-            "version": "5.0.0",
-            "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz",
-            "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==",
-            "license": "ISC",
-            "engines": {
-                "node": ">=8"
-            }
-        },
-        "node_modules/tar/node_modules/yallist": {
-            "version": "4.0.0",
-            "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
-            "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
-            "license": "ISC"
-        },
         "node_modules/terser": {
             "version": "5.37.0",
             "resolved": "https://registry.npmjs.org/terser/-/terser-5.37.0.tgz",
@@ -36967,37 +36967,6 @@
             "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
             "license": "MIT"
         },
-        "node_modules/tesseract.js": {
-            "version": "5.1.1",
-            "resolved": "https://registry.npmjs.org/tesseract.js/-/tesseract.js-5.1.1.tgz",
-            "integrity": "sha512-lzVl/Ar3P3zhpUT31NjqeCo1f+D5+YfpZ5J62eo2S14QNVOmHBTtbchHm/YAbOOOzCegFnKf4B3Qih9LuldcYQ==",
-            "hasInstallScript": true,
-            "license": "Apache-2.0",
-            "dependencies": {
-                "bmp-js": "^0.1.0",
-                "idb-keyval": "^6.2.0",
-                "is-electron": "^2.2.2",
-                "is-url": "^1.2.4",
-                "node-fetch": "^2.6.9",
-                "opencollective-postinstall": "^2.0.3",
-                "regenerator-runtime": "^0.13.3",
-                "tesseract.js-core": "^5.1.1",
-                "wasm-feature-detect": "^1.2.11",
-                "zlibjs": "^0.3.1"
-            }
-        },
-        "node_modules/tesseract.js-core": {
-            "version": "5.1.1",
-            "resolved": "https://registry.npmjs.org/tesseract.js-core/-/tesseract.js-core-5.1.1.tgz",
-            "integrity": "sha512-KX3bYSU5iGcO1XJa+QGPbi+Zjo2qq6eBhNjSGR5E5q0JtzkoipJKOUQD7ph8kFyteCEfEQ0maWLu8MCXtvX5uQ==",
-            "license": "Apache-2.0"
-        },
-        "node_modules/tesseract.js/node_modules/regenerator-runtime": {
-            "version": "0.13.11",
-            "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
-            "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==",
-            "license": "MIT"
-        },
         "node_modules/text-decoder": {
             "version": "1.2.3",
             "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.3.tgz",
@@ -37104,21 +37073,21 @@
             }
         },
         "node_modules/tldts": {
-            "version": "6.1.71",
-            "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.71.tgz",
-            "integrity": "sha512-LQIHmHnuzfZgZWAf2HzL83TIIrD8NhhI0DVxqo9/FdOd4ilec+NTNZOlDZf7EwrTNoutccbsHjvWHYXLAtvxjw==",
+            "version": "6.1.75",
+            "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.75.tgz",
+            "integrity": "sha512-+lFzEXhpl7JXgWYaXcB6DqTYXbUArvrWAE/5ioq/X3CdWLbDjpPP4XTrQBmEJ91y3xbe4Fkw7Lxv4P3GWeJaNg==",
             "license": "MIT",
             "dependencies": {
-                "tldts-core": "^6.1.71"
+                "tldts-core": "^6.1.75"
             },
             "bin": {
                 "tldts": "bin/cli.js"
             }
         },
         "node_modules/tldts-core": {
-            "version": "6.1.71",
-            "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.71.tgz",
-            "integrity": "sha512-LRbChn2YRpic1KxY+ldL1pGXN/oVvKfCVufwfVzEQdFYNo39uF7AJa/WXdo+gYO7PTvdfkCPCed6Hkvz/kR7jg==",
+            "version": "6.1.75",
+            "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.75.tgz",
+            "integrity": "sha512-AOvV5YYIAFFBfransBzSTyztkc3IMfz5Eq3YluaRiEu55nn43Fzaufx70UqEKYr8BoLCach4q8g/bg6e5+/aFw==",
             "license": "MIT"
         },
         "node_modules/to-regex-range": {
@@ -37213,9 +37182,9 @@
             }
         },
         "node_modules/tough-cookie": {
-            "version": "5.0.0",
-            "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.0.0.tgz",
-            "integrity": "sha512-FRKsF7cz96xIIeMZ82ehjC3xW2E+O2+v11udrDYewUbszngYhsGa8z6YUMMzO9QJZzzyd0nGGXnML/TReX6W8Q==",
+            "version": "5.1.0",
+            "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.0.tgz",
+            "integrity": "sha512-rvZUv+7MoBYTiDmFPBrhL7Ujx9Sk+q9wwm22x8c8T5IJaR+Wsyc7TNxbVxo84kZoRJZZMazowFLqpankBEQrGg==",
             "license": "BSD-3-Clause",
             "dependencies": {
                 "tldts": "^6.1.32"
@@ -37326,9 +37295,9 @@
             }
         },
         "node_modules/ts-loader": {
-            "version": "9.5.1",
-            "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.1.tgz",
-            "integrity": "sha512-rNH3sK9kGZcH9dYzC7CewQm4NtxJTjSEVRJ2DyBZR7f8/wcta+iV44UPCXc5+nzDzivKtlzV6c9P4e+oFhDLYg==",
+            "version": "9.5.2",
+            "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.2.tgz",
+            "integrity": "sha512-Qo4piXvOTWcMGIgRiuFa6nHNm+54HbYaZCKqc9eeZCLRy3XqafQgwX2F7mofrbJG3g7EEb+lkiR+z2Lic2s3Zw==",
             "dev": true,
             "license": "MIT",
             "dependencies": {
@@ -37952,9 +37921,9 @@
             }
         },
         "node_modules/type-fest": {
-            "version": "4.31.0",
-            "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.31.0.tgz",
-            "integrity": "sha512-yCxltHW07Nkhv/1F6wWBr8kz+5BGMfP+RbRSYFnegVb0qV/UMT0G0ElBloPVerqn4M2ZV80Ir1FtCcYv1cT6vQ==",
+            "version": "4.33.0",
+            "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.33.0.tgz",
+            "integrity": "sha512-s6zVrxuyKbbAsSAD5ZPTB77q4YIdRctkTbJ2/Dqlinwz+8ooH2gd+YA7VA6Pa93KML9GockVvoxjZ2vHP+mu8g==",
             "license": "(MIT OR CC0-1.0)",
             "engines": {
                 "node": ">=16"
@@ -38061,9 +38030,9 @@
             "license": "MIT"
         },
         "node_modules/typescript": {
-            "version": "5.7.2",
-            "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz",
-            "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==",
+            "version": "5.7.3",
+            "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz",
+            "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==",
             "license": "Apache-2.0",
             "bin": {
                 "tsc": "bin/tsc",
@@ -38080,15 +38049,15 @@
             "license": "MIT"
         },
         "node_modules/typescript-eslint": {
-            "version": "8.19.1",
-            "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.19.1.tgz",
-            "integrity": "sha512-LKPUQpdEMVOeKluHi8md7rwLcoXHhwvWp3x+sJkMuq3gGm9yaYJtPo8sRZSblMFJ5pcOGCAak/scKf1mvZDlQw==",
+            "version": "8.22.0",
+            "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.22.0.tgz",
+            "integrity": "sha512-Y2rj210FW1Wb6TWXzQc5+P+EWI9/zdS57hLEc0gnyuvdzWo8+Y8brKlbj0muejonhMI/xAZCnZZwjbIfv1CkOw==",
             "dev": true,
             "license": "MIT",
             "dependencies": {
-                "@typescript-eslint/eslint-plugin": "8.19.1",
-                "@typescript-eslint/parser": "8.19.1",
-                "@typescript-eslint/utils": "8.19.1"
+                "@typescript-eslint/eslint-plugin": "8.22.0",
+                "@typescript-eslint/parser": "8.22.0",
+                "@typescript-eslint/utils": "8.22.0"
             },
             "engines": {
                 "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -38225,9 +38194,9 @@
             "license": "MIT"
         },
         "node_modules/undici": {
-            "version": "6.21.0",
-            "resolved": "https://registry.npmjs.org/undici/-/undici-6.21.0.tgz",
-            "integrity": "sha512-BUgJXc752Kou3oOIuU1i+yZZypyZRqNPW0vqoMPl8VaoalSfeR0D8/t4iAS3yirs79SSMTxTag+ZC86uswv+Cw==",
+            "version": "6.21.1",
+            "resolved": "https://registry.npmjs.org/undici/-/undici-6.21.1.tgz",
+            "integrity": "sha512-q/1rj5D0/zayJB2FraXdaWxbhWiNKDvu8naDT2dl1yTlvJp4BLtOcp2a5BvgGNQpYYJzau7tf1WgKv3b+7mqpQ==",
             "license": "MIT",
             "engines": {
                 "node": ">=18.17"
@@ -38485,9 +38454,9 @@
             }
         },
         "node_modules/unstructured-client": {
-            "version": "0.18.2",
-            "resolved": "https://registry.npmjs.org/unstructured-client/-/unstructured-client-0.18.2.tgz",
-            "integrity": "sha512-KwsrkZYtMyMHL44qQ46H3nss2kN8jmW92pb8opUJslByPf0tfMr8EIFWGxxHtcOoBuJRpt3BtjeE3dYip3UuSA==",
+            "version": "0.19.0",
+            "resolved": "https://registry.npmjs.org/unstructured-client/-/unstructured-client-0.19.0.tgz",
+            "integrity": "sha512-lW6IRmZPv8paWHYa18HVxv3N5i9pmPGzz16UyiO4T10VqlQHW8pO2966Fqa8URjr9SOjwu1fU6WAyV1slq6Q1w==",
             "dependencies": {
                 "async": "^3.2.5",
                 "pdf-lib": "^1.17.1"
@@ -38752,9 +38721,9 @@
             }
         },
         "node_modules/uuid": {
-            "version": "11.0.4",
-            "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.4.tgz",
-            "integrity": "sha512-IzL6VtTTYcAhA/oghbFJ1Dkmqev+FpQWnCBaKq/gUluLxliWvO8DPFWfIviRmYbtaavtSQe4WBL++rFjdcGWEg==",
+            "version": "11.0.5",
+            "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.5.tgz",
+            "integrity": "sha512-508e6IcKLrhxKdBbcA2b4KQZlLVp2+J5UwQ6F7Drckkc5N9ZJwFa4TgWtsww9UG8fGHbm6gbV19TdM5pQ4GaIA==",
             "funding": [
                 "https://github.com/sponsors/broofa",
                 "https://github.com/sponsors/ctavan"
@@ -38979,12 +38948,6 @@
                 "loose-envify": "^1.0.0"
             }
         },
-        "node_modules/wasm-feature-detect": {
-            "version": "1.8.0",
-            "resolved": "https://registry.npmjs.org/wasm-feature-detect/-/wasm-feature-detect-1.8.0.tgz",
-            "integrity": "sha512-zksaLKM2fVlnB5jQQDqKXXwYHLQUVH9es+5TOOHwGOVJOCeRBCiPjwSg+3tN2AdTCzjgli4jijCH290kXb/zWQ==",
-            "license": "Apache-2.0"
-        },
         "node_modules/watchpack": {
             "version": "2.4.2",
             "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz",
@@ -39028,12 +38991,14 @@
             }
         },
         "node_modules/web-streams-polyfill": {
-            "version": "4.0.0-beta.3",
-            "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz",
-            "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==",
+            "version": "4.1.0",
+            "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.1.0.tgz",
+            "integrity": "sha512-A7Jxrg7+eV+eZR/CIdESDnRGFb6/bcKukGvJBB5snI6cw3is1c2qamkYstC1bY1p08TyMRlN9eTMkxmnKJBPBw==",
             "license": "MIT",
+            "optional": true,
+            "peer": true,
             "engines": {
-                "node": ">= 14"
+                "node": ">= 8"
             }
         },
         "node_modules/webidl-conversions": {
@@ -39193,9 +39158,9 @@
             }
         },
         "node_modules/webpack-dev-middleware/node_modules/memfs": {
-            "version": "4.15.3",
-            "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.15.3.tgz",
-            "integrity": "sha512-vR/g1SgqvKJgAyYla+06G4p/EOcEmwhYuVb1yc1ixcKf8o/sh7Zngv63957ZSNd1xrZJoinmNyDf2LzuP8WJXw==",
+            "version": "4.17.0",
+            "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.17.0.tgz",
+            "integrity": "sha512-4eirfZ7thblFmqFjywlTmuWVSvccHAJbn1r8qQLzmTO11qcqpohOjmY2mFce6x7x7WtskzRqApPD0hv+Oa74jg==",
             "license": "Apache-2.0",
             "dependencies": {
                 "@jsonjoy.com/json-pack": "^1.0.3",
@@ -39660,56 +39625,6 @@
             "integrity": "sha512-Gd9+TUn5nXdwj/hFsPVx5cuHHiF5Bwuc30jZ4+ronF1qHK5O7HD0sgmXWSEgwKquT3ClLoKPVbO6qGwVwLzvAw==",
             "license": "MIT"
         },
-        "node_modules/wide-align": {
-            "version": "1.1.5",
-            "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz",
-            "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==",
-            "license": "ISC",
-            "dependencies": {
-                "string-width": "^1.0.2 || 2 || 3 || 4"
-            }
-        },
-        "node_modules/wide-align/node_modules/ansi-regex": {
-            "version": "5.0.1",
-            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
-            "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
-            "license": "MIT",
-            "engines": {
-                "node": ">=8"
-            }
-        },
-        "node_modules/wide-align/node_modules/emoji-regex": {
-            "version": "8.0.0",
-            "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
-            "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
-            "license": "MIT"
-        },
-        "node_modules/wide-align/node_modules/string-width": {
-            "version": "4.2.3",
-            "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
-            "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
-            "license": "MIT",
-            "dependencies": {
-                "emoji-regex": "^8.0.0",
-                "is-fullwidth-code-point": "^3.0.0",
-                "strip-ansi": "^6.0.1"
-            },
-            "engines": {
-                "node": ">=8"
-            }
-        },
-        "node_modules/wide-align/node_modules/strip-ansi": {
-            "version": "6.0.1",
-            "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
-            "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
-            "license": "MIT",
-            "dependencies": {
-                "ansi-regex": "^5.0.1"
-            },
-            "engines": {
-                "node": ">=8"
-            }
-        },
         "node_modules/wikijs": {
             "version": "6.4.1",
             "resolved": "https://registry.npmjs.org/wikijs/-/wikijs-6.4.1.tgz",
@@ -40193,15 +40108,6 @@
                 "node": ">= 14"
             }
         },
-        "node_modules/zlibjs": {
-            "version": "0.3.1",
-            "resolved": "https://registry.npmjs.org/zlibjs/-/zlibjs-0.3.1.tgz",
-            "integrity": "sha512-+J9RrgTKOmlxFSDHo0pI1xM6BLVUv+o0ZT9ANtCxGkjIVCCUdx9alUF8Gm+dGLKbkkkidWIHFDZHDMpfITt4+w==",
-            "license": "MIT",
-            "engines": {
-                "node": "*"
-            }
-        },
         "node_modules/zod": {
             "version": "3.24.1",
             "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.1.tgz",
diff --git a/package.json b/package.json
index 64fc39080..5d96bd03f 100644
--- a/package.json
+++ b/package.json
@@ -86,7 +86,7 @@
         "eslint": "^9.12.0",
         "eslint-plugin-react": "^7.37.1",
         "globals": "^15.10.0",
-        "mocha": "^10.2.0",
+        "mocha": "^11.1.0",
         "prettier": "^3.1.0",
         "scss-loader": "0.0.1",
         "ts-loader": "^9.5.1",
@@ -142,7 +142,7 @@
         "@types/reveal": "^4.2.0",
         "@types/supercluster": "^7.1.3",
         "@types/textfit": "^2.4.4",
-        "@types/web": "^0.0.188",
+        "@types/web": "^0.0.200",
         "@types/webpack-hot-middleware": "^2.25.9",
         "@webscopeio/react-textarea-autocomplete": "^4.9.2",
         "adm-zip": "^0.5.10",
@@ -162,7 +162,7 @@
         "brotli": "^1.3.3",
         "browser-assert": "^1.2.1",
         "bson": "^6.2.0",
-        "canvas": "^2.11.2",
+        "canvas": "^3.1.0",
         "chart.js": "^4.4.0",
         "cheerio": "^1.0.0",
         "child_process": "^1.0.2",
@@ -236,7 +236,7 @@
         "jpeg-autorotate": "^9.0.0",
         "jquery": "^3.7.1",
         "js-datepicker": "^5.18.2",
-        "jsdom": "^25.0.1",
+        "jsdom": "^26.0.0",
         "jsonschema": "^1.4.1",
         "jszip": "^3.10.1",
         "ldrs": "^1.0.2",
@@ -282,7 +282,7 @@
         "prosemirror-transform": "^1.8.0",
         "prosemirror-view": "^1.32.5",
         "pug": "^3.0.2",
-        "puppeteer": "^23.1.0",
+        "puppeteer": "^24.1.1",
         "query-string": "^7.1.3",
         "querystring-es3": "^0.2.1",
         "raw-loader": "^4.0.2",
@@ -334,7 +334,6 @@
         "styled-components": "^6.1.1",
         "supercluster": "^8.0.1",
         "svgson": "^5.3.1",
-        "tesseract.js": "^5.1.0",
         "textarea-caret": "^3.1.0",
         "textfit": "^2.4.0",
         "tough-cookie": "^5.0.0",
@@ -344,7 +343,7 @@
         "typescript-collections": "^1.3.3",
         "typescript-language-server": "^4.1.3",
         "uninstall": "^0.0.0",
-        "unstructured-client": "^0.18.2",
+        "unstructured-client": "^0.19.0",
         "url": "^0.11.3",
         "url-loader": "^4.1.1",
         "util": "^0.12.5",
diff --git a/src/client/apis/gpt/GPT.ts b/src/client/apis/gpt/GPT.ts
index d96972784..269a4284a 100644
--- a/src/client/apis/gpt/GPT.ts
+++ b/src/client/apis/gpt/GPT.ts
@@ -84,18 +84,6 @@ const callTypeMap: { [type: string]: GPTCallOpts } = {
         temp: 0.1, //0.3
         prompt: "BRIEFLY (<50 words) describe any differences between the rubric and the user's answer answer in second person. If there are no differences, say correct",
     },
-    type: {
-        model: 'gpt-4-turbo',
-        maxTokens: 512,
-        temp: 0.5,
-        prompt: '',
-    },
-    info: {
-        model: 'gpt-4-turbo',
-        maxTokens: 512,
-        temp: 0.5,
-        prompt: '',
-    },
     template: {
         model: 'gpt-4-turbo',
         maxTokens: 512,
@@ -133,6 +121,39 @@ const callTypeMap: { [type: string]: GPTCallOpts } = {
         temp: 0.5,
         prompt: 'You will be coloring drawings. You will be given what the drawing is, then a list of descriptions for parts of the drawing. Based on each description, respond with the stroke and fill color that it should be. Follow the rules: 1. Avoid using black for stroke color 2. Make the stroke color 1-3 shades darker than the fill color 3. Use the same colors when possible. Format as {#abcdef #abcdef}, making sure theres a color for each description, and do not include any additional text.',
     },
+    type: {
+        model: 'gpt-4-turbo',
+        maxTokens: 1024,
+        temp: 0,
+        prompt: `I'm going to provide you with a question. 
+                 Based on the question, is the user asking you to 
+                     1. Assigns docs with tags(like star / heart etc)/labels, 
+                     2. Filter docs, 
+                     3. Provide information about a specific doc 
+                     4. Provide a specific doc based on a question/information 
+                     5. Provide general information 
+                     6. Put cards in a specific order. 
+                Answer with only the number for 2-6. For number one, provide the number (1) and the appropriate tag`,
+    },
+    subset: {
+        model: 'gpt-4-turbo',
+        maxTokens: 1024,
+        temp: 0,
+        prompt: "I'm going to give you a list of descriptions. Each one is separated by `======` on either side. Descriptions will vary in length, so make sure to only separate when you see `======`. Based on the question the user asks, provide a subset of the given descriptions that best matches the user's specifications. Make sure each description is only in the list once. Each item should be separated by `======`. Immediately afterward, surrounded by `------` on BOTH SIDES, provide some insight into your reasoning in the 2nd person (and mention nothing about the formatting details given in this description). It is VERY important that you format it exactly as described, ensuring the proper number of `=` and `-` (6 of each) and no commas",
+    },
+
+    info: {
+        model: 'gpt-4-turbo',
+        maxTokens: 1024,
+        temp: 0,
+        prompt: "Answer the user's question with a short (<100 word) response. If a particular document is selected I will provide that information (which may help with your response)",
+    },
+    rubric: {
+        model: 'gpt-4-turbo',
+        maxTokens: 1024,
+        temp: 0,
+        prompt: "BRIEFLY (<25 words) provide a definition for the following term. It will be used as a rubric to evaluate the user's understanding of the topic",
+    },
 };
 let lastCall = '';
 let lastResp = '';
diff --git a/src/client/views/collections/CollectionCardDeckView.tsx b/src/client/views/collections/CollectionCardDeckView.tsx
index aaf33c3a1..366d0f448 100644
--- a/src/client/views/collections/CollectionCardDeckView.tsx
+++ b/src/client/views/collections/CollectionCardDeckView.tsx
@@ -27,7 +27,7 @@ import { DocumentView, DocumentViewProps } from '../nodes/DocumentView';
 import { FocusViewOptions } from '../nodes/FocusViewOptions';
 import { GPTPopup } from '../pdf/GPTPopup/GPTPopup';
 import './CollectionCardDeckView.scss';
-import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
+import { CollectionSubView, docSortings, SubCollectionViewProps } from './CollectionSubView';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { SettingsManager } from '../../util/SettingsManager';
 
@@ -243,7 +243,7 @@ export class CollectionCardView extends CollectionSubView() {
                     const sorted = this.childDocs;
                     const originalIndex = sorted.findIndex(doc => doc === draggedDoc);
 
-                    this.Document.card_sort = '';
+                    this.Document[this._props.fieldKey + '_sort'] = '';
                     originalIndex !== -1 && sorted.splice(originalIndex, 1);
                     sorted.splice(dragIndex, 0, draggedDoc);
                     if (de.complete.docDragData.removeDocument?.(draggedDoc)) {
@@ -450,7 +450,7 @@ export class CollectionCardView extends CollectionSubView() {
         }
 
         if (questionType === '6') {
-            this.Document.card_sort = 'chat';
+            this.Document[this._props.fieldKey + '_sort'] = docSortings.Chat;
         }
 
         listItems.forEach((item, index) => {
@@ -467,16 +467,15 @@ export class CollectionCardView extends CollectionSubView() {
                         let myTag = '';
                         if (tag) {
                             for (let i = 0; i < allHotKeys.length; i++) {
-                                // bcz: CHECK THIS CODE OUT -- SOMETHING CHANGED
                                 const keyTag = StrCast(allHotKeys[i].toolType);
-                                if (tag.includes(keyTag)) {
+                                if (keyTag.includes(tag)) {
                                     myTag = keyTag;
                                     break;
                                 }
                             }
 
                             if (myTag) {
-                                doc[myTag] = true;
+                                TagItem.addTagToDoc(doc, myTag);
                             }
                         }
                         break;
-- 
cgit v1.2.3-70-g09d2


From 6fa61343a3e63a61747bc7123810190057d0bfe4 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 29 Jan 2025 12:14:12 -0500
Subject: cleaned up filter icons.

---
 src/client/util/CurrentUserUtils.ts                | 38 +++++++++++++++++-----
 src/client/views/MainView.tsx                      | 25 +++-----------
 .../views/collections/CollectionCardDeckView.tsx   | 20 +++---------
 src/client/views/pdf/GPTPopup/GPTPopup.tsx         | 34 +++++++------------
 4 files changed, 48 insertions(+), 69 deletions(-)

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

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index e806675aa..306914450 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -40,6 +40,7 @@ import { ColorScheme } from "./SettingsManager";
 import { SnappingManager } from "./SnappingManager";
 import { UndoManager } from "./UndoManager";
 import { DocumentView } from "../views/nodes/DocumentView";
+import { IconProp } from "@fortawesome/fontawesome-svg-core";
 
 export interface Button {
     // DocumentOptions fields a button can set
@@ -67,6 +68,21 @@ export interface Button {
     subMenu?: Button[];
 }
 
+// Not really necessary, but for now, all tags should start with a capital first letter
+export type TagName<T extends string> =
+        // eslint-disable-next-line @typescript-eslint/no-unused-vars
+        T extends `${infer First}${infer Rest}`
+          ? First extends Uppercase<First> 
+            ? First extends Lowercase<First> 
+              ? never  // If it's the same when uppercased and lowercased, it's not a letter.
+              : T      // Otherwise, it's a valid capitalized string.
+            : never
+          : never;
+export function ToTagName(key: string):"Tag"{
+    return ((str => str[0].toUpperCase() + str.slice(1))(key.startsWith('#') ? key.substring(1) : key)) as "Tag";
+}
+
+
 export let resolvedPorts: { server: number, socket: number };
 export class CurrentUserUtils {
 
@@ -703,18 +719,22 @@ pie title Minerals in my tap water
             ]},
          ]
     }
+
+    static filterBtnDesc<T extends string>(tag:TagName<T>|"Tag", icon:IconProp):Button {
+        return { title: tag, isSystem: false, icon: icon.toString(),  toolTip:`Click to toggle visibility of ${tag} tagged Docs`,  btnType: ButtonType.ToggleButton,  expertMode: false, toolType:`#${tag.toLowerCase()}`, funcs: {}, scripts: { onClick: '{ return setTagFilter(this.toolType, _added_, _readOnly_);}'}}
+    }
   
     static filterTools(): Button[] {
-        const defaultTagButtonDescs = [
-            { title: "Star", isSystem: false,icon: "star",  toolTip:"Click to toggle visibility of Star tagged Docs",  btnType: ButtonType.ToggleButton,  expertMode: false, toolType:"#star", funcs: {}, scripts: { onClick: '{ return setTagFilter(this.toolType, _added_, _readOnly_);}'}},
-            { title: "Like", isSystem: false,icon: "heart", toolTip:"Click to toggle visibility of Like tagged Docs",  btnType: ButtonType.ToggleButton,  expertMode: false, toolType:"#like", funcs: {}, scripts: { onClick: '{ return setTagFilter(this.toolType, _added_, _readOnly_);}'}},
-            { title: "Todo", isSystem: false,icon: "bolt",  toolTip:"Click to toggle visibility of Todo tagged Docs",  btnType: ButtonType.ToggleButton,  expertMode: false, toolType:"#todo", funcs: {}, scripts: { onClick: '{ return setTagFilter(this.toolType, _added_, _readOnly_);}'}},
-            { title: "Idea", isSystem: false,icon: "cloud", toolTip:"Click to toggle visibility of Idea tagged Docs",  btnType: ButtonType.ToggleButton,  expertMode: false, toolType:"#idea", funcs: {}, scripts: { onClick: '{ return setTagFilter(this.toolType, _added_, _readOnly_);}'}},
+        // If there's no active dashboard, then a default set of tags are added, otherwise, the user controls which tags are kept
+        const tagButtonDescs = Doc.UserDoc().activeDashboard ? [] : [
+            this.filterBtnDesc("Star", "star"),
+            this.filterBtnDesc("Like", "heart"),
+            this.filterBtnDesc("Todo", "bolt"),
+            this.filterBtnDesc("Idea", "cloud")
         ];
-        // hack: if there's no dashboard, create default filters.  otherwise, just make sure that the Options button is preserved
         return [
-            { title:"Options",isSystem: true,icon: "gear",  toolTip:"Click to customize list of filter buttons",       btnType: ButtonType.ClickButton,  expertMode: false, toolType:"-opts-",funcs: {}, scripts: { onClick: '{ return setTagFilter(this.toolType, false,_readOnly_);}'}},
-            ...(Doc.UserDoc().activeDashboard ? [] : defaultTagButtonDescs)
+            { title:"Options",isSystem: true,icon: "gear",  toolTip:"Click to customize list of filter buttons", btnType: ButtonType.ClickButton, expertMode: false, toolType:"-opts-",funcs: {}, scripts: { onClick: '{ return setTagFilter(this.toolType, false,_readOnly_);}'}},
+            ...tagButtonDescs
         ]
     }
     static viewTools(): Button[] {
@@ -866,7 +886,7 @@ pie title Minerals in my tap water
             childDontRegisterViews: true, flexGap: 0, _height: 30, _width: 30, ignoreClick: !params.scripts?.onClick,
             linearView_SubMenu: true, linearView_Expandable: true, embedContainer: menuDoc};
         
-        const items = (menutBtn?:Doc) => !menutBtn ? [] : subMenu.map(sub => this.setupContextMenuBtn(sub, menutBtn) );
+        const items = (menuBtn?:Doc) => !menuBtn ? [] : subMenu.map(sub => this.setupContextMenuBtn(sub, menuBtn) );
         const creator = params.btnType === ButtonType.MultiToggleButton ? this.multiToggleList : this.linearButtonList;
         const btnDoc = DocUtils.AssignScripts( DocUtils.AssignDocField(menuDoc, StrCast(params.title), 
                 (opts) => creator(opts, items(menuBtnDoc)), reqdSubMenuOpts, items(menuBtnDoc)), params.scripts, params.funcs);
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 7abca5197..d748b70ae 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -20,7 +20,7 @@ import { CollectionViewType, DocumentType } from '../documents/DocumentTypes';
 import { Docs } from '../documents/Documents';
 import { CalendarManager } from '../util/CalendarManager';
 import { CaptureManager } from '../util/CaptureManager';
-import { Button, CurrentUserUtils } from '../util/CurrentUserUtils';
+import { CurrentUserUtils, ToTagName } from '../util/CurrentUserUtils';
 import { DocumentManager } from '../util/DocumentManager';
 import { DragManager } from '../util/DragManager';
 import { dropActionType } from '../util/DropActionTypes';
@@ -63,7 +63,6 @@ import { DocCreatorMenu } from './nodes/DataVizBox/DocCreatorMenu';
 import { SchemaCSVPopUp } from './nodes/DataVizBox/SchemaCSVPopUp';
 import { DocButtonState } from './nodes/DocumentLinksButton';
 import { DocumentView, DocumentViewInternal } from './nodes/DocumentView';
-import { ButtonType } from './nodes/FontIconBox/FontIconBox';
 import { ImageEditorData as ImageEditor } from './nodes/ImageBox';
 import { LinkDescriptionPopup } from './nodes/LinkDescriptionPopup';
 import { LinkDocPreview, LinkInfo } from './nodes/LinkDocPreview';
@@ -874,25 +873,9 @@ export class MainView extends ObservableReactComponent<object> {
      * @param hotKey tite of the new hotkey
      */
     addHotKey = (hotKey: string) => {
-        const buttons = DocCast(Doc.UserDoc().myContextMenuBtns);
-        const filter = DocCast(buttons.Filter);
-        const title = hotKey.startsWith('#') ? hotKey.substring(1) : hotKey;
-
-        const newKey: Button = {
-            title,
-            icon: 'question',
-            toolTip: `Click to toggle the ${title}'s group's visibility`,
-            btnType: ButtonType.ToggleButton,
-            expertMode: false,
-            toolType: '#' + title,
-            funcs: {},
-            scripts: { onClick: '{ return handleTags(this.toolType, _readOnly_);}' },
-        };
-
-        const newBtn = CurrentUserUtils.setupContextMenuBtn(newKey, filter);
-        newBtn.isSystem = newBtn[DocData].isSystem = undefined;
-
-        Doc.AddToFilterHotKeys(newBtn);
+        const filterIcons = DocCast(DocCast(Doc.UserDoc().myContextMenuBtns)?.Filter);
+        const menuDoc = CurrentUserUtils.setupContextMenuBtn(CurrentUserUtils.filterBtnDesc(ToTagName(hotKey), 'question'), filterIcons);
+        Doc.AddToFilterHotKeys(menuDoc);
     };
 
     @computed get mainInnerContent() {
diff --git a/src/client/views/collections/CollectionCardDeckView.tsx b/src/client/views/collections/CollectionCardDeckView.tsx
index 366d0f448..b40aa37de 100644
--- a/src/client/views/collections/CollectionCardDeckView.tsx
+++ b/src/client/views/collections/CollectionCardDeckView.tsx
@@ -462,24 +462,13 @@ export class CollectionCardView extends CollectionSubView() {
                     case '6':
                         doc.chatIndex = index;
                         break;
-                    case '1': {
-                        const allHotKeys = Doc.MyFilterHotKeys;
-                        let myTag = '';
+                    case '1':
                         if (tag) {
-                            for (let i = 0; i < allHotKeys.length; i++) {
-                                const keyTag = StrCast(allHotKeys[i].toolType);
-                                if (keyTag.includes(tag)) {
-                                    myTag = keyTag;
-                                    break;
-                                }
-                            }
-
-                            if (myTag) {
-                                TagItem.addTagToDoc(doc, myTag);
-                            }
+                            const hashTag = tag.startsWith('#') ? tag : '#' + tag[0].toLowerCase() + tag.slice(1);
+                            const filterTag = Doc.MyFilterHotKeys.map(key => StrCast(key.toolType)).find(key => key.includes(tag)) ?? hashTag;
+                            TagItem.addTagToDoc(doc, filterTag);
                         }
                         break;
-                    }
                     case '2':
                     case '4':
                         doc.chatFilter = true;
@@ -584,7 +573,6 @@ export class CollectionCardView extends CollectionSubView() {
      * Actually renders all the cards
      */
     @computed get renderCards() {
-        console.log(this.docDraggedIndex, this.childDocs[0].title, this.childDocs[1].title);
         // Map sorted documents to their rendered components
         return this.childDocs.map((doc, index) => {
             const cardsInRow = this.cardsInRowThatIncludesCardIndex(index);
diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.tsx b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
index 4028ef479..c33b81eb4 100644
--- a/src/client/views/pdf/GPTPopup/GPTPopup.tsx
+++ b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
@@ -188,8 +188,8 @@ export class GPTPopup extends ObservableReactComponent<object> {
 
         const selected = DocumentView.SelectedDocs().lastElement();
 
-        const questionText = 'Question: ' + StrCast(selected['gptInputText']);
-        const rubricText = 'Rubric: ' + StrCast(selected['gptRubric']);
+        const questionText = 'Question: ' + StrCast(selected.gptInputText);
+        const rubricText = 'Rubric: ' + StrCast(selected.gptRubric);
         const queryText = questionText + ' UserAnswer: ' + this.quizAnswer + '. ' + 'Rubric' + rubricText;
 
         try {
@@ -262,27 +262,15 @@ export class GPTPopup extends ObservableReactComponent<object> {
 
         try {
             const questionType = await gptAPICall(this.chatSortPrompt, GPTCallType.TYPE);
-            const questionNumber = questionType.split(' ')[0];
-            let res = '';
-
-            switch (questionNumber) {
-                case '1':
-                case '2':
-                case '4':
-                    res = await gptAPICall(this.sortDesc, GPTCallType.SUBSET, this.chatSortPrompt);
-                    break;
-                case '6':
-                    res = await gptAPICall(this.sortDesc, GPTCallType.SORT, this.chatSortPrompt);
-                    break;
-                default:
-                    {
-                        const selected = DocumentView.SelectedDocs().lastElement();
-                        const questionText = StrCast(selected?.gptInputText);
-
-                        res = await gptAPICall(questionText, GPTCallType.INFO, this.chatSortPrompt);
-                    }
-                    break;
-            }
+            const questionNumber = questionType.split(' ')[0][0];
+            const res = await (() => {
+                switch (questionNumber) {
+                    case '1':
+                    case '2':
+                    case '4': return gptAPICall(this.sortDesc, GPTCallType.SUBSET, this.chatSortPrompt);
+                    case '6': return gptAPICall(this.sortDesc, GPTCallType.SORT, this.chatSortPrompt);
+                    default: return gptAPICall(StrCast(DocumentView.SelectedDocs().lastElement()?.gptInputText), GPTCallType.INFO, this.chatSortPrompt);
+                }})(); // prettier-ignore
 
             // Trigger the callback with the result
             if (this.onSortComplete) {
-- 
cgit v1.2.3-70-g09d2


From 45aa1b54ab37d821257fb00b3be5ef5eca03ef2c Mon Sep 17 00:00:00 2001
From: bobzel <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/CollectionCardDeckView.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


From 1e673454f8cabf894e8dfec36734d2cb41caa7b1 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 3 Feb 2025 10:23:56 -0500
Subject: changed filter 'check' to be exact match and 'match' to be substring.
  fixed GPTpopup filtering to use a #chat tag instead of a chatFilter field.

---
 src/client/apis/gpt/GPT.ts                         |  2 +-
 src/client/documents/DocUtils.ts                   |  4 +-
 src/client/util/CurrentUserUtils.ts                |  3 +-
 src/client/views/TagsView.scss                     |  4 +-
 src/client/views/TagsView.tsx                      |  2 +-
 .../views/collections/CollectionCardDeckView.tsx   | 11 +++--
 src/client/views/global/globalScripts.ts           |  4 +-
 src/client/views/nodes/ComparisonBox.tsx           |  2 +-
 src/client/views/nodes/IconTagBox.scss             |  2 -
 src/client/views/pdf/GPTPopup/GPTPopup.tsx         | 57 ++++++++++------------
 10 files changed, 44 insertions(+), 47 deletions(-)

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

diff --git a/src/client/apis/gpt/GPT.ts b/src/client/apis/gpt/GPT.ts
index 269a4284a..1894bb4df 100644
--- a/src/client/apis/gpt/GPT.ts
+++ b/src/client/apis/gpt/GPT.ts
@@ -42,7 +42,7 @@ const callTypeMap: { [type: string]: GPTCallOpts } = {
         model: 'gpt-4o',
         maxTokens: 2048,
         temp: 0.7,
-        prompt: 'Create a stack of flashcards out of this text with each question and answer labeled as question and answer. Create a title that represents the question in just a few words and label it "title". For some questions, ask "what is this image of" but tailored to stacks theme and the image and write a keyword that represents the image and label it "keyword". Otherwise, write none. Do not label each flashcard and do not include asterisks.',
+        prompt: 'Create a stack of at least 10 flashcards out of this text with each question and answer labeled as question and answer. Each flashcard should have a title that represents the question in just a few words and label it "title". For some questions, ask "what is this image of" but tailored to stacks theme and the image and write a keyword that represents the image and label it "keyword". Otherwise, write none. Do not label each flashcard and do not include asterisks.',
     },
 
     completion: { model: 'gpt-4-turbo', maxTokens: 256, temp: 0.5, prompt: "You are a helpful assistant. Answer the user's prompt." },
diff --git a/src/client/documents/DocUtils.ts b/src/client/documents/DocUtils.ts
index d11a3e235..23032b62e 100644
--- a/src/client/documents/DocUtils.ts
+++ b/src/client/documents/DocUtils.ts
@@ -72,9 +72,9 @@ export namespace DocUtils {
         }
         const vals = StrListCast(fieldVal); // list typing is very imperfect.  casting to a string list doesn't mean that the entries will actually be strings
         if (vals.length) {
-            return vals.some(v => typeof v === 'string' && v.includes(value as string)); // bcz: arghh: Todo: comparison should be parameterized as exact, or substring
+            return vals.some(v => typeof v === 'string' && v === (value as string)); // bcz: arghh: Todo: comparison should be parameterized as exact, or substring
         }
-        return Field.toString(fieldVal as FieldType).includes(value as string); // bcz: arghh: Todo: comparison should be parameterized as exact, or substring
+        return Field.toString(fieldVal as FieldType) === (value as string); // bcz: arghh: Todo: comparison should be parameterized as exact, or substring
     }
     /**
      * @param docs
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index b75961575..0fd09b479 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -728,7 +728,8 @@ pie title Minerals in my tap water
             this.filterBtnDesc("Star", "star"),
             this.filterBtnDesc("Like", "heart"),
             this.filterBtnDesc("Todo", "bolt"),
-            this.filterBtnDesc("Idea", "cloud")
+            this.filterBtnDesc("Idea", "cloud"),
+            this.filterBtnDesc("Chat", "robot")
         ];
         return [
             { title:"Options",isSystem: true,icon: "gear",  toolTip:"Click to customize list of filter buttons", btnType: ButtonType.ClickButton, expertMode: false, toolType:"-opts-",funcs: {}, scripts: { onClick: '{ return setTagFilter(this.toolType, false,_readOnly_);}'}},
diff --git a/src/client/views/TagsView.scss b/src/client/views/TagsView.scss
index 303a08e1e..b21d303fb 100644
--- a/src/client/views/TagsView.scss
+++ b/src/client/views/TagsView.scss
@@ -12,7 +12,7 @@
         .tagsView-list {
             display: flex;
             flex-wrap: wrap;
-            height: inherit;
+            height: 1;
             .iconButton-container {
                 min-height: unset !important;
             }
@@ -58,7 +58,7 @@
 }
 
 .tagsView-editing-box {
-    margin-top: 8px;
+    margin-top: 20px;
 }
 
 .tagsView-input-box {
diff --git a/src/client/views/TagsView.tsx b/src/client/views/TagsView.tsx
index 9e936ea54..b70e21918 100644
--- a/src/client/views/TagsView.tsx
+++ b/src/client/views/TagsView.tsx
@@ -146,7 +146,7 @@ export class TagItem extends ObservableReactComponent<TagItemProps> {
                 }
             }
         }
-        doc[DocData].tags = new List<string>((doc[DocData].tags as List<string>).filter(label => label !== tag));
+        doc[DocData].tags = new List<string>(StrListCast(doc[DocData].tags).filter(label => label !== tag));
     };
 
     private _ref: React.RefObject<HTMLDivElement>;
diff --git a/src/client/views/collections/CollectionCardDeckView.tsx b/src/client/views/collections/CollectionCardDeckView.tsx
index f24673c39..43464e50c 100644
--- a/src/client/views/collections/CollectionCardDeckView.tsx
+++ b/src/client/views/collections/CollectionCardDeckView.tsx
@@ -85,6 +85,10 @@ export class CollectionCardView extends CollectionSubView() {
         });
 
     componentDidMount() {
+        this._disposers.chatVis = reaction(
+            () => GPTPopup.Instance.Visible,
+            vis => !vis && this.onGptHide()
+        );
         GPTPopup.Instance.setRegenerateCallback(this.Document, this.childPairStringListAndUpdateSortDesc);
         this._props.setContentViewBox?.(this);
         // if card deck moves, then the child doc views are hidden so their screen to local transforms will return empty rectangles
@@ -106,6 +110,7 @@ export class CollectionCardView extends CollectionSubView() {
         );
     }
 
+    onGptHide = () => Doc.setDocFilter(this.Document, 'tags', '#chat', 'remove');
     componentWillUnmount() {
         GPTPopup.Instance.setSortDesc('');
         GPTPopup.Instance.onSortComplete = undefined;
@@ -445,7 +450,7 @@ export class CollectionCardView extends CollectionSubView() {
 
             if (questionType === '2' || questionType === '4') {
                 this.childDocs.forEach(d => {
-                    d.chatFilter = false;
+                    TagItem.removeTagFromDoc(d, '#chat');
                 });
             }
 
@@ -471,8 +476,8 @@ export class CollectionCardView extends CollectionSubView() {
                             break;
                         case '2':
                         case '4':
-                            doc.chatFilter = true;
-                            Doc.setDocFilter(DocCast(this.Document), 'chatFilter', true, 'check');
+                            TagItem.addTagToDoc(doc, '#chat');
+                            Doc.setDocFilter(this.Document, 'tags', '#chat', 'check');
                             break;
                     }
                 } else {
diff --git a/src/client/views/global/globalScripts.ts b/src/client/views/global/globalScripts.ts
index b02eabab0..b44292164 100644
--- a/src/client/views/global/globalScripts.ts
+++ b/src/client/views/global/globalScripts.ts
@@ -223,9 +223,9 @@ ScriptingGlobals.add(function showFreeform(
             setDoc: (doc: Doc, dv: DocumentView) => { doc[Doc.LayoutFieldKey(doc)+"_sort_desc"] = true; },
         }],
         ['toggle-chat', {
-            checkResult: (doc: Doc) => GPTPopup.Instance.visible,
+            checkResult: (doc: Doc) => GPTPopup.Instance.Visible,
             setDoc: (doc: Doc, dv: DocumentView) => {
-                if (GPTPopup.Instance.visible){
+                if (GPTPopup.Instance.Visible){
                     doc[Doc.LayoutFieldKey(doc)+"_sort"] = '';
                     GPTPopup.Instance.setVisible(false);
    
diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx
index f5291a4c1..53fbd11c5 100644
--- a/src/client/views/nodes/ComparisonBox.tsx
+++ b/src/client/views/nodes/ComparisonBox.tsx
@@ -510,7 +510,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
      * Calls GPT for each flashcard type.
      */
     askGPT = async (callType: GPTCallType) => {
-        const questionText = 'Question: ' + this.frontText;
+        const questionText = this.frontText;
         const queryText = questionText + (callType == GPTCallType.QUIZ ? ' UserAnswer: ' + this._inputValue + '. ' + ' Rubric: ' + this.backText : '');
 
         this.loading = true;
diff --git a/src/client/views/nodes/IconTagBox.scss b/src/client/views/nodes/IconTagBox.scss
index 90cc06092..c79d662f4 100644
--- a/src/client/views/nodes/IconTagBox.scss
+++ b/src/client/views/nodes/IconTagBox.scss
@@ -10,8 +10,6 @@
     gap: 5px;
     padding-left: 5px;
     padding-right: 5px;
-    padding-top: 2px;
-    padding-bottom: 2px;
 
     button {
         pointer-events: auto;
diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.tsx b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
index da0cbea7a..f5a9f9e6a 100644
--- a/src/client/views/pdf/GPTPopup/GPTPopup.tsx
+++ b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
@@ -46,9 +46,9 @@ export class GPTPopup extends ObservableReactComponent<object> {
     @observable private chatMode: boolean = false;
     private correlatedColumns: string[] = [];
 
-    @observable public visible: boolean = false;
+    @observable public Visible: boolean = false;
     @action public setVisible = (vis: boolean) => {
-        this.visible = vis;
+        this.Visible = vis;
     };
     @observable public loading: boolean = false;
     @action public setLoading = (loading: boolean) => {
@@ -114,8 +114,8 @@ export class GPTPopup extends ObservableReactComponent<object> {
         this.sortDesc = t;
     };
 
-    @observable onSortComplete?: (sortResult: string, questionType: string, tag?: string) => void;
-    @observable onQuizRandom?: () => void;
+    onSortComplete?: (sortResult: string, questionType: string, tag?: string) => void;
+    onQuizRandom?: () => void;
     @observable cardsDoneLoading = false;
 
     @observable collectionDoc: Doc | undefined = undefined;
@@ -154,30 +154,27 @@ export class GPTPopup extends ObservableReactComponent<object> {
     generateQuiz = async () => {
         this.setLoading(true);
 
-        const selected = DocumentView.SelectedDocs().lastElement();
+        await this.regenerateCallback?.();
 
-        const questionText = 'Question: ' + StrCast(selected.gptInputText);
-        const rubricText = 'Rubric: ' + StrCast(selected.gptRubric);
-        const queryText = questionText + ' UserAnswer: ' + this.quizAnswer + '. ' + rubricText;
+        const selected = DocumentView.SelectedDocs().lastElement();
+        if (!StrCast(selected.gptRubric)) {
+            await this.generateRubric(StrCast(selected.gptInputText), selected);
+        }
 
         try {
-            const res = await gptAPICall(queryText, GPTCallType.QUIZ);
-            if (!res) {
-                console.error('GPT call failed');
-                return;
-            }
-            console.log(res);
-            this.setQuizResp(res);
-            this.conversationArray.push(res);
+            const res = await gptAPICall('Question: ' + StrCast(selected.gptInputText) + ' UserAnswer: ' + this.quizAnswer + '.  Rubric: ' + StrCast(selected.gptRubric), GPTCallType.QUIZ);
+            if (res) {
+                this.setQuizResp(res);
+                this.conversationArray.push(res);
 
-            this.setLoading(false);
+                this.setLoading(false);
+                this.onQuizRandom?.();
+            } else {
+                console.error('GPT provided no response');
+            }
         } catch (err) {
             console.error('GPT call failed', err);
         }
-
-        if (this.onQuizRandom) {
-            this.onQuizRandom();
-        }
     };
 
     /**
@@ -223,9 +220,7 @@ export class GPTPopup extends ObservableReactComponent<object> {
     generateCard = async () => {
         this.setLoading(true);
 
-        if (this.regenerateCallback) {
-            await this.regenerateCallback();
-        }
+        await this.regenerateCallback?.();
 
         try {
             const questionType = await gptAPICall(this.chatSortPrompt, GPTCallType.TYPE);
@@ -442,9 +437,7 @@ export class GPTPopup extends ObservableReactComponent<object> {
                 onClick={() => {
                     this.conversationArray = ['Define the selected card!'];
                     this.setMode(GPTPopupMode.QUIZ);
-                    if (this.onQuizRandom) {
-                        this.onQuizRandom();
-                    }
+                    this.onQuizRandom?.();
                 }}
                 color={StrCast(Doc.UserDoc().userVariantColor)}
                 type={Type.TERT}
@@ -694,13 +687,13 @@ export class GPTPopup extends ObservableReactComponent<object> {
                         <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"
+                        tooltip="Clear Chat filter"
                         toggleType={ToggleType.BUTTON}
                         type={Type.PRIM}
-                        toggleStatus={this.collectionDoc?.childFilters ? true : false}
-                        text={this.collectionDoc?.childFilters ? 'filtered' : ''}
+                        toggleStatus={Doc.hasDocFilter(this.collectionDoc, 'tags', '#chat')}
+                        text={Doc.hasDocFilter(this.collectionDoc, 'tags', '#chat') ? 'filtered' : ''}
                         color="red"
-                        onClick={() => this.collectionDoc && (this.collectionDoc.childFilters = undefined)}
+                        onClick={() => this.collectionDoc && Doc.setDocFilter(this.collectionDoc, 'tags', '#chat', 'remove')}
                     />
                     <IconButton
                         color={StrCast(SettingsManager.userVariantColor)}
@@ -738,7 +731,7 @@ export class GPTPopup extends ObservableReactComponent<object> {
         }
 
         return (
-            <div className="summary-box" style={{ display: this.visible ? 'flex' : 'none' }}>
+            <div className="summary-box" style={{ display: this.Visible ? 'flex' : 'none' }}>
                 {content}
                 <div className="resize-handle" />
             </div>
-- 
cgit v1.2.3-70-g09d2


From 3f6a168b2916ccac707cf5ea1e4ef898a470d7d1 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 13 Feb 2025 23:27:42 -0500
Subject: lots of cleanup in GPTpopup.

---
 src/client/apis/gpt/GPT.ts                         |  20 +-
 .../views/collections/CollectionCardDeckView.tsx   |  20 +-
 src/client/views/global/globalScripts.ts           |   5 -
 src/client/views/nodes/DataVizBox/DataVizBox.tsx   |   4 +-
 src/client/views/nodes/WebBox.tsx                  |   4 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |   6 +-
 src/client/views/pdf/AnchorMenu.tsx                |  17 +-
 src/client/views/pdf/GPTPopup/GPTPopup.scss        |  45 +-
 src/client/views/pdf/GPTPopup/GPTPopup.tsx         | 629 ++++++++-------------
 src/client/views/pdf/PDFViewer.tsx                 |   2 +-
 10 files changed, 285 insertions(+), 467 deletions(-)

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

diff --git a/src/client/apis/gpt/GPT.ts b/src/client/apis/gpt/GPT.ts
index dc4607b94..066510a6a 100644
--- a/src/client/apis/gpt/GPT.ts
+++ b/src/client/apis/gpt/GPT.ts
@@ -1,6 +1,14 @@
 import { ChatCompletionMessageParam, Image } from 'openai/resources';
 import { openai } from './setup';
 
+export enum GPTTypeStyle {
+    AssignTags = 1,
+    Filter = 2,
+    DocInfo = 3,
+    ChooseDoc = 4,
+    GeneralInfo = 5,
+    SortDocs = 6,
+}
 enum GPTCallType {
     SUMMARY = 'summary',
     COMPLETION = 'completion',
@@ -127,12 +135,12 @@ const callTypeMap: { [type: string]: GPTCallOpts } = {
         temp: 0,
         prompt: `I'm going to provide you with a question. 
                  Based on the question, is the user asking you to 
-                     1. Assigns docs with tags(like star / heart etc)/labels, 
-                     2. Filter docs, 
-                     3. Provide information about a specific doc 
-                     4. Provide a specific doc based on a question/information 
-                     5. Provide general information 
-                     6. Put cards in a specific order. 
+                     ${GPTTypeStyle.AssignTags}. Assigns docs with tags(like star / heart etc)/labels, 
+                     ${GPTTypeStyle.ChooseDoc}. Filter docs, 
+                     ${GPTTypeStyle.DocInfo}. Provide information about a specific doc 
+                     ${GPTTypeStyle.Filter}. Provide a specific doc based on a question/information 
+                     ${GPTTypeStyle.GeneralInfo}. Provide general information 
+                     ${GPTTypeStyle.SortDocs}. Put cards in a specific order. 
                 Answer with only the number for 2-6. For number one, provide the number (1) and the appropriate tag`,
     },
     subset: {
diff --git a/src/client/views/collections/CollectionCardDeckView.tsx b/src/client/views/collections/CollectionCardDeckView.tsx
index 43464e50c..a3ec3884b 100644
--- a/src/client/views/collections/CollectionCardDeckView.tsx
+++ b/src/client/views/collections/CollectionCardDeckView.tsx
@@ -12,7 +12,7 @@ import { List } from '../../../fields/List';
 import { ScriptField } from '../../../fields/ScriptField';
 import { BoolCast, DocCast, NumCast, RTFCast, ScriptCast, StrCast } from '../../../fields/Types';
 import { URLField } from '../../../fields/URLField';
-import { gptImageLabel } from '../../apis/gpt/GPT';
+import { gptImageLabel, GPTTypeStyle } from '../../apis/gpt/GPT';
 import { DocumentType } from '../../documents/DocumentTypes';
 import { Docs } from '../../documents/Documents';
 import { DragManager } from '../../util/DragManager';
@@ -80,7 +80,7 @@ export class CollectionCardView extends CollectionSubView() {
     childPairStringListAndUpdateSortDesc = () =>
         this.childPairStringList().then(sortDesc => {
             GPTPopup.Instance.setSortDesc(sortDesc.join());
-            GPTPopup.Instance.onSortComplete = this.processGptOutput;
+            GPTPopup.Instance.onGptResponse = this.processGptResponse;
             GPTPopup.Instance.onQuizRandom = this.quizMode;
         });
 
@@ -113,7 +113,7 @@ export class CollectionCardView extends CollectionSubView() {
     onGptHide = () => Doc.setDocFilter(this.Document, 'tags', '#chat', 'remove');
     componentWillUnmount() {
         GPTPopup.Instance.setSortDesc('');
-        GPTPopup.Instance.onSortComplete = undefined;
+        GPTPopup.Instance.onGptResponse = undefined;
         GPTPopup.Instance.onQuizRandom = undefined;
         GPTPopup.Instance.setRegenerateCallback(undefined, null);
         Object.keys(this._disposers).forEach(key => this._disposers[key]?.());
@@ -443,18 +443,18 @@ export class CollectionCardView extends CollectionSubView() {
      * @param questionType
      * @param tag
      */
-    processGptOutput = (gptOutput: string, questionType: string, tag?: string) =>
+    processGptResponse = (gptOutput: string, questionType: GPTTypeStyle, tag?: string) =>
         undoable(() => {
             // Split the string into individual list items
             const listItems = gptOutput.split('======').filter(item => item.trim() !== '');
 
-            if (questionType === '2' || questionType === '4') {
+            if (questionType === GPTTypeStyle.Filter || questionType === GPTTypeStyle.ChooseDoc) {
                 this.childDocs.forEach(d => {
                     TagItem.removeTagFromDoc(d, '#chat');
                 });
             }
 
-            if (questionType === '6') {
+            if (questionType === GPTTypeStyle.SortDocs) {
                 this.Document[this._props.fieldKey + '_sort'] = docSortings.Chat;
             }
 
@@ -464,18 +464,18 @@ export class CollectionCardView extends CollectionSubView() {
                 const doc = this._textToDoc.get(normalizedItem);
                 if (doc) {
                     switch (questionType) {
-                        case '6':
+                        case GPTTypeStyle.SortDocs:
                             doc.chatIndex = index;
                             break;
-                        case '1':
+                        case GPTTypeStyle.AssignTags:
                             if (tag) {
                                 const hashTag = tag.startsWith('#') ? tag : '#' + tag[0].toLowerCase() + tag.slice(1);
                                 const filterTag = Doc.MyFilterHotKeys.map(key => StrCast(key.toolType)).find(key => key.includes(tag)) ?? hashTag;
                                 TagItem.addTagToDoc(doc, filterTag);
                             }
                             break;
-                        case '2':
-                        case '4':
+                        case GPTTypeStyle.Filter:
+                        case GPTTypeStyle.ChooseDoc:
                             TagItem.addTagToDoc(doc, '#chat');
                             Doc.setDocFilter(this.Document, 'tags', '#chat', 'check');
                             break;
diff --git a/src/client/views/global/globalScripts.ts b/src/client/views/global/globalScripts.ts
index b44292164..029c4dbc7 100644
--- a/src/client/views/global/globalScripts.ts
+++ b/src/client/views/global/globalScripts.ts
@@ -228,15 +228,10 @@ ScriptingGlobals.add(function showFreeform(
                 if (GPTPopup.Instance.Visible){
                     doc[Doc.LayoutFieldKey(doc)+"_sort"] = '';
                     GPTPopup.Instance.setVisible(false);
-   
                 } else {
                     GPTPopup.Instance.setVisible(true);
                     GPTPopup.Instance.setMode(GPTPopupMode.CARD);
-                    GPTPopup.Instance.setCardsDoneLoading(true);
-
                 }
-                
-
             },
         }],
         ['toggle-tags', {
diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx
index b874d077b..fa3ab73a7 100644
--- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx
+++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx
@@ -489,7 +489,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
         }
 
         // Changing which document to add the annotation to (the currently selected PDF)
-        GPTPopup.Instance.setSidebarId('data_sidebar');
+        GPTPopup.Instance.setSidebarFieldKey('data_sidebar');
         GPTPopup.Instance.addDoc = this.sidebarAddDocument;
     };
 
@@ -523,7 +523,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
     };
 
     askGPT = action(async () => {
-        GPTPopup.Instance.setSidebarId('data_sidebar');
+        GPTPopup.Instance.setSidebarFieldKey('data_sidebar');
         GPTPopup.Instance.addDoc = this.sidebarAddDocument;
         GPTPopup.Instance.createFilteredDoc = this.createFilteredDoc;
         GPTPopup.Instance.setDataJson('');
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 6026d9ca7..e7a10cc29 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -383,7 +383,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
                 this._textAnnotationCreator = () => this.createTextAnnotation(sel, !sel.isCollapsed ? sel.getRangeAt(0) : undefined);
                 AnchorMenu.Instance.jumpTo(e.clientX * scale + mainContBounds.translateX, e.clientY * scale + mainContBounds.translateY - NumCast(this.layoutDoc._layout_scrollTop) * scale);
                 // Changing which document to add the annotation to (the currently selected WebBox)
-                GPTPopup.Instance.setSidebarId(`${this._props.fieldKey}_${this._urlHash ? this._urlHash + '_' : ''}sidebar`);
+                GPTPopup.Instance.setSidebarFieldKey(`${this._props.fieldKey}_${this._urlHash ? this._urlHash + '_' : ''}sidebar`);
                 GPTPopup.Instance.addDoc = this.sidebarAddDocument;
             }
         } else {
@@ -446,7 +446,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
             this._textAnnotationCreator = () => this.createTextAnnotation(sel, selRange);
             (!sel.isCollapsed || this.marqueeing) && AnchorMenu.Instance.jumpTo(e.clientX, e.clientY);
             // Changing which document to add the annotation to (the currently selected WebBox)
-            GPTPopup.Instance.setSidebarId(`${this._props.fieldKey}_${this._urlHash ? this._urlHash + '_' : ''}sidebar`);
+            GPTPopup.Instance.setSidebarFieldKey(`${this._props.fieldKey}_${this._urlHash ? this._urlHash + '_' : ''}sidebar`);
             GPTPopup.Instance.addDoc = this.sidebarAddDocument;
         }
     };
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index eb1f9d07b..17b9bce47 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -135,7 +135,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
 
     /**
      * ApplyingChange - Marks whether an interactive text edit is currently in the process of being written to the database.
-     * This is needed to distinguish changes to text fields caused by editing vs those caused by changes to 
+     * This is needed to distinguish changes to text fields caused by editing vs those caused by changes to
      * the prototype or other external edits
      */
     public ApplyingChange: string = '';
@@ -1043,7 +1043,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
 
     askGPT = action(async () => {
         try {
-            GPTPopup.Instance.setSidebarId(this.sidebarKey);
+            GPTPopup.Instance.setSidebarFieldKey(this.sidebarKey);
             GPTPopup.Instance.addDoc = this.sidebarAddDocument;
             const res = await gptAPICall((this.dataDoc.text as RichTextField)?.Text, GPTCallType.COMPLETION);
             if (!res) {
@@ -1660,7 +1660,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
         }
     };
     onSelectEnd = () => {
-        GPTPopup.Instance.setSidebarId(this.sidebarKey);
+        GPTPopup.Instance.setSidebarFieldKey(this.sidebarKey);
         GPTPopup.Instance.addDoc = this.sidebarAddDocument;
         document.removeEventListener('pointerup', this.onSelectEnd);
     };
diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx
index 18da01890..f7070c780 100644
--- a/src/client/views/pdf/AnchorMenu.tsx
+++ b/src/client/views/pdf/AnchorMenu.tsx
@@ -1,5 +1,5 @@
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { ColorPicker, Group, IconButton, Popup, Size, Toggle, ToggleType, Type } from '@dash/components';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { IReactionDisposer, ObservableMap, action, computed, makeObservable, observable, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
@@ -9,16 +9,15 @@ import { ClientUtils, returnFalse, setupMoveUpEvents } from '../../../ClientUtil
 import { emptyFunction, unimplementedFunction } from '../../../Utils';
 import { Doc, Opt } from '../../../fields/Doc';
 import { DocData } from '../../../fields/DocSymbols';
-import { GPTCallType, gptAPICall } from '../../apis/gpt/GPT';
 import { SettingsManager } from '../../util/SettingsManager';
 import { undoBatch } from '../../util/UndoManager';
 import { AntimodeMenu, AntimodeMenuProps } from '../AntimodeMenu';
 import { LinkPopup } from '../linking/LinkPopup';
+import { ComparisonBox } from '../nodes/ComparisonBox';
 import { DocumentView } from '../nodes/DocumentView';
 import { DrawingOptions, SmartDrawHandler } from '../smartdraw/SmartDrawHandler';
 import './AnchorMenu.scss';
-import { GPTPopup, GPTPopupMode } from './GPTPopup/GPTPopup';
-import { ComparisonBox } from '../nodes/ComparisonBox';
+import { GPTPopup } from './GPTPopup/GPTPopup';
 
 @observer
 export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
@@ -98,15 +97,7 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
      * Invokes the API with the selected text and stores it in the summarized text.
      * @param e pointer down event
      */
-    gptSummarize = () => {
-        GPTPopup.Instance.setVisible(true);
-        GPTPopup.Instance.setMode(GPTPopupMode.SUMMARY);
-        GPTPopup.Instance.setLoading(true);
-        gptAPICall(this._selectedText, GPTCallType.SUMMARY)
-            .then(res => GPTPopup.Instance.setText(res || 'Something went wrong.'))
-            .catch(err => console.error(err))
-            .finally(() => GPTPopup.Instance.setLoading(false));
-    };
+    gptSummarize = () => GPTPopup.Instance.generateSummary(this._selectedText);
 
     /*
      * Transfers the flashcard text generated by GPT on flashcards and creates a collection out them.
diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.scss b/src/client/views/pdf/GPTPopup/GPTPopup.scss
index 0247dc10c..9cf318dc0 100644
--- a/src/client/views/pdf/GPTPopup/GPTPopup.scss
+++ b/src/client/views/pdf/GPTPopup/GPTPopup.scss
@@ -5,7 +5,7 @@ $lightgrey: #ececec;
 $button: #5b97ff;
 $highlightedText: #82e0ff;
 
-.summary-box {
+.gptPopup-summary-box {
     position: fixed;
     top: 115px;
     left: 75px;
@@ -35,7 +35,7 @@ $highlightedText: #82e0ff;
         right: 0;
         bottom: 0;
         cursor: se-resize;
-      }
+    }
 
     .summary-heading {
         display: flex;
@@ -63,12 +63,12 @@ $highlightedText: #82e0ff;
         cursor: pointer;
     }
 
-    .content-wrapper {
+    .gptPopup-content-wrapper {
         padding-top: 10px;
         min-height: 50px;
         // max-height: 150px;
         overflow-y: auto;
-        height: 100%
+        height: 100%;
     }
 
     .btns-wrapper-gpt {
@@ -78,7 +78,7 @@ $highlightedText: #82e0ff;
         align-items: center;
         flex-direction: column;
 
-        .inputWrapper{
+        .inputWrapper {
             display: flex;
             justify-content: center;
             align-items: center;
@@ -87,17 +87,15 @@ $highlightedText: #82e0ff;
             bottom: 0;
             width: 100%;
             background-color: white;
-
-
         }
 
-        .searchBox-input{
+        .searchBox-input {
             height: 40px;
             border-radius: 10px;
             position: absolute;
             bottom: 10px;
             border-color: #5b97ff;
-            width: 90%
+            width: 90%;
         }
 
         .chat-wrapper {
@@ -106,52 +104,41 @@ $highlightedText: #82e0ff;
             width: 100%;
             max-height: calc(100vh - 80px);
             overflow-y: auto;
-            padding-bottom: 60px; 
+            padding-bottom: 60px;
         }
-    
+
         .chat-bubbles {
             margin-top: 20px;
             display: flex;
             flex-direction: column;
             flex-grow: 1;
         }
-    
+
         .chat-bubble {
             padding: 10px;
             margin-bottom: 10px;
             border-radius: 10px;
             max-width: 60%;
         }
-    
+
         .user-message {
             background-color: #283d53;
             align-self: flex-end;
             color: whitesmoke;
         }
-    
+
         .chat-message {
             background-color: #367ae7;
             align-self: flex-start;
-            color:whitesmoke;
+            color: whitesmoke;
         }
 
-
-        
-
         .summarizing {
             display: flex;
             align-items: center;
         }
-
-        
-
-        
-
-        
     }
 
-
-
     .text-btn {
         &:hover {
             background-color: $button;
@@ -198,18 +185,12 @@ $highlightedText: #82e0ff;
     color: #666;
 }
 
-
-
-
-
 @keyframes spin {
     to {
         transform: rotate(360deg);
     }
 }
 
-
-
 .image-content-wrapper {
     display: flex;
     flex-direction: column;
diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.tsx b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
index 2d95ac2eb..691c24792 100644
--- a/src/client/views/pdf/GPTPopup/GPTPopup.tsx
+++ b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
@@ -1,6 +1,6 @@
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Button, IconButton, Toggle, ToggleType, Type } from '@dash/components';
-import { action, makeObservable, observable, runInAction } from 'mobx';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { action, makeObservable, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { CgClose, CgCornerUpLeft } from 'react-icons/cg';
@@ -10,7 +10,7 @@ import { ClientUtils } from '../../../../ClientUtils';
 import { Doc } from '../../../../fields/Doc';
 import { NumCast, StrCast } from '../../../../fields/Types';
 import { Networking } from '../../../Network';
-import { GPTCallType, gptAPICall, gptImageCall } from '../../../apis/gpt/GPT';
+import { GPTCallType, GPTTypeStyle, gptAPICall, gptImageCall } from '../../../apis/gpt/GPT';
 import { DocUtils } from '../../../documents/DocUtils';
 import { Docs } from '../../../documents/Documents';
 import { SettingsManager } from '../../../util/SettingsManager';
@@ -19,7 +19,6 @@ import { ObservableReactComponent } from '../../ObservableReactComponent';
 import { DocumentView } from '../../nodes/DocumentView';
 import { AnchorMenu } from '../AnchorMenu';
 import './GPTPopup.scss';
-import { isJSDocProtectedTag } from 'typescript';
 
 export enum GPTPopupMode {
     SUMMARY,
@@ -42,133 +41,79 @@ export enum GPTQuizType {
 export class GPTPopup extends ObservableReactComponent<object> {
     // eslint-disable-next-line no-use-before-define
     static Instance: GPTPopup;
-    private messagesEndRef: React.RefObject<HTMLDivElement>;
-
-    @observable private chatMode: boolean = false;
-    private correlatedColumns: string[] = [];
-
-    @observable public Visible: boolean = false;
-    @action public setVisible = (vis: boolean) => {
-        this.Visible = vis;
-    };
-    @observable public loading: boolean = false;
-    @action public setLoading = (loading: boolean) => {
-        this.loading = loading;
-    };
-    @observable public text: string = '';
-    @action public setText = (text: string) => {
-        this.text = text;
-    };
-    @observable public selectedText: string = '';
-    @action public setSelectedText = (text: string) => {
-        this.selectedText = text;
-    };
-    @observable public dataJson: string = '';
-    public dataChatPrompt: string | undefined = undefined;
-    @action public setDataJson = (text: string) => {
-        if (text === '') this.dataChatPrompt = '';
-        this.dataJson = text;
-    };
-
-    @observable public imgDesc: string = '';
-    @action public setImgDesc = (text: string) => {
-        this.imgDesc = text;
-    };
-
-    @observable public imgUrls: string[][] = [];
-    @action public setImgUrls = (imgs: string[][]) => {
-        this.imgUrls = imgs;
-    };
-
-    @observable public mode: GPTPopupMode = GPTPopupMode.SUMMARY;
-    @action public setMode = (mode: GPTPopupMode) => {
-        this.mode = mode;
-    };
-
-    @observable public highlightRange: number[] = [];
-    @action callSummaryApi = () => {};
-
-    @observable private done: boolean = false;
-    @action public setDone = (done: boolean) => {
-        this.done = done;
-        this.chatMode = false;
-    };
-
-    // change what can be a ref into a ref
-    @observable private sidebarId: string = '';
-    @action public setSidebarId = (id: string) => {
-        this.sidebarId = id;
-    };
-
-    @observable private imgTargetDoc: Doc | undefined;
-    @action public setImgTargetDoc = (anchor: Doc) => {
-        this.imgTargetDoc = anchor;
-    };
-
-    @observable private textAnchor: Doc | undefined;
-    @action public setTextAnchor = (anchor: Doc) => {
-        this.textAnchor = anchor;
+    private _regenerateCallback: (() => Promise<void>) | null = null;
+    private _messagesEndRef: React.RefObject<HTMLDivElement>;
+    private _correlatedColumns: string[] = [];
+    private _dataChatPrompt: string | undefined = undefined;
+    private _imgTargetDoc: Doc | undefined;
+    private _textAnchor: Doc | undefined;
+    private _dataJson: string = '';
+    private _sortDesc: string = '';
+    private _sidebarFieldKey: string = '';
+    private _imgDesc: string = '';
+    private _selectedText: string = '';
+
+    public setImgDesc = (text: string) => (this._imgDesc = text);
+    public setSidebarFieldKey = (id: string) => (this._sidebarFieldKey = id);
+    public setSortDesc = (t: string) => (this._sortDesc = t);
+    public setImgTargetDoc = (anchor: Doc) => (this._imgTargetDoc = anchor);
+    public setTextAnchor = (anchor: Doc) => (this._textAnchor = anchor);
+    public onGptResponse?: (sortResult: string, questionType: GPTTypeStyle, tag?: string) => void;
+    public onQuizRandom?: () => void;
+    public setDataJson = (text: string) => {
+        if (text === '') this._dataChatPrompt = '';
+        this._dataJson = text;
     };
 
-    @observable public sortDesc: string = '';
-    @action public setSortDesc = (t: string) => {
-        this.sortDesc = t;
-    };
-
-    onSortComplete?: (sortResult: string, questionType: string, tag?: string) => void;
-    onQuizRandom?: () => void;
-
-    @observable collectionDoc: Doc | undefined = undefined;
-    @action setCollectionDoc(doc: Doc | undefined) {
-        this.collectionDoc = doc;
-    }
-
-    @observable sortRespText: string = '';
-
-    @action setSortRespText(resp: string) {
-        this.sortRespText = resp;
+    constructor(props: object) {
+        super(props);
+        makeObservable(this);
+        GPTPopup.Instance = this;
+        this._messagesEndRef = React.createRef();
     }
 
-    @observable chatSortPrompt: string = '';
-
-    sortPromptChanged = action((e: React.ChangeEvent<HTMLInputElement>) => {
-        this.chatSortPrompt = e.target.value;
-    });
+    componentDidUpdate = () => this._gptProcessing && this.setStopAnimatingResponse(false);
 
-    @observable quizAnswer: string = '';
-
-    quizAnswerChanged = action((e: React.ChangeEvent<HTMLInputElement>) => {
-        this.quizAnswer = e.target.value;
-    });
-
-    @observable conversationArray: string[] = ['Hi! In this pop up, you can ask ChatGPT questions about your documents and filter / sort them. '];
+    @observable private _conversationArray: string[] = ['Hi! In this pop up, you can ask ChatGPT questions about your documents and filter / sort them. '];
+    @observable private _chatEnabled: boolean = false;
+    @action private setChatEnabled = (start: boolean) => (this._chatEnabled = start);
+    @observable public Visible: boolean = false;
+    @action public setVisible = (vis: boolean) => (this.Visible = vis);
+    @observable private _gptProcessing: boolean = false;
+    @action public setGptProcessing = (loading: boolean) => (this._gptProcessing = loading);
+    @observable private _responseText: string = '';
+    @action public setResponseText = (text: string) => (this._responseText = text);
+    @observable private _imgUrls: string[][] = [];
+    @action public setImgUrls = (imgs: string[][]) => (this._imgUrls = imgs);
+    @observable private _mode: GPTPopupMode = GPTPopupMode.SUMMARY;
+    @action public setMode = (mode: GPTPopupMode) => (this._mode = mode);
+    @observable private _collectionContext: Doc | undefined = undefined;
+    @action setCollectionContext = (doc: Doc | undefined) => (this._collectionContext = doc);
+    @observable private _sortPrompt: string = '';
+    @action setSortPrompt = (e: React.ChangeEvent<HTMLInputElement>) => (this._sortPrompt = e.target.value);
+    @observable private _quizAnswer: string = '';
+    @action setQuizAnswer = (e: React.ChangeEvent<HTMLInputElement>) => (this._quizAnswer = e.target.value);
+    @observable private _stopAnimatingResponse: boolean = false;
+    @action public setStopAnimatingResponse = (done: boolean) => (this._stopAnimatingResponse = done);
 
     /**
-     * When the cards are in quiz mode in the card view, allows gpt to determine whether the user's answer was correct
-     * @returns
+     * Callback function that causes the card view to update the childpair string list
+     * @param callback
      */
-    generateQuiz = (selected: Doc) =>
-        (this.regenerateCallback?.() ?? Promise.resolve()).then(() =>
-            this.generateRubric(selected).then(() =>
-                gptAPICall('Question: ' + StrCast(selected.gptInputText) + ' UserAnswer: ' + this.quizAnswer + '.  Rubric: ' + StrCast(selected.gptRubric), GPTCallType.QUIZ)
-                    .then(res => {
-                        if (res) {
-                            this.setQuizResp(res);
-                            this.conversationArray.push(res);
-                            this.onQuizRandom?.();
-                        } else {
-                            console.error('GPT provided no response');
-                        }
-                    })
-                    .catch(err => console.error('GPT call failed', err))
-            )
-        );
+    public setRegenerateCallback(collectionDoc: Doc | undefined, callback: null | (() => Promise<void>)) {
+        this.setCollectionContext(collectionDoc);
+        this._regenerateCallback = callback;
+    }
+
+    public addDoc: (doc: Doc | Doc[], sidebarKey?: string | undefined) => boolean = () => false;
+    public createFilteredDoc: (axes?: string[]) => boolean = () => false;
+    public addToCollection: ((doc: Doc | Doc[], annotationKey?: string | undefined) => boolean) | undefined;
+    public questionTypeNumberToStyle = (questionType: string) => +questionType.split(' ')[0][0];
 
     /**
-     * Generates a rubric by which to compare the user's answer to
-     * @param inputText user's answer
+     * Generates a rubric for evaluating the user's description of the document's text
      * @param doc the doc the user is providing info about
-     * @returns gpt's response
+     * @returns gpt's response rubric
      */
     generateRubric = (doc: Doc) =>
         StrCast(doc.gptRubric)
@@ -177,148 +122,129 @@ export class GPTPopup extends ObservableReactComponent<object> {
                   .then(res => (doc.gptRubric = res))
                   .catch(err => console.error('GPT call failed', err));
 
-    @observable private regenerateCallback: (() => Promise<void>) | null = null;
-
     /**
-     * Callback function that causes the card view to update the childpair string list
-     * @param callback
+     * When the cards are in quiz mode in the card view, allows gpt to determine whether the user's answer was correct
+     * @param doc the doc the user is providing info about
+     * @param quizAnswer the user's answer/description for the document
+     * @returns
      */
-    @action public setRegenerateCallback(collectionDoc: Doc | undefined, callback: null | (() => Promise<void>)) {
-        this.setCollectionDoc(collectionDoc);
-        this.regenerateCallback = callback;
-    }
-
-    public addDoc: (doc: Doc | Doc[], sidebarKey?: string | undefined) => boolean = () => false;
-    public createFilteredDoc: (axes?: string[]) => boolean = () => false;
-    public addToCollection: ((doc: Doc | Doc[], annotationKey?: string | undefined) => boolean) | undefined;
-
-    @observable quizRespText: string = '';
-
-    @action setQuizResp(resp: string) {
-        this.quizRespText = resp;
-    }
+    generateQuizAnswerAnalysis = (doc: Doc, quizAnswer: string) =>
+        (this._regenerateCallback?.() ?? Promise.resolve()).then(
+            () =>
+            this.generateRubric(doc).then(() =>
+                gptAPICall(
+                    `Question: ${StrCast(doc.gptInputText)}; 
+                     UserAnswer: ${quizAnswer};
+                     Rubric: ${StrCast(doc.gptRubric)}`,
+                    GPTCallType.QUIZ
+                ).then(res => {
+                    this._conversationArray.push(res || 'GPT provided no answer');
+                    this.onQuizRandom?.();
+                })
+                .catch(err => console.error('GPT call failed', err))
+            ) // prettier-ignore
+        );
 
     /**
      * Generates a response to the user's question depending on the type of their question
+     * @param userPrompt the user's input that chat will respond to
      */
-    generateCard = async () => {
-        this.setLoading(true);
-
-        await this.regenerateCallback?.();
-
-        try {
-            const questionType = await gptAPICall(this.chatSortPrompt, GPTCallType.TYPE);
-            const questionNumber = questionType.split(' ')[0][0];
-            const res = await (() => {
-                switch (questionNumber) {
-                    case '1':
-                    case '2':
-                    case '4': return gptAPICall(this.sortDesc, GPTCallType.SUBSET, this.chatSortPrompt);
-                    case '6': return gptAPICall(this.sortDesc, GPTCallType.SORT, this.chatSortPrompt);
-                    default: return gptAPICall(StrCast(DocumentView.SelectedDocs().lastElement()?.gptInputText), GPTCallType.INFO, this.chatSortPrompt);
-                }})(); // prettier-ignore
-
-            // Trigger the callback with the result
-            if (this.onSortComplete) {
-                this.onSortComplete(res || 'Something went wrong :(', questionNumber, questionType.split(' ').slice(1).join(' '));
-
-                let explanation = res;
-
-                if (questionType != '5' && questionType != '3') {
-                    // Extract explanation surrounded by ------ at the top or both at the top and bottom
-                    const explanationMatch = res.match(/------\s*([\s\S]*?)\s*(?:------|$)/) || [];
-                    explanation = explanationMatch[1] ? explanationMatch[1].trim() : 'No explanation found';
-                }
-
-                // Set the extracted explanation to sortRespText
-                this.setSortRespText(explanation);
-                runInAction(() => this.conversationArray.push(this.sortRespText));
-                this.scrollToBottom();
-
-                console.log(res);
-            }
-        } catch (err) {
-            console.error(err);
-        }
-
-        this.setLoading(false);
-    };
+    generateQueryResponse = (userPrompt: string) =>
+        (this._regenerateCallback ?? Promise.resolve)().then(() =>
+            gptAPICall(userPrompt, GPTCallType.TYPE).then(questionType =>
+                (() => {
+                    switch (this.questionTypeNumberToStyle(questionType)) {
+                        case GPTTypeStyle.AssignTags:
+                        case GPTTypeStyle.Filter:
+                        case GPTTypeStyle.ChooseDoc: return gptAPICall(this._sortDesc, GPTCallType.SUBSET, userPrompt);
+                        case GPTTypeStyle.SortDocs:  return gptAPICall(this._sortDesc, GPTCallType.SORT, userPrompt);
+                        default:                     return gptAPICall(StrCast(DocumentView.SelectedDocs().lastElement()?.gptInputText), GPTCallType.INFO, userPrompt);
+                    } // prettier-ignore
+                })().then(
+                    action(res => {
+                        // Trigger the callback with the result
+                        this.onGptResponse?.(res || 'Something went wrong :(', this.questionTypeNumberToStyle(questionType), questionType.split(' ').slice(1).join(' '));
+                        this._conversationArray.push(
+                            ![GPTTypeStyle.GeneralInfo, GPTTypeStyle.DocInfo].includes(this.questionTypeNumberToStyle(questionType))? 
+                            // Extract explanation surrounded by ------ at the top or both at the top and bottom
+                            (res.match(/------\s*([\s\S]*?)\s*(?:------|$)/) ?? [])[1]?.trim() ?? 'No explanation found' : res);
+                    })
+                ).catch(err => console.log(err))
+            ).catch(err => console.log(err))
+        ); // prettier-ignore
 
     /**
      * Generates a Dalle image and uploads it to the server.
      */
-    generateImage = async () => {
-        if (this.imgDesc === '') return undefined;
-        this.setImgUrls([]);
-        this.setMode(GPTPopupMode.IMAGE);
-        this.setVisible(true);
-        this.setLoading(true);
-
-        try {
-            const imageUrls = await gptImageCall(this.imgDesc);
-            if (imageUrls && imageUrls[0]) {
-                const [result] = await Networking.PostToServer('/uploadRemoteImage', { sources: [imageUrls[0]] });
-                const source = ClientUtils.prepend(result.accessPaths.agnostic.client);
-                this.setImgUrls([[imageUrls[0], source]]);
-            }
-        } catch (err) {
-            console.error(err);
+    generateImage = () => {
+        if (this._imgDesc !== '') {
+            this.setImgUrls([]);
+            this.setMode(GPTPopupMode.IMAGE);
+            this.setVisible(true);
+            this.setGptProcessing(true);
+
+            return gptImageCall(this._imgDesc)
+                .then(imageUrls =>
+                    imageUrls?.[0]
+                        ? Networking.PostToServer('/uploadRemoteImage', { sources: [imageUrls[0]] }).then(res => {
+                              const source = ClientUtils.prepend(res[0].accessPaths.agnostic.client);
+                              return this.setImgUrls([[imageUrls[0]!, source]]);
+                          })
+                        : undefined
+                )
+                .catch(err => console.error(err))
+                .finally(() => this.setGptProcessing(false));
         }
-        this.setLoading(false);
         return undefined;
     };
 
     /**
-     * Completes an API call to generate a summary of
-     * this.selectedText in the popup.
+     * Completes an API call to generate a summary of the specified text
+     *
+     * @param text the text to summarizz
      */
-    generateSummary = async () => {
-        GPTPopup.Instance.setVisible(true);
-        GPTPopup.Instance.setMode(GPTPopupMode.SUMMARY);
-        GPTPopup.Instance.setLoading(true);
-
-        try {
-            const res = await gptAPICall(this.selectedText, GPTCallType.SUMMARY);
-            GPTPopup.Instance.setText(res || 'Something went wrong.');
-        } catch (err) {
-            console.error(err);
-        }
-        GPTPopup.Instance.setLoading(false);
+    generateSummary = (text?: string) => {
+        this._selectedText = text ?? this._selectedText;
+        this.setVisible(true);
+        this.setMode(GPTPopupMode.SUMMARY);
+        this.setGptProcessing(true);
+        return gptAPICall(this._selectedText, GPTCallType.SUMMARY)
+            .then(res => this.setResponseText(res || 'Something went wrong.'))
+            .catch(err => console.error(err))
+            .finally(() => this.setGptProcessing(false));
     };
 
     /**
      * Completes an API call to generate an analysis of
      * this.dataJson in the popup.
      */
-    generateDataAnalysis = async () => {
-        GPTPopup.Instance.setVisible(true);
-        GPTPopup.Instance.setLoading(true);
-        try {
-            const res = await gptAPICall(this.dataJson, GPTCallType.DATA, this.dataChatPrompt);
-            const json = JSON.parse(res! as string);
-            const keys = Object.keys(json);
-            this.correlatedColumns = [];
-            this.correlatedColumns.push(json[keys[0]]);
-            this.correlatedColumns.push(json[keys[1]]);
-            GPTPopup.Instance.setText(json[keys[2]] || 'Something went wrong.');
-        } catch (err) {
-            console.error(err);
-        }
-        GPTPopup.Instance.setLoading(false);
+    generateDataAnalysis = () => {
+        this.setVisible(true);
+        this.setGptProcessing(true);
+        return gptAPICall(this._dataJson, GPTCallType.DATA, this._dataChatPrompt)
+            .then(res => {
+                const json = JSON.parse(res! as string);
+                const keys = Object.keys(json);
+                this._correlatedColumns = [];
+                this._correlatedColumns.push(json[keys[0]]);
+                this._correlatedColumns.push(json[keys[1]]);
+                this.setResponseText(json[keys[2]] || 'Something went wrong.');
+            })
+            .catch(err => console.error(err))
+            .finally(() => this.setGptProcessing(false));
     };
 
     /**
      * Transfers the summarization text to a sidebar annotation text document.
      */
     private transferToText = () => {
-        const newDoc = Docs.Create.TextDocument(this.text.trim(), {
+        const newDoc = Docs.Create.TextDocument(this._responseText.trim(), {
             _width: 200,
             _height: 50,
             _layout_fitWidth: true,
             _layout_autoHeight: true,
         });
-        this.addDoc(newDoc, this.sidebarId);
-        // newDoc.data = 'Hello world';
+        this.addDoc(newDoc, this._sidebarFieldKey);
         const anchor = AnchorMenu.Instance?.GetAnchor(undefined, false);
         if (anchor) {
             DocUtils.MakeLink(newDoc, anchor, {
@@ -330,73 +256,35 @@ export class GPTPopup extends ObservableReactComponent<object> {
     /**
      * Creates a histogram to show the correlation relationship that was found
      */
-    private createVisualization = () => {
-        this.createFilteredDoc(this.correlatedColumns);
-    };
+    private createVisualization = () => this.createFilteredDoc(this._correlatedColumns);
 
     /**
      * Transfers the image urls to actual image docs
      */
     private transferToImage = (source: string) => {
-        const textAnchor = this.textAnchor ?? this.imgTargetDoc;
-        if (!textAnchor) return;
-        const newDoc = Docs.Create.ImageDocument(source, {
-            x: NumCast(textAnchor.x) + NumCast(textAnchor._width) + 10,
-            y: NumCast(textAnchor.y),
-            _height: 200,
-            _width: 200,
-            data_nativeWidth: 1024,
-            data_nativeHeight: 1024,
-        });
-        if (Doc.IsInMyOverlay(textAnchor)) {
-            newDoc.overlayX = textAnchor.x;
-            newDoc.overlayY = NumCast(textAnchor.y) + NumCast(textAnchor._height);
-            Doc.AddToMyOverlay(newDoc);
-        } else {
-            this.addToCollection?.(newDoc);
-        }
-        // Create link between prompt and image
-        DocUtils.MakeLink(textAnchor, newDoc, { link_relationship: 'Image Prompt' });
-    };
-
-    /**
-     * Creates a chatbox for analyzing data so that users can ask specific questions.
-     */
-    private chatWithAI = () => {
-        this.chatMode = true;
-    };
-    dataPromptChanged = action((e: React.ChangeEvent<HTMLInputElement>) => {
-        this.dataChatPrompt = e.target.value;
-    });
-
-    private getPreviewUrl = (source: string) => source.split('.').join('_m.');
-
-    constructor(props: object) {
-        super(props);
-        makeObservable(this);
-        GPTPopup.Instance = this;
-        this.messagesEndRef = React.createRef();
-    }
-
-    scrollToBottom = () => {
-        setTimeout(() => {
-            // Code to execute after 1 second (1000 ms)
-            if (this.messagesEndRef.current) {
-                this.messagesEndRef.current.scrollIntoView({ behavior: 'smooth', block: 'end' });
+        const textAnchor = this._textAnchor ?? this._imgTargetDoc;
+        if (textAnchor) {
+            const newDoc = Docs.Create.ImageDocument(source, {
+                x: NumCast(textAnchor.x) + NumCast(textAnchor._width) + 10,
+                y: NumCast(textAnchor.y),
+                _height: 200,
+                _width: 200,
+                data_nativeWidth: 1024,
+                data_nativeHeight: 1024,
+            });
+            if (Doc.IsInMyOverlay(textAnchor)) {
+                newDoc.overlayX = textAnchor.x;
+                newDoc.overlayY = NumCast(textAnchor.y) + NumCast(textAnchor._height);
+                Doc.AddToMyOverlay(newDoc);
+            } else {
+                this.addToCollection?.(newDoc);
             }
-        }, 50);
-    };
-
-    componentDidUpdate = () => {
-        if (this.loading) {
-            this.setDone(false);
+            // Create link between prompt and image
+            DocUtils.MakeLink(textAnchor, newDoc, { link_relationship: 'Image Prompt' });
         }
     };
 
-    @observable quizMode: GPTQuizType = GPTQuizType.CURRENT;
-    @action setQuizMode(g: GPTQuizType) {
-        this.quizMode = g;
-    }
+    scrollToBottom = () => setTimeout(() => this._messagesEndRef.current?.scrollIntoView({ behavior: 'smooth', block: 'end' }), 50);
 
     cardMenu = () => (
         <div className="btns-wrapper-gpt">
@@ -419,7 +307,7 @@ export class GPTPopup extends ObservableReactComponent<object> {
                 tooltip="Test your knowledge with ChatGPT!"
                 text="Quiz Cards!"
                 onClick={() => {
-                    this.conversationArray = ['Define the selected card!'];
+                    this._conversationArray = ['Define the selected card!'];
                     this.setMode(GPTPopupMode.QUIZ);
                     this.onQuizRandom?.();
                 }}
@@ -437,27 +325,19 @@ export class GPTPopup extends ObservableReactComponent<object> {
     );
 
     @action
-    handleKeyPress = (e: React.KeyboardEvent, isSort: boolean) => {
+    handleKeyPress = async (e: React.KeyboardEvent, isSort: boolean) => {
         if (e.key === 'Enter') {
             e.stopPropagation();
 
+            this.setGptProcessing(true);
             if (isSort) {
-                this.conversationArray.push(this.chatSortPrompt);
-                this.generateCard().then(
-                    action(() => {
-                        this.chatSortPrompt = '';
-                    })
-                );
+                this._conversationArray.push(this._sortPrompt);
+                await this.generateQueryResponse(this._sortPrompt).then(action(() => (this._sortPrompt = '')));
             } else {
-                this.conversationArray.push(this.quizAnswer);
-                this.setLoading(true);
-                this.generateQuiz(DocumentView.SelectedDocs().lastElement()).then(
-                    action(() => {
-                        this.quizAnswer = '';
-                        this.setLoading(false);
-                    })
-                );
+                this._conversationArray.push(this._quizAnswer);
+                await this.generateQuizAnswerAnalysis(DocumentView.SelectedDocs().lastElement(), this._quizAnswer).then(action(() => (this._quizAnswer = '')));
             }
+            this.setGptProcessing(false);
 
             this.scrollToBottom();
         }
@@ -467,26 +347,24 @@ export class GPTPopup extends ObservableReactComponent<object> {
         <div className="btns-wrapper-gpt">
             <div className="chat-wrapper">
                 <div className="chat-bubbles">
-                    {this.conversationArray.map((message, index) => (
+                    {this._conversationArray.map((message, index) => (
                         <div key={index} className={`chat-bubble ${index % 2 === 1 ? 'user-message' : 'chat-message'}`}>
                             {message}
                         </div>
                     ))}
-                    {this.loading && <div className={`chat-bubble chat-message`}>...</div>}
+                    {this._gptProcessing && <div className={`chat-bubble chat-message`}>...</div>}
                 </div>
 
-                <div ref={this.messagesEndRef} style={{ height: '100px' }} />
+                <div ref={this._messagesEndRef} style={{ height: '100px' }} />
             </div>
             <div className="inputWrapper">
                 <input
                     className="searchBox-input"
                     defaultValue=""
-                    value={isSort ? this.chatSortPrompt : this.quizAnswer} // Controlled input
+                    value={isSort ? this._sortPrompt : this._quizAnswer} // Controlled input
                     autoComplete="off"
-                    onChange={isSort ? this.sortPromptChanged : this.quizAnswerChanged}
-                    onKeyDown={e => {
-                        this.handleKeyPress(e, isSort);
-                    }}
+                    onChange={isSort ? this.setSortPrompt : this.setQuizAnswer}
+                    onKeyDown={e => this.handleKeyPress(e, isSort)}
                     type="text"
                     placeholder={`${isSort ? 'Have ChatGPT sort, tag, define, or filter your cards for you!' : 'Define the selected card!'}`}
                 />
@@ -497,7 +375,7 @@ export class GPTPopup extends ObservableReactComponent<object> {
     sortBox = (isSort: boolean) => (
         <div className="gptPopup-sortBox" style={{ height: '80%' }}>
             {this.heading(isSort ? 'SORTING' : 'QUIZ')}
-            {this.mode === GPTPopupMode.CARD ? this.cardMenu() : this.cardActual(isSort)}
+            {this._mode === GPTPopupMode.CARD ? this.cardMenu() : this.cardActual(isSort)}
         </div>
     );
 
@@ -505,7 +383,7 @@ export class GPTPopup extends ObservableReactComponent<object> {
         <div style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
             {this.heading('GENERATED IMAGE')}
             <div className="image-content-wrapper">
-                {this.imgUrls.map((rawSrc, i) => (
+                {this._imgUrls.map((rawSrc, i) => (
                     <div key={rawSrc[0] + i} className="img-wrapper">
                         <div className="img-container">
                             <img key={rawSrc[0]} src={rawSrc[0]} width={150} height={150} alt="dalle generation" />
@@ -516,7 +394,7 @@ export class GPTPopup extends ObservableReactComponent<object> {
                     </div>
                 ))}
             </div>
-            {!this.loading && <IconButton tooltip="Generate Again" onClick={this.generateImage} icon={<FontAwesomeIcon icon="redo-alt" size="lg" />} color={StrCast(Doc.UserDoc().userVariantColor)} />}
+            {this._gptProcessing ? null : <IconButton tooltip="Generate Again" onClick={this.generateImage} icon={<FontAwesomeIcon icon="redo-alt" size="lg" />} color={StrCast(Doc.UserDoc().userVariantColor)} />}
         </div>
     );
 
@@ -524,44 +402,35 @@ export class GPTPopup extends ObservableReactComponent<object> {
         <>
             <div>
                 {this.heading('SUMMARY')}
-                <div className="content-wrapper">
-                    {!this.loading &&
-                        (!this.done ? (
+                <div className="gptPopup-content-wrapper">
+                    {!this._gptProcessing &&
+                        (!this._stopAnimatingResponse ? (
                             <TypeAnimation
                                 speed={50}
                                 sequence={[
-                                    this.text,
+                                    this._responseText,
                                     () => {
-                                        setTimeout(() => {
-                                            this.setDone(true);
-                                        }, 500);
+                                        setTimeout(() => this.setStopAnimatingResponse(true), 500);
                                     },
                                 ]}
                             />
                         ) : (
-                            this.text
+                            this._responseText
                         ))}
                 </div>
             </div>
-            {!this.loading && (
+            {!this._gptProcessing && (
                 <div className="btns-wrapper">
-                    {this.done ? (
+                    {this._stopAnimatingResponse ? (
                         <>
-                            <IconButton tooltip="Generate Again" onClick={this.generateSummary /* this.callSummaryApi */} icon={<FontAwesomeIcon icon="redo-alt" size="lg" />} color={StrCast(SettingsManager.userVariantColor)} />
+                            <IconButton tooltip="Generate Again" onClick={() => this.generateSummary(this._selectedText + ' ')} icon={<FontAwesomeIcon icon="redo-alt" size="lg" />} color={StrCast(SettingsManager.userVariantColor)} />
                             <Button tooltip="Transfer to text" text="Transfer To Text" onClick={this.transferToText} color={StrCast(SettingsManager.userVariantColor)} type={Type.TERT} />
                         </>
                     ) : (
                         <div className="summarizing">
                             <span>Summarizing</span>
                             <ReactLoading type="bubbles" color="#bcbcbc" width={20} height={20} />
-                            <Button
-                                text="Stop Animation"
-                                onClick={() => {
-                                    this.setDone(true);
-                                }}
-                                color={StrCast(SettingsManager.userVariantColor)}
-                                type={Type.TERT}
-                            />
+                            <Button text="Stop Animation" onClick={() => this.setStopAnimatingResponse(true)} color={StrCast(SettingsManager.userVariantColor)} type={Type.TERT} />
                         </div>
                     )}
                 </div>
@@ -573,33 +442,31 @@ export class GPTPopup extends ObservableReactComponent<object> {
         <>
             <div>
                 {this.heading('ANALYSIS')}
-                <div className="content-wrapper">
-                    {!this.loading &&
-                        (!this.done ? (
+                <div className="gptPopup-content-wrapper">
+                    {!this._gptProcessing &&
+                        (!this._stopAnimatingResponse ? (
                             <TypeAnimation
                                 speed={50}
                                 sequence={[
-                                    this.text,
+                                    this._responseText,
                                     () => {
-                                        setTimeout(() => {
-                                            this.setDone(true);
-                                        }, 500);
+                                        setTimeout(() => this.setStopAnimatingResponse(true), 500);
                                     },
                                 ]}
                             />
                         ) : (
-                            this.text
+                            this._responseText
                         ))}
                 </div>
             </div>
-            {!this.loading && (
+            {!this._gptProcessing && (
                 <div className="btns-wrapper">
-                    {this.done ? (
-                        this.chatMode ? (
+                    {this._stopAnimatingResponse ? (
+                        this._chatEnabled ? (
                             <input
                                 defaultValue=""
                                 autoComplete="off"
-                                onChange={this.dataPromptChanged}
+                                onChange={e => (this._dataChatPrompt = e.target.value)}
                                 onKeyDown={e => {
                                     e.key === 'Enter' ? this.generateDataAnalysis() : null;
                                     e.stopPropagation();
@@ -613,21 +480,14 @@ export class GPTPopup extends ObservableReactComponent<object> {
                         ) : (
                             <>
                                 <Button tooltip="Transfer to text" text="Transfer To Text" onClick={this.transferToText} color={StrCast(SnappingManager.userVariantColor)} type={Type.TERT} />
-                                <Button tooltip="Chat with AI" text="Chat with AI" onClick={this.chatWithAI} color={StrCast(SnappingManager.userVariantColor)} type={Type.TERT} />
+                                <Button tooltip="Chat with AI" text="Chat with AI" onClick={() => this.setChatEnabled(true)} color={StrCast(SnappingManager.userVariantColor)} type={Type.TERT} />
                             </>
                         )
                     ) : (
                         <div className="summarizing">
                             <span>Summarizing</span>
                             <ReactLoading type="bubbles" color="#bcbcbc" width={20} height={20} />
-                            <Button
-                                text="Stop Animation"
-                                onClick={() => {
-                                    this.setDone(true);
-                                }}
-                                color={StrCast(SnappingManager.userVariantColor)}
-                                type={Type.TERT}
-                            />
+                            <Button text="Stop Animation" onClick={() => this.setStopAnimatingResponse(true)} color={StrCast(SnappingManager.userVariantColor)} type={Type.TERT} />
                         </div>
                     )}
                 </div>
@@ -636,70 +496,53 @@ export class GPTPopup extends ObservableReactComponent<object> {
     );
 
     aiWarning = () =>
-        this.done ? (
+        !this._stopAnimatingResponse ? null : (
             <div className="ai-warning">
                 <FontAwesomeIcon icon="exclamation-circle" size="sm" style={{ paddingRight: '5px' }} />
                 AI generated responses can contain inaccurate or misleading content.
             </div>
-        ) : null;
+        );
 
     heading = (headingText: string) => (
         <div className="summary-heading">
             <label className="summary-text">{headingText}</label>
-            {this.loading ? (
+            {this._gptProcessing ? (
                 <ReactLoading type="spin" color="#bcbcbc" width={14} height={14} />
             ) : (
                 <>
-                    {(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' }} />
+                    {(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 Chat filter"
                         toggleType={ToggleType.BUTTON}
                         type={Type.PRIM}
-                        toggleStatus={Doc.hasDocFilter(this.collectionDoc, 'tags', '#chat')}
-                        text={Doc.hasDocFilter(this.collectionDoc, 'tags', '#chat') ? 'filtered' : ''}
+                        toggleStatus={Doc.hasDocFilter(this._collectionContext, 'tags', '#chat')}
+                        text={Doc.hasDocFilter(this._collectionContext, 'tags', '#chat') ? 'filtered' : ''}
                         color="red"
-                        onClick={() => this.collectionDoc && Doc.setDocFilter(this.collectionDoc, 'tags', '#chat', 'remove')}
-                    />
-                    <IconButton
-                        color={StrCast(SettingsManager.userVariantColor)}
-                        tooltip="close"
-                        icon={<CgClose size="16px" />}
-                        onClick={() => {
-                            this.setVisible(false);
-                        }}
+                        onClick={() => this._collectionContext && Doc.setDocFilter(this._collectionContext, 'tags', '#chat', 'remove')}
                     />
+                    <IconButton color={StrCast(SettingsManager.userVariantColor)} tooltip="close" icon={<CgClose size="16px" />} onClick={() => this.setVisible(false)} />
                 </>
             )}
         </div>
     );
 
     render() {
-        let content;
-
-        switch (this.mode) {
-            case GPTPopupMode.SUMMARY:
-                content = this.summaryBox();
-                break;
-            case GPTPopupMode.DATA:
-                content = this.dataAnalysisBox();
-                break;
-            case GPTPopupMode.IMAGE:
-                content = this.imageBox();
-                break;
-            case GPTPopupMode.SORT:
-            case GPTPopupMode.CARD:
-            case GPTPopupMode.QUIZ:
-                content = this.sortBox(this.mode === GPTPopupMode.SORT);
-                break;
-            default:
-                content = null;
-        }
-
         return (
-            <div className="summary-box" style={{ display: this.Visible ? 'flex' : 'none' }}>
-                {content}
+            <div className="gptPopup-summary-box" style={{ display: this.Visible ? 'flex' : 'none' }}>
+                {(() => {
+                    //prettier-ignore
+                    switch (this._mode) {
+                        case GPTPopupMode.SORT:
+                        case GPTPopupMode.CARD:
+                        case GPTPopupMode.QUIZ:    return this.sortBox(this._mode === GPTPopupMode.SORT);
+                        case GPTPopupMode.SUMMARY: return this.summaryBox();
+                        case GPTPopupMode.DATA:    return this.dataAnalysisBox();
+                        case GPTPopupMode.IMAGE:   return this.imageBox();
+                        default:                   return null;
+                }
+                })()}
                 <div className="resize-handle" />
             </div>
         );
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 8728ce99c..167421a4a 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -437,7 +437,7 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> {
             AnchorMenu.Instance.jumpTo(e.clientX, e.clientY);
         }
 
-        GPTPopup.Instance.setSidebarId('data_sidebar');
+        GPTPopup.Instance.setSidebarFieldKey('data_sidebar');
         GPTPopup.Instance.addDoc = this._props.sidebarAddDoc;
         // allows for creating collection
         AnchorMenu.Instance.addToCollection = this._props.DocumentView?.()._props.addDocument;
-- 
cgit v1.2.3-70-g09d2


From b7845adf2904e18e237638a0bf907c6ec0fe06ee Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 14 Feb 2025 00:23:41 -0500
Subject: more gptPopup cleanup and starting to allow it to work on any
 collection type.

---
 .../views/collections/CollectionCardDeckView.tsx   | 18 +++---
 src/client/views/pdf/GPTPopup/GPTPopup.tsx         | 64 +++++++++++-----------
 2 files changed, 38 insertions(+), 44 deletions(-)

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

diff --git a/src/client/views/collections/CollectionCardDeckView.tsx b/src/client/views/collections/CollectionCardDeckView.tsx
index a3ec3884b..ba68504d7 100644
--- a/src/client/views/collections/CollectionCardDeckView.tsx
+++ b/src/client/views/collections/CollectionCardDeckView.tsx
@@ -75,13 +75,13 @@ export class CollectionCardView extends CollectionSubView() {
     }
 
     /**
-     * update's gpt's doc-text list and initializes callbacks
+     * update's gpt's doc-text list and initialize callbacks to respond to GPT output
      */
-    childPairStringListAndUpdateSortDesc = () =>
-        this.childPairStringList().then(sortDesc => {
-            GPTPopup.Instance.setSortDesc(sortDesc.join());
+    updateDocumentDescriptions = () =>
+        this.childPairStringList().then(docDescriptions => {
+            GPTPopup.Instance.setDocumentDescriptions(docDescriptions.join());
             GPTPopup.Instance.onGptResponse = this.processGptResponse;
-            GPTPopup.Instance.onQuizRandom = this.quizMode;
+            GPTPopup.Instance.onQuizRandom = this.randomlyChooseDoc;
         });
 
     componentDidMount() {
@@ -89,7 +89,7 @@ export class CollectionCardView extends CollectionSubView() {
             () => GPTPopup.Instance.Visible,
             vis => !vis && this.onGptHide()
         );
-        GPTPopup.Instance.setRegenerateCallback(this.Document, this.childPairStringListAndUpdateSortDesc);
+        GPTPopup.Instance.setRetrieveDocDescriptionsCallback(this.Document, this.updateDocumentDescriptions);
         this._props.setContentViewBox?.(this);
         // if card deck moves, then the child doc views are hidden so their screen to local transforms will return empty rectangles
         // when inquired from the dom (below in childScreenToLocal).  When the doc is actually rendered, we need to act like the
@@ -112,10 +112,8 @@ export class CollectionCardView extends CollectionSubView() {
 
     onGptHide = () => Doc.setDocFilter(this.Document, 'tags', '#chat', 'remove');
     componentWillUnmount() {
-        GPTPopup.Instance.setSortDesc('');
         GPTPopup.Instance.onGptResponse = undefined;
         GPTPopup.Instance.onQuizRandom = undefined;
-        GPTPopup.Instance.setRegenerateCallback(undefined, null);
         Object.keys(this._disposers).forEach(key => this._disposers[key]?.());
         this._dropDisposer?.();
     }
@@ -169,9 +167,7 @@ export class CollectionCardView extends CollectionSubView() {
     /**
      * When in quiz mode, randomly selects a document
      */
-    quizMode = () => {
-        this.layoutDoc._card_curDoc = this.childDocs[Math.floor(Math.random() * this.childDocs.length)];
-    };
+    randomlyChooseDoc = () => (this.layoutDoc._card_curDoc = this.childDocs[Math.floor(Math.random() * this.childDocs.length)]);
 
     setHoveredNodeIndex = action((index: number) => {
         if (!SnappingManager.IsDragging) this._hoveredNodeIndex = index;
diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.tsx b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
index 4457bde91..281748155 100644
--- a/src/client/views/pdf/GPTPopup/GPTPopup.tsx
+++ b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
@@ -39,20 +39,20 @@ export enum GPTQuizType {
 export class GPTPopup extends ObservableReactComponent<object> {
     // eslint-disable-next-line no-use-before-define
     static Instance: GPTPopup;
-    private _regenerateCallback: (() => Promise<void>) | null = null;
+    private _retrieveDocDescriptions: (() => Promise<void>) | null = null;
     private _messagesEndRef: React.RefObject<HTMLDivElement>;
     private _correlatedColumns: string[] = [];
     private _dataChatPrompt: string | undefined = undefined;
     private _imgTargetDoc: Doc | undefined;
     private _textAnchor: Doc | undefined;
     private _dataJson: string = '';
-    private _sortDesc: string = '';
+    private _documentDescriptions: string = '';
     private _sidebarFieldKey: string = '';
-    private _selectedText: string = '';
-    private _imgDesc: string = '';
+    private _textToSummarize: string = '';
+    private _imageDescription: string = '';
 
     public setSidebarFieldKey = (id: string) => (this._sidebarFieldKey = id);
-    public setSortDesc = (t: string) => (this._sortDesc = t);
+    public setDocumentDescriptions = (t: string) => (this._documentDescriptions = t);
     public setImgTargetDoc = (anchor: Doc) => (this._imgTargetDoc = anchor);
     public setTextAnchor = (anchor: Doc) => (this._textAnchor = anchor);
     public onGptResponse?: (sortResult: string, questionType: GPTTypeStyle, tag?: string) => void;
@@ -94,12 +94,13 @@ export class GPTPopup extends ObservableReactComponent<object> {
     @action public setStopAnimatingResponse = (done: boolean) => (this._stopAnimatingResponse = done);
 
     /**
-     * Callback function that causes the card view to update the childpair string list
-     * @param callback
+     * sets callback needed to retrieve an updated description of the collection's documents
+     * @param collectionDoc - the collection doc context for the GPT prompts
+     * @param callback - function to retrieve document descriptions
      */
-    public setRegenerateCallback(collectionDoc: Doc | undefined, callback: null | (() => Promise<void>)) {
+    public setRetrieveDocDescriptionsCallback(collectionDoc: Doc | undefined, callback: null | (() => Promise<void>)) {
         this.setCollectionContext(collectionDoc);
-        this._regenerateCallback = callback;
+        this._retrieveDocDescriptions = callback;
     }
 
     public addDoc: (doc: Doc | Doc[], sidebarKey?: string | undefined) => boolean = () => false;
@@ -126,35 +127,33 @@ export class GPTPopup extends ObservableReactComponent<object> {
      * @returns
      */
     generateQuizAnswerAnalysis = (doc: Doc, quizAnswer: string) =>
-        (this._regenerateCallback?.() ?? Promise.resolve()).then(
-            () =>
-            this.generateRubric(doc).then(() =>
-                gptAPICall(
-                    `Question: ${StrCast(doc.gptInputText)}; 
-                     UserAnswer: ${quizAnswer};
-                     Rubric: ${StrCast(doc.gptRubric)}`,
-                    GPTCallType.QUIZ
-                ).then(res => {
-                    this._conversationArray.push(res || 'GPT provided no answer');
-                    this.onQuizRandom?.();
-                })
-                .catch(err => console.error('GPT call failed', err))
-            ) // prettier-ignore
-        );
+        this.generateRubric(doc).then(() =>
+            gptAPICall(
+                `Question: ${StrCast(doc.gptInputText)}; 
+                 UserAnswer: ${quizAnswer};
+                 Rubric: ${StrCast(doc.gptRubric)}`,
+                GPTCallType.QUIZ
+            ).then(res => {
+                this._conversationArray.push(res || 'GPT provided no answer');
+                this.onQuizRandom?.();
+            })
+            .catch(err => console.error('GPT call failed', err))
+        ) // prettier-ignore
 
     /**
-     * Generates a response to the user's question depending on the type of their question
+     * Generates a response to the user's question about the docs in the collection.
+     * The type of response depends on the chat's analysis of the type of their question
      * @param userPrompt the user's input that chat will respond to
      */
     generateUserPromptResponse = (userPrompt: string) =>
-        (this._regenerateCallback ?? Promise.resolve)().then(() =>
+        (this._retrieveDocDescriptions ?? Promise.resolve)().then(() =>
             gptAPICall(userPrompt, GPTCallType.TYPE).then(questionType =>
                 (() => {
                     switch (this.questionTypeNumberToStyle(questionType)) {
                         case GPTTypeStyle.AssignTags:
                         case GPTTypeStyle.Filter:
-                        case GPTTypeStyle.ChooseDoc: return gptAPICall(this._sortDesc, GPTCallType.SUBSET, userPrompt);
-                        case GPTTypeStyle.SortDocs:  return gptAPICall(this._sortDesc, GPTCallType.SORT, userPrompt);
+                        case GPTTypeStyle.ChooseDoc: return gptAPICall(this._documentDescriptions, GPTCallType.SUBSET, userPrompt);
+                        case GPTTypeStyle.SortDocs:  return gptAPICall(this._documentDescriptions, GPTCallType.SORT, userPrompt);
                         default:                     return gptAPICall(StrCast(DocumentView.SelectedDocs().lastElement()?.gptInputText), GPTCallType.INFO, userPrompt);
                     } // prettier-ignore
                 })().then(
@@ -178,7 +177,7 @@ export class GPTPopup extends ObservableReactComponent<object> {
         this.setMode(GPTPopupMode.IMAGE);
         this.setVisible(true);
         this.setGptProcessing(true);
-        this._imgDesc = imgDesc;
+        this._imageDescription = imgDesc;
 
         return gptImageCall(imgDesc)
             .then(imageUrls =>
@@ -199,7 +198,7 @@ export class GPTPopup extends ObservableReactComponent<object> {
      * @param text the text to summarizz
      */
     generateSummary = (text: string) => {
-        this._selectedText = text;
+        this._textToSummarize = text;
         this.setVisible(true);
         this.setMode(GPTPopupMode.SUMMARY);
         this.setGptProcessing(true);
@@ -355,7 +354,6 @@ export class GPTPopup extends ObservableReactComponent<object> {
             <div className="inputWrapper">
                 <input
                     className="searchBox-input"
-                    defaultValue=""
                     value={isUserPrompt ? this._userPrompt : this._quizAnswer} // Controlled input
                     autoComplete="off"
                     onChange={isUserPrompt ? this.setUserPrompt : this.setQuizAnswer}
@@ -389,7 +387,7 @@ export class GPTPopup extends ObservableReactComponent<object> {
                     </div>
                 ))}
             </div>
-            {this._gptProcessing ? null : <IconButton tooltip="Generate Again" onClick={() => this.generateImage(this._imgDesc)} icon={<FontAwesomeIcon icon="redo-alt" size="lg" />} color={StrCast(Doc.UserDoc().userVariantColor)} />}
+            {this._gptProcessing ? null : <IconButton tooltip="Generate Again" onClick={() => this.generateImage(this._imageDescription)} icon={<FontAwesomeIcon icon="redo-alt" size="lg" />} color={StrCast(Doc.UserDoc().userVariantColor)} />}
         </div>
     );
 
@@ -418,7 +416,7 @@ export class GPTPopup extends ObservableReactComponent<object> {
                 <div className="btns-wrapper">
                     {this._stopAnimatingResponse ? (
                         <>
-                            <IconButton tooltip="Generate Again" onClick={() => this.generateSummary(this._selectedText + ' ')} icon={<FontAwesomeIcon icon="redo-alt" size="lg" />} color={StrCast(SettingsManager.userVariantColor)} />
+                            <IconButton tooltip="Generate Again" onClick={() => this.generateSummary(this._textToSummarize + ' ')} icon={<FontAwesomeIcon icon="redo-alt" size="lg" />} color={StrCast(SettingsManager.userVariantColor)} />
                             <Button tooltip="Transfer to text" text="Transfer To Text" onClick={this.transferToText} color={StrCast(SettingsManager.userVariantColor)} type={Type.TERT} />
                         </>
                     ) : (
-- 
cgit v1.2.3-70-g09d2


From 4a9d8bf9e09a25faeba41c7ece46b86548d66847 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 14 Feb 2025 00:29:49 -0500
Subject: removed chooseDoc option from gpttypes because its redundant with
 Filter

---
 src/client/apis/gpt/GPT.ts                              | 10 ++++------
 src/client/views/collections/CollectionCardDeckView.tsx |  3 +--
 src/client/views/pdf/GPTPopup/GPTPopup.tsx              |  3 +--
 3 files changed, 6 insertions(+), 10 deletions(-)

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

diff --git a/src/client/apis/gpt/GPT.ts b/src/client/apis/gpt/GPT.ts
index 066510a6a..dc969da16 100644
--- a/src/client/apis/gpt/GPT.ts
+++ b/src/client/apis/gpt/GPT.ts
@@ -5,9 +5,8 @@ export enum GPTTypeStyle {
     AssignTags = 1,
     Filter = 2,
     DocInfo = 3,
-    ChooseDoc = 4,
-    GeneralInfo = 5,
-    SortDocs = 6,
+    GeneralInfo = 4,
+    SortDocs = 5,
 }
 enum GPTCallType {
     SUMMARY = 'summary',
@@ -136,12 +135,11 @@ const callTypeMap: { [type: string]: GPTCallOpts } = {
         prompt: `I'm going to provide you with a question. 
                  Based on the question, is the user asking you to 
                      ${GPTTypeStyle.AssignTags}. Assigns docs with tags(like star / heart etc)/labels, 
-                     ${GPTTypeStyle.ChooseDoc}. Filter docs, 
                      ${GPTTypeStyle.DocInfo}. Provide information about a specific doc 
-                     ${GPTTypeStyle.Filter}. Provide a specific doc based on a question/information 
+                     ${GPTTypeStyle.Filter}. Filter docs based on a question/information 
                      ${GPTTypeStyle.GeneralInfo}. Provide general information 
                      ${GPTTypeStyle.SortDocs}. Put cards in a specific order. 
-                Answer with only the number for 2-6. For number one, provide the number (1) and the appropriate tag`,
+                Answer with only the number for 2-5. For number one, provide the number (1) and the appropriate tag`,
     },
     subset: {
         model: 'gpt-4-turbo',
diff --git a/src/client/views/collections/CollectionCardDeckView.tsx b/src/client/views/collections/CollectionCardDeckView.tsx
index ba68504d7..e00aa65d7 100644
--- a/src/client/views/collections/CollectionCardDeckView.tsx
+++ b/src/client/views/collections/CollectionCardDeckView.tsx
@@ -444,7 +444,7 @@ export class CollectionCardView extends CollectionSubView() {
             // Split the string into individual list items
             const listItems = gptOutput.split('======').filter(item => item.trim() !== '');
 
-            if (questionType === GPTTypeStyle.Filter || questionType === GPTTypeStyle.ChooseDoc) {
+            if (questionType === GPTTypeStyle.Filter) {
                 this.childDocs.forEach(d => {
                     TagItem.removeTagFromDoc(d, '#chat');
                 });
@@ -471,7 +471,6 @@ export class CollectionCardView extends CollectionSubView() {
                             }
                             break;
                         case GPTTypeStyle.Filter:
-                        case GPTTypeStyle.ChooseDoc:
                             TagItem.addTagToDoc(doc, '#chat');
                             Doc.setDocFilter(this.Document, 'tags', '#chat', 'check');
                             break;
diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.tsx b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
index 281748155..0b1ee78e3 100644
--- a/src/client/views/pdf/GPTPopup/GPTPopup.tsx
+++ b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
@@ -151,8 +151,7 @@ export class GPTPopup extends ObservableReactComponent<object> {
                 (() => {
                     switch (this.questionTypeNumberToStyle(questionType)) {
                         case GPTTypeStyle.AssignTags:
-                        case GPTTypeStyle.Filter:
-                        case GPTTypeStyle.ChooseDoc: return gptAPICall(this._documentDescriptions, GPTCallType.SUBSET, userPrompt);
+                        case GPTTypeStyle.Filter:    return gptAPICall(this._documentDescriptions, GPTCallType.SUBSET, userPrompt);
                         case GPTTypeStyle.SortDocs:  return gptAPICall(this._documentDescriptions, GPTCallType.SORT, userPrompt);
                         default:                     return gptAPICall(StrCast(DocumentView.SelectedDocs().lastElement()?.gptInputText), GPTCallType.INFO, userPrompt);
                     } // prettier-ignore
-- 
cgit v1.2.3-70-g09d2


From c079b7aa916d7eb35ce186806ca312c84e910cb2 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 14 Feb 2025 14:07:18 -0500
Subject: major restructure of GPTPopup so that it works with all collections.

---
 src/client/apis/gpt/GPT.ts                         |  28 ++-
 src/client/views/ViewBoxInterface.ts               |   1 +
 .../views/collections/CollectionCardDeckView.tsx   | 157 ++------------
 src/client/views/collections/CollectionSubView.tsx |   1 +
 src/client/views/collections/CollectionView.tsx    |   4 +-
 .../nodes/chatbot/tools/CreateDocumentTool.ts      |  38 ++--
 .../views/nodes/formattedText/FormattedTextBox.tsx |   4 +-
 src/client/views/pdf/GPTPopup/GPTPopup.tsx         | 231 +++++++++++++--------
 src/fields/Doc.ts                                  |  24 ++-
 src/fields/Types.ts                                |   4 +
 10 files changed, 240 insertions(+), 252 deletions(-)

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

diff --git a/src/client/apis/gpt/GPT.ts b/src/client/apis/gpt/GPT.ts
index dc969da16..e1ae99d97 100644
--- a/src/client/apis/gpt/GPT.ts
+++ b/src/client/apis/gpt/GPT.ts
@@ -8,6 +8,10 @@ export enum GPTTypeStyle {
     GeneralInfo = 4,
     SortDocs = 5,
 }
+
+export const DescriptionSeperator = '======';
+export const DocSeperator = '------';
+
 enum GPTCallType {
     SUMMARY = 'summary',
     COMPLETION = 'completion',
@@ -41,7 +45,7 @@ type GPTCallOpts = {
     prompt: string;
 };
 
-const callTypeMap: { [type: string]: GPTCallOpts } = {
+const callTypeMap: { [type in GPTCallType]: GPTCallOpts } = {
     // newest model: gpt-4
     summary: { model: 'gpt-4-turbo', maxTokens: 256, temp: 0.5, prompt: 'Summarize the text given in simpler terms.' },
     edit: { model: 'gpt-4-turbo', maxTokens: 256, temp: 0.5, prompt: 'Reword the text.' },
@@ -69,7 +73,13 @@ const callTypeMap: { [type: string]: GPTCallOpts } = {
         model: 'gpt-4o',
         maxTokens: 2048,
         temp: 0.25,
-        prompt: "The user is going to give you a list of descriptions. Each one is separated by `======` on either side. Descriptions will vary in length, so make sure to only separate when you see `======`. Sort them by the user's specifications. Make sure each description is only in the list once. Each item should be separated by `======`. Immediately afterward, surrounded by `------` on BOTH SIDES, provide some insight into your reasoning for the way you sorted (and mention nothing about the formatting details given in this description). It is VERY important that you format it exactly as described, ensuring the proper number of `=` and `-` (6 of each) and NO commas",
+        prompt: `The user is going to give you a list of descriptions. 
+                Each one is separated by '${DescriptionSeperator}' on either side. 
+                Descriptions will vary in length, so make sure to only separate when you see '${DescriptionSeperator}'. 
+                Sort them by the user's specifications. 
+                Make sure each description is only in the list once. Each item should be separated by '${DescriptionSeperator}'. 
+                Immediately afterward, surrounded by '${DocSeperator}' on BOTH SIDES, provide some insight into your reasoning for the way you sorted (and mention nothing about the formatting details given in this description). 
+                It is VERY important that you format it exactly as described, ensuring the proper number of '${DescriptionSeperator[0]}' and '${DocSeperator[0]}' (${DescriptionSeperator.length} of each) and NO commas`,
     },
     describe: { model: 'gpt-4-vision-preview', maxTokens: 2048, temp: 0, prompt: 'Describe these images in 3-5 words' },
     flashcard: {
@@ -145,20 +155,28 @@ const callTypeMap: { [type: string]: GPTCallOpts } = {
         model: 'gpt-4-turbo',
         maxTokens: 1024,
         temp: 0,
-        prompt: "I'm going to give you a list of descriptions. Each one is separated by `======` on either side. Descriptions will vary in length, so make sure to only separate when you see `======`. Based on the question the user asks, provide a subset of the given descriptions that best matches the user's specifications. Make sure each description is only in the list once. Each item should be separated by `======`. Immediately afterward, surrounded by `------` on BOTH SIDES, provide some insight into your reasoning in the 2nd person (and mention nothing about the formatting details given in this description). It is VERY important that you format it exactly as described, ensuring the proper number of `=` and `-` (6 of each) and no commas",
+        prompt: `I'm going to give you a list of descriptions.
+                Each one is separated by '${DescriptionSeperator}' on either side. 
+                Descriptions will vary in length, so make sure to only separate when you see '${DescriptionSeperator}'. 
+                Based on the question the user asks, provide a subset of the given descriptions that best matches the user's specifications. 
+                Make sure each description is only in the list once. Each item should be separated by '${DescriptionSeperator}'. 
+                Immediately afterward, surrounded by '${DocSeperator}' on BOTH SIDES, provide some insight into your reasoning in the 2nd person (and mention nothing about the formatting details given in this description). 
+                It is VERY important that you format it exactly as described, ensuring the proper number of '${DescriptionSeperator[0]}' and '${DocSeperator[0]}' (${DescriptionSeperator.length} of each) and NO commas`,
     },
 
     info: {
         model: 'gpt-4-turbo',
         maxTokens: 1024,
         temp: 0,
-        prompt: "Answer the user's question with a short (<100 word) response. If a particular document is selected I will provide that information (which may help with your response)",
+        prompt: `Answer the user's question with a short (<100 word) response. 
+                 If a particular document is selected I will provide that information (which may help with your response)`,
     },
     rubric: {
         model: 'gpt-4-turbo',
         maxTokens: 1024,
         temp: 0,
-        prompt: "BRIEFLY (<25 words) provide a definition for the following term. It will be used as a rubric to evaluate the user's understanding of the topic",
+        prompt: `BRIEFLY (<25 words) provide a definition for the following term. 
+                 It will be used as a rubric to evaluate the user's understanding of the topic`,
     },
 };
 let lastCall = '';
diff --git a/src/client/views/ViewBoxInterface.ts b/src/client/views/ViewBoxInterface.ts
index a66a20cf6..30da8c616 100644
--- a/src/client/views/ViewBoxInterface.ts
+++ b/src/client/views/ViewBoxInterface.ts
@@ -22,6 +22,7 @@ export abstract class ViewBoxInterface<P> extends ObservableReactComponent<React
         return ''; //
     }
     promoteCollection?: () => void; // moves contents of collection to parent
+    hasChildDocs?: () => Doc[];
     updateIcon?: (usePanelDimensions?: boolean) => Promise<void>; // updates the icon representation of the document
     getAnchor?: (addAsAnnotation: boolean, pinData?: PinProps) => Doc; // returns an Anchor Doc that represents the current state of the doc's componentview (e.g., the current playhead location of a an audio/video box)
     restoreView?: (viewSpec: Doc) => boolean; // DEPRECATED: do not use, it will go away.  see PresBox.restoreTargetDocView
diff --git a/src/client/views/collections/CollectionCardDeckView.tsx b/src/client/views/collections/CollectionCardDeckView.tsx
index e00aa65d7..d7f4251f3 100644
--- a/src/client/views/collections/CollectionCardDeckView.tsx
+++ b/src/client/views/collections/CollectionCardDeckView.tsx
@@ -1,22 +1,22 @@
-import { IReactionDisposer, ObservableMap, action, computed, makeObservable, observable, reaction, runInAction } from 'mobx';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import * as CSS from 'csstype';
+import { action, computed, IReactionDisposer, makeObservable, observable, ObservableMap, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import { computedFn } from 'mobx-utils';
 import * as React from 'react';
-import * as CSS from 'csstype';
-import { ClientUtils, imageUrlToBase64, returnFalse, returnNever, returnZero, setupMoveUpEvents } from '../../../ClientUtils';
+import { ClientUtils, returnFalse, returnNever, returnZero, setupMoveUpEvents } from '../../../ClientUtils';
 import { emptyFunction } from '../../../Utils';
 import { Doc, DocListCast, Opt } from '../../../fields/Doc';
-import { Animation, DocData } from '../../../fields/DocSymbols';
+import { Animation } from '../../../fields/DocSymbols';
 import { Id } from '../../../fields/FieldSymbols';
 import { List } from '../../../fields/List';
 import { ScriptField } from '../../../fields/ScriptField';
-import { BoolCast, DocCast, NumCast, RTFCast, ScriptCast, StrCast } from '../../../fields/Types';
-import { URLField } from '../../../fields/URLField';
-import { gptImageLabel, GPTTypeStyle } from '../../apis/gpt/GPT';
+import { BoolCast, DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
 import { DocumentType } from '../../documents/DocumentTypes';
 import { Docs } from '../../documents/Documents';
 import { DragManager } from '../../util/DragManager';
 import { dropActionType } from '../../util/DropActionTypes';
+import { SettingsManager } from '../../util/SettingsManager';
 import { SnappingManager } from '../../util/SnappingManager';
 import { Transform } from '../../util/Transform';
 import { undoable, UndoManager } from '../../util/UndoManager';
@@ -25,11 +25,8 @@ import { StyleProp } from '../StyleProp';
 import { TagItem } from '../TagsView';
 import { DocumentView, DocumentViewProps } from '../nodes/DocumentView';
 import { FocusViewOptions } from '../nodes/FocusViewOptions';
-import { GPTPopup } from '../pdf/GPTPopup/GPTPopup';
 import './CollectionCardDeckView.scss';
-import { CollectionSubView, docSortings, SubCollectionViewProps } from './CollectionSubView';
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { SettingsManager } from '../../util/SettingsManager';
+import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
 
 /**
  * New view type specifically for studying more dynamically. Allows you to reorder docs however you see fit, easily
@@ -42,7 +39,6 @@ import { SettingsManager } from '../../util/SettingsManager';
 export class CollectionCardView extends CollectionSubView() {
     private _dropDisposer?: DragManager.DragDropDisposer;
     private _disposers: { [key: string]: IReactionDisposer } = {};
-    private _textToDoc = new Map<string, Doc>();
     private _oldWheel: HTMLElement | null = null;
     private _dropped = false; // set when a card doc has just moved and the drop method has been called - prevents the pointerUp method from hiding doc decorations (which needs to be done when clicking on a card to animate it to front/center)
     private _setCurDocScript = () => ScriptField.MakeScript('scriptContext.layoutDoc._card_curDoc=this', { scriptContext: 'any' })!;
@@ -74,22 +70,7 @@ export class CollectionCardView extends CollectionSubView() {
         return Math.ceil(this.cardDeckWidth / this.cardWidth);
     }
 
-    /**
-     * update's gpt's doc-text list and initialize callbacks to respond to GPT output
-     */
-    updateDocumentDescriptions = () =>
-        this.childPairStringList().then(docDescriptions => {
-            GPTPopup.Instance.setDocumentDescriptions(docDescriptions.join());
-            GPTPopup.Instance.onGptResponse = this.processGptResponse;
-            GPTPopup.Instance.onQuizRandom = this.randomlyChooseDoc;
-        });
-
     componentDidMount() {
-        this._disposers.chatVis = reaction(
-            () => GPTPopup.Instance.Visible,
-            vis => !vis && this.onGptHide()
-        );
-        GPTPopup.Instance.setRetrieveDocDescriptionsCallback(this.Document, this.updateDocumentDescriptions);
         this._props.setContentViewBox?.(this);
         // if card deck moves, then the child doc views are hidden so their screen to local transforms will return empty rectangles
         // when inquired from the dom (below in childScreenToLocal).  When the doc is actually rendered, we need to act like the
@@ -110,10 +91,7 @@ export class CollectionCardView extends CollectionSubView() {
         );
     }
 
-    onGptHide = () => Doc.setDocFilter(this.Document, 'tags', '#chat', 'remove');
     componentWillUnmount() {
-        GPTPopup.Instance.onGptResponse = undefined;
-        GPTPopup.Instance.onQuizRandom = undefined;
         Object.keys(this._disposers).forEach(key => this._disposers[key]?.());
         this._dropDisposer?.();
     }
@@ -128,7 +106,7 @@ export class CollectionCardView extends CollectionSubView() {
      * Circle arc size, in radians, to layout cards
      */
     @computed get archAngle() {
-        return NumCast(this.layoutDoc.card_arch, 90) * (Math.PI / 180) * (this.childCards.length < this._maxRowCount ? this.childCards.length / this._maxRowCount : 1);
+        return NumCast(this.layoutDoc.card_arch, 90) * (Math.PI / 180) * (this.childDocsNoInk.length < this._maxRowCount ? this.childDocsNoInk.length / this._maxRowCount : 1);
     }
     /**
      * Spacing card rows as a percent of Doc size.   100 means rows spread out to fill 100% of the Doc vertically.  Default is 60%
@@ -140,7 +118,7 @@ export class CollectionCardView extends CollectionSubView() {
     /**
      * The child documents to be rendered-- everything other than ink/link docs (which are marks as being svg's)
      */
-    @computed get childCards() {
+    @computed get childDocsNoInk() {
         return this.childLayoutPairs.filter(pair => !pair.layout.layout_isSvg);
     }
 
@@ -148,7 +126,7 @@ export class CollectionCardView extends CollectionSubView() {
      * how much to scale down the contents of the view so that everything will fit
      */
     @computed get fitContentScale() {
-        const length = Math.min(this.childCards.length, this._maxRowCount);
+        const length = Math.min(this.childDocsNoInk.length, this._maxRowCount);
         return (this.childPanelWidth() * length) / this._props.PanelWidth();
     }
 
@@ -164,17 +142,12 @@ export class CollectionCardView extends CollectionSubView() {
         return this._props.PanelWidth() - 2 * this.xMargin;
     }
 
-    /**
-     * When in quiz mode, randomly selects a document
-     */
-    randomlyChooseDoc = () => (this.layoutDoc._card_curDoc = this.childDocs[Math.floor(Math.random() * this.childDocs.length)]);
-
     setHoveredNodeIndex = action((index: number) => {
         if (!SnappingManager.IsDragging) this._hoveredNodeIndex = index;
     });
 
     isSelected = (doc: Doc) => this._docRefs.get(doc)?.IsSelected;
-    childPanelWidth = () => NumCast(this.layoutDoc.childPanelWidth, Math.max(150, this._props.PanelWidth() / (this.childCards.length > this._maxRowCount ? this._maxRowCount : this.childCards.length) / this.nativeScaling));
+    childPanelWidth = () => NumCast(this.layoutDoc.childPanelWidth, Math.max(150, this._props.PanelWidth() / (this.childDocsNoInk.length > this._maxRowCount ? this._maxRowCount : this.childDocsNoInk.length) / this.nativeScaling));
     childPanelHeight = () => this._props.PanelHeight() * this.fitContentScale;
     onChildDoubleClick = () => ScriptCast(this.layoutDoc.onChildDoubleClick);
     isContentActive = () => this._props.isSelected() || this._props.isContentActive() || this.isAnyChildContentActive();
@@ -318,10 +291,10 @@ export class CollectionCardView extends CollectionSubView() {
      * @returns number of cards in row that contains index
      */
     cardsInRowThatIncludesCardIndex = (index: number) => {
-        if (this.childCards.length < this._maxRowCount) {
-            return this.childCards.length;
+        if (this.childDocsNoInk.length < this._maxRowCount) {
+            return this.childDocsNoInk.length;
         }
-        const totalCards = this.childCards.length;
+        const totalCards = this.childDocsNoInk.length;
         if (index < totalCards - (totalCards % this._maxRowCount)) {
             return this._maxRowCount;
         }
@@ -385,102 +358,6 @@ export class CollectionCardView extends CollectionSubView() {
             : this.translateY(index);
     };
 
-    /**
-     * A list of the text content of all the child docs. RTF documents will have just their text and pdf documents will have the first 50 words.
-     * Image documents are converted to bse64 and gpt generates a description for them. all other documents use their title. This string is
-     * inputted into the gpt prompt to sort everything together
-     * @returns
-     */
-    childPairStringList = () => {
-        const docToText = (doc: Doc) => {
-            switch (doc.type) {
-                case DocumentType.PDF:  return StrCast(doc.text).split(/\s+/).slice(0, 50).join(' '); // first 50 words of pdf text
-                case DocumentType.IMG:  return this.getImageDesc(doc);
-                case DocumentType.RTF:  return StrCast(RTFCast(doc.text).Text);
-                default:                return StrCast(doc.title);
-            } // prettier-ignore
-        };
-        const docTextPromises = this.childCards
-            .map(pair => pair.layout)
-            .map(async doc => {
-                const docText = (await docToText(doc)) ?? '';
-                doc.gptInputText = docText;
-                this._textToDoc.set(docText.replace(/\n/g, ' ').trim(), doc);
-                return `======${docText.replace(/\n/g, ' ').trim()}======`;
-            });
-        return Promise.all<string>(docTextPromises);
-    };
-
-    /**
-     * Calls the gpt API to generate descriptions for the images in the view
-     * @param image
-     * @returns
-     */
-    getImageDesc = async (image: Doc) => {
-        if (StrCast(image.description)) return StrCast(image.description); // Return existing description
-        const { href } = (image.data as URLField).url;
-        const hrefParts = href.split('.');
-        const hrefComplete = `${hrefParts[0]}_o.${hrefParts[1]}`;
-        try {
-            const hrefBase64 = await imageUrlToBase64(hrefComplete);
-            const response = await gptImageLabel(hrefBase64, 'Give three to five labels to describe this image.');
-            image[DocData].description = response.trim();
-            return response; // Return the response from gptImageLabel
-        } catch (error) {
-            console.log(error);
-        }
-        return '';
-    };
-
-    /**
-     * Processes gpt's output depending on the type of question the user asked. Converts gpt's string output to
-     * usable code
-     * @param gptOutput
-     * @param questionType
-     * @param tag
-     */
-    processGptResponse = (gptOutput: string, questionType: GPTTypeStyle, tag?: string) =>
-        undoable(() => {
-            // Split the string into individual list items
-            const listItems = gptOutput.split('======').filter(item => item.trim() !== '');
-
-            if (questionType === GPTTypeStyle.Filter) {
-                this.childDocs.forEach(d => {
-                    TagItem.removeTagFromDoc(d, '#chat');
-                });
-            }
-
-            if (questionType === GPTTypeStyle.SortDocs) {
-                this.Document[this._props.fieldKey + '_sort'] = docSortings.Chat;
-            }
-
-            listItems.forEach((item, index) => {
-                const normalizedItem = item.trim();
-                // find the corresponding Doc in the textToDoc map
-                const doc = this._textToDoc.get(normalizedItem);
-                if (doc) {
-                    switch (questionType) {
-                        case GPTTypeStyle.SortDocs:
-                            doc.chatIndex = index;
-                            break;
-                        case GPTTypeStyle.AssignTags:
-                            if (tag) {
-                                const hashTag = tag.startsWith('#') ? tag : '#' + tag[0].toLowerCase() + tag.slice(1);
-                                const filterTag = Doc.MyFilterHotKeys.map(key => StrCast(key.toolType)).find(key => key.includes(tag)) ?? hashTag;
-                                TagItem.addTagToDoc(doc, filterTag);
-                            }
-                            break;
-                        case GPTTypeStyle.Filter:
-                            TagItem.addTagToDoc(doc, '#chat');
-                            Doc.setDocFilter(this.Document, 'tags', '#chat', 'check');
-                            break;
-                    }
-                } else {
-                    console.warn(`No matching document found for item: ${normalizedItem}`);
-                }
-            });
-        }, '')();
-
     childScreenToLocal = computedFn((doc: Doc, index: number, isSelected: boolean) => () => {
         // need to explicitly trigger an invalidation since we're reading everything from the Dom
         this._forceChildXf;
@@ -620,7 +497,7 @@ export class CollectionCardView extends CollectionSubView() {
     curDoc = () => DocCast(this.layoutDoc._card_curDoc);
 
     render() {
-        const fitContentScale = this.childCards.length === 0 ? 1 : this.fitContentScale;
+        const fitContentScale = this.childDocsNoInk.length === 0 ? 1 : this.fitContentScale;
         return (
             <div
                 className="collectionCardView-outer"
@@ -652,7 +529,7 @@ export class CollectionCardView extends CollectionSubView() {
                     <div
                         className="collectionCardView-flashcardUI"
                         style={{
-                            pointerEvents: this.childCards.length === 0 ? undefined : 'none',
+                            pointerEvents: this.childDocsNoInk.length === 0 ? undefined : 'none',
                             height: `${100 / this.nativeScaling / fitContentScale}%`,
                             width: `${100 / this.nativeScaling / fitContentScale}%`,
                             transform: `scale(${this.nativeScaling * fitContentScale})`,
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 5e99bec39..954f9aa4c 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -121,6 +121,7 @@ export function CollectionSubView<X>() {
             return this.dataDoc[this._props.fieldKey]; // this used to be 'layoutDoc', but then template fields will get ignored since the template is not a proto of the layout.  hopefully nothing depending on the previous code.
         }
 
+        hasChildDocs = () => this.childLayoutPairs.map(pair => pair.layout);
         @computed get childLayoutPairs(): { layout: Doc; data: Doc }[] {
             const { Document, TemplateDataDocument } = this._props;
             const validPairs = this.childDocs
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 6f0833a22..a4900e9d7 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -89,6 +89,8 @@ export class CollectionView extends ViewBoxAnnotatableComponent<CollectionViewPr
         TraceMobx();
         if (type === undefined) return null;
         switch (type) {
+            default:
+            case CollectionViewType.Freeform:     return <CollectionFreeFormView    key="collview" {...props} />;
             case CollectionViewType.Schema:       return <CollectionSchemaView      key="collview" {...props} />;
             case CollectionViewType.Calendar:     return <CalendarBox               key="collview" {...props} />;
             case CollectionViewType.Docking:      return <CollectionDockingView     key="collview" {...props} />;
@@ -105,8 +107,6 @@ export class CollectionView extends ViewBoxAnnotatableComponent<CollectionViewPr
             case CollectionViewType.Time:         return <CollectionTimeView        key="collview" {...props} />;
             case CollectionViewType.Grid:         return <CollectionGridView        key="collview" {...props} />;
             case CollectionViewType.Card:         return <CollectionCardView        key="collview" {...props} />;
-            case CollectionViewType.Freeform:     
-            default:                              return <CollectionFreeFormView    key="collview" {...props} />;
         }
     };
 
diff --git a/src/client/views/nodes/chatbot/tools/CreateDocumentTool.ts b/src/client/views/nodes/chatbot/tools/CreateDocumentTool.ts
index 6dc36b0d1..284879a4a 100644
--- a/src/client/views/nodes/chatbot/tools/CreateDocumentTool.ts
+++ b/src/client/views/nodes/chatbot/tools/CreateDocumentTool.ts
@@ -263,79 +263,79 @@ const standardOptions = ['title', 'backgroundColor'];
  * Description of document options and data field for each type.
  */
 const documentTypesInfo: { [key in supportedDocTypes]: { options: string[]; dataDescription: string } } = {
-    [supportedDocTypes.comparison]: {
+    comparison: {
         options: [...standardOptions, 'fontColor', 'text_align'],
         dataDescription: 'an array of two documents of any kind that can be compared.',
     },
-    [supportedDocTypes.deck]: {
+    deck: {
         options: [...standardOptions, 'fontColor', 'text_align'],
         dataDescription: 'an array of flashcard docs',
     },
-    [supportedDocTypes.flashcard]: {
+    flashcard: {
         options: [...standardOptions, 'fontColor', 'text_align'],
         dataDescription: 'an array of two strings.  the first string contains a question, and the second string contains an answer',
     },
-    [supportedDocTypes.text]: {
+    text: {
         options: [...standardOptions, 'fontColor', 'text_align'],
         dataDescription: 'The text content of the document.',
     },
-    [supportedDocTypes.web]: {
+    web: {
         options: [],
         dataDescription: 'A URL to a webpage. Example: https://en.wikipedia.org/wiki/Brown_University',
     },
-    [supportedDocTypes.html]: {
+    html: {
         options: [],
         dataDescription: 'The HTML-formatted text content of the document.',
     },
-    [supportedDocTypes.equation]: {
+    equation: {
         options: [...standardOptions, 'fontColor'],
         dataDescription: 'The equation content represented as a MathML string.',
     },
-    [supportedDocTypes.functionplot]: {
+    functionplot: {
         options: [...standardOptions, 'function_definition'],
         dataDescription: 'The function definition(s) for plotting. Provide as a string or array of function definitions.',
     },
-    [supportedDocTypes.dataviz]: {
+    dataviz: {
         options: [...standardOptions, 'chartType'],
         dataDescription: 'A string of comma-separated values representing the CSV data.',
     },
-    [supportedDocTypes.notetaking]: {
+    notetaking: {
         options: standardOptions,
         dataDescription: 'An array of related text documents with small amounts of text.',
     },
-    [supportedDocTypes.rtf]: {
+    rtf: {
         options: standardOptions,
         dataDescription: 'The rich text content in RTF format.',
     },
-    [supportedDocTypes.image]: {
+    image: {
         options: standardOptions,
         dataDescription: `A url string that must end with '.png', '.jpeg', '.gif', or '.jpg'`,
     },
-    [supportedDocTypes.pdf]: {
+    pdf: {
         options: standardOptions,
         dataDescription: 'the pdf content as a PDF file url.',
     },
-    [supportedDocTypes.audio]: {
+    audio: {
         options: standardOptions,
         dataDescription: 'The audio content as a file url.',
     },
-    [supportedDocTypes.video]: {
+    video: {
         options: standardOptions,
         dataDescription: 'The video content as a file url.',
     },
-    [supportedDocTypes.message]: {
+    message: {
         options: standardOptions,
         dataDescription: 'The message content of the document.',
     },
-    [supportedDocTypes.diagram]: {
+    diagram: {
         options: standardOptions,
         dataDescription: 'diagram content as a text string in Mermaid format.',
     },
-    [supportedDocTypes.script]: {
+    script: {
         options: standardOptions,
         dataDescription: 'The compilable JavaScript code. Use this for creating scripts.',
     },
-    [supportedDocTypes.collection]: {
+    collection: {
         options: [...standardOptions, 'type_collection'],
         dataDescription: 'A collection of Docs represented as an array.',
     },
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 2e14fb1d9..6960247e9 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1063,9 +1063,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
 
     generateImage = async () => {
         GPTPopup.Instance?.setTextAnchor(this.getAnchor(false));
-        GPTPopup.Instance?.setImgTargetDoc(this.Document);
-        GPTPopup.Instance.addToCollection = this._props.addDocument;
-        GPTPopup.Instance.generateImage((this.dataDoc.text as RichTextField)?.Text);
+        GPTPopup.Instance.generateImage((this.dataDoc.text as RichTextField)?.Text, this.Document, this._props.addDocument);
     };
 
     breakupDictation = () => {
diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.tsx b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
index 0b1ee78e3..f09d786d0 100644
--- a/src/client/views/pdf/GPTPopup/GPTPopup.tsx
+++ b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
@@ -1,6 +1,6 @@
 import { Button, IconButton, Toggle, ToggleType, Type } from '@dash/components';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, makeObservable, observable } from 'mobx';
+import { action, makeObservable, observable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { CgClose, CgCornerUpLeft } from 'react-icons/cg';
@@ -10,12 +10,15 @@ import { ClientUtils } from '../../../../ClientUtils';
 import { Doc } from '../../../../fields/Doc';
 import { NumCast, StrCast } from '../../../../fields/Types';
 import { Networking } from '../../../Network';
-import { GPTCallType, GPTTypeStyle, gptAPICall, gptImageCall } from '../../../apis/gpt/GPT';
+import { DescriptionSeperator, DocSeperator, GPTCallType, GPTTypeStyle, gptAPICall, gptImageCall } from '../../../apis/gpt/GPT';
 import { DocUtils } from '../../../documents/DocUtils';
 import { Docs } from '../../../documents/Documents';
 import { SettingsManager } from '../../../util/SettingsManager';
 import { SnappingManager } from '../../../util/SnappingManager';
+import { undoable } from '../../../util/UndoManager';
 import { ObservableReactComponent } from '../../ObservableReactComponent';
+import { TagItem } from '../../TagsView';
+import { docSortings } from '../../collections/CollectionSubView';
 import { DocumentView } from '../../nodes/DocumentView';
 import { AnchorMenu } from '../AnchorMenu';
 import './GPTPopup.scss';
@@ -29,61 +32,71 @@ export enum GPTPopupMode {
     QUIZ_RESPONSE, // user definitions or explanations to be evaluated by GPT
 }
 
-export enum GPTQuizType {
-    CURRENT = 0,
-    CHOOSE = 1,
-    MULTIPLE = 2,
-}
-
 @observer
 export class GPTPopup extends ObservableReactComponent<object> {
     // eslint-disable-next-line no-use-before-define
     static Instance: GPTPopup;
-    private _retrieveDocDescriptions: (() => Promise<void>) | null = null;
     private _messagesEndRef: React.RefObject<HTMLDivElement>;
     private _correlatedColumns: string[] = [];
     private _dataChatPrompt: string | undefined = undefined;
     private _imgTargetDoc: Doc | undefined;
     private _textAnchor: Doc | undefined;
     private _dataJson: string = '';
-    private _documentDescriptions: string = '';
+    private _documentDescriptions: Promise<string> | undefined; // a cache of the descriptions of all docs in the selected collection. makes it more efficient when asking GPT multiple questions about the collection.
     private _sidebarFieldKey: string = '';
     private _textToSummarize: string = '';
     private _imageDescription: string = '';
+    private _textToDocMap = new Map<string, Doc>(); // when GPT answers with a doc's content, this helps us find the Doc
+    private _addToCollection: ((doc: Doc | Doc[], annotationKey?: string | undefined) => boolean) | undefined;
+
+    constructor(props: object) {
+        super(props);
+        makeObservable(this);
+        GPTPopup.Instance = this;
+        this._messagesEndRef = React.createRef();
+    }
 
+    public addDoc: ((doc: Doc | Doc[], sidebarKey?: string | undefined) => boolean) | undefined;
+    public createFilteredDoc: (axes?: string[]) => boolean = () => false;
     public setSidebarFieldKey = (id: string) => (this._sidebarFieldKey = id);
-    public setDocumentDescriptions = (t: string) => (this._documentDescriptions = t);
     public setImgTargetDoc = (anchor: Doc) => (this._imgTargetDoc = anchor);
     public setTextAnchor = (anchor: Doc) => (this._textAnchor = anchor);
-    public onGptResponse?: (sortResult: string, questionType: GPTTypeStyle, tag?: string) => void;
-    public onQuizRandom?: () => void;
     public setDataJson = (text: string) => {
         if (text === '') this._dataChatPrompt = '';
         this._dataJson = text;
     };
 
-    constructor(props: object) {
-        super(props);
-        makeObservable(this);
-        GPTPopup.Instance = this;
-        this._messagesEndRef = React.createRef();
+    componentDidUpdate() {
+        this._gptProcessing && this.setStopAnimatingResponse(false);
+    }
+    componentDidMount(): void {
+        reaction(
+            () => DocumentView.Selected().lastElement(),
+            selDoc => {
+                const hasChildDocs = selDoc?.ComponentView?.hasChildDocs;
+                if (hasChildDocs) {
+                    this._textToDocMap.clear();
+                    this.setCollectionContext(selDoc.Document);
+                    this.onGptResponse = (sortResult: string, questionType: GPTTypeStyle, tag?: string) => this.processGptResponse(selDoc, this._textToDocMap, sortResult, questionType, tag);
+                    this.onQuizRandom = () => this.randomlyChooseDoc(selDoc.Document, hasChildDocs());
+                    this._documentDescriptions = Promise.all(hasChildDocs().map(doc =>
+                            Doc.getDescription(doc).then(text => this._textToDocMap.set(text, doc) && `${DescriptionSeperator}${text}${DescriptionSeperator}`)
+                        )).then(docDescriptions => docDescriptions.join()); // prettier-ignore
+                }
+            },
+            { fireImmediately: true }
+        );
     }
-
-    componentDidUpdate = () => this._gptProcessing && this.setStopAnimatingResponse(false);
 
     @observable private _conversationArray: string[] = ['Hi! In this pop up, you can ask ChatGPT questions about your documents and filter / sort them. '];
     @observable private _chatEnabled: boolean = false;
     @action private setChatEnabled = (start: boolean) => (this._chatEnabled = start);
-    @observable public Visible: boolean = false;
-    @action public setVisible = (vis: boolean) => (this.Visible = vis);
     @observable private _gptProcessing: boolean = false;
-    @action public setGptProcessing = (loading: boolean) => (this._gptProcessing = loading);
+    @action private setGptProcessing = (loading: boolean) => (this._gptProcessing = loading);
     @observable private _responseText: string = '';
-    @action public setResponseText = (text: string) => (this._responseText = text);
+    @action private setResponseText = (text: string) => (this._responseText = text);
     @observable private _imgUrls: string[][] = [];
-    @action public setImgUrls = (imgs: string[][]) => (this._imgUrls = imgs);
-    @observable private _mode: GPTPopupMode = GPTPopupMode.SUMMARY;
-    @action public setMode = (mode: GPTPopupMode) => (this._mode = mode);
+    @action private setImgUrls = (imgs: string[][]) => (this._imgUrls = imgs);
     @observable private _collectionContext: Doc | undefined = undefined;
     @action setCollectionContext = (doc: Doc | undefined) => (this._collectionContext = doc);
     @observable private _userPrompt: string = '';
@@ -91,23 +104,68 @@ export class GPTPopup extends ObservableReactComponent<object> {
     @observable private _quizAnswer: string = '';
     @action setQuizAnswer = (e: React.ChangeEvent<HTMLInputElement>) => (this._quizAnswer = e.target.value);
     @observable private _stopAnimatingResponse: boolean = false;
-    @action public setStopAnimatingResponse = (done: boolean) => (this._stopAnimatingResponse = done);
+    @action private setStopAnimatingResponse = (done: boolean) => (this._stopAnimatingResponse = done);
+
+    @observable private _mode: GPTPopupMode = GPTPopupMode.SUMMARY;
+    @action public setMode = (mode: GPTPopupMode) => (this._mode = mode);
+    @observable public Visible: boolean = false;
+    @action public setVisible = (vis: boolean) => (this.Visible = vis);
+
+    onQuizRandom?: () => void;
+    onGptResponse?: (sortResult: string, questionType: GPTTypeStyle, tag?: string) => void;
+    questionTypeNumberToStyle = (questionType: string) => +questionType.split(' ')[0][0];
 
     /**
-     * sets callback needed to retrieve an updated description of the collection's documents
-     * @param collectionDoc - the collection doc context for the GPT prompts
-     * @param callback - function to retrieve document descriptions
+     * 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
      */
-    public setRetrieveDocDescriptionsCallback(collectionDoc: Doc | undefined, callback: null | (() => Promise<void>)) {
-        this.setCollectionContext(collectionDoc);
-        this._retrieveDocDescriptions = callback;
-    }
+    processGptResponse = (docView: DocumentView, textToDocMap: Map<string, Doc>, gptOutput: string, questionType: GPTTypeStyle, tag?: string) =>
+        undoable(() => {
+            // Split the string into individual list items
+            const listItems = gptOutput.split('======').filter(item => item.trim() !== '');
 
-    public addDoc: (doc: Doc | Doc[], sidebarKey?: string | undefined) => boolean = () => false;
-    public createFilteredDoc: (axes?: string[]) => boolean = () => false;
-    public addToCollection: ((doc: Doc | Doc[], annotationKey?: string | undefined) => boolean) | undefined;
-    public questionTypeNumberToStyle = (questionType: string) => +questionType.split(' ')[0][0];
+            if (questionType === GPTTypeStyle.Filter) {
+                docView.ComponentView?.hasChildDocs?.().forEach(d => TagItem.removeTagFromDoc(d, '#chat'));
+            }
+
+            if (questionType === GPTTypeStyle.SortDocs) {
+                docView.Document[docView.ComponentView?.fieldKey + '_sort'] = docSortings.Chat;
+            }
 
+            listItems.forEach((item, index) => {
+                const normalizedItem = item.replace(/\n/g, ' ').trim();
+                // find the corresponding Doc in the textToDoc map
+                const doc = textToDocMap.get(normalizedItem);
+                if (doc) {
+                    switch (questionType) {
+                        case GPTTypeStyle.SortDocs:
+                            doc.chatIndex = index;
+                            break;
+                        case GPTTypeStyle.AssignTags:
+                            if (tag) {
+                                const hashTag = tag.startsWith('#') ? tag : '#' + tag[0].toLowerCase() + tag.slice(1);
+                                const filterTag = Doc.MyFilterHotKeys.map(key => StrCast(key.toolType)).find(key => key.includes(tag)) ?? hashTag;
+                                TagItem.addTagToDoc(doc, filterTag);
+                            }
+                            break;
+                        case GPTTypeStyle.Filter:
+                            TagItem.addTagToDoc(doc, '#chat');
+                            Doc.setDocFilter(docView.Document, 'tags', '#chat', 'check');
+                            break;
+                    }
+                } else {
+                    console.warn(`No matching document found for item: ${normalizedItem}`);
+                }
+            });
+        }, '')();
+
+    /**
+     * When in quiz mode, randomly selects a document
+     */
+    randomlyChooseDoc = (doc: Doc, childDocs: Doc[]) => DocumentView.getDocumentView(childDocs[Math.floor(Math.random() * childDocs.length)])?.select(false);
     /**
      * Generates a rubric for evaluating the user's description of the document's text
      * @param doc the doc the user is providing info about
@@ -116,9 +174,11 @@ export class GPTPopup extends ObservableReactComponent<object> {
     generateRubric = (doc: Doc) =>
         StrCast(doc.gptRubric)
             ? Promise.resolve(StrCast(doc.gptRubric))
-            : gptAPICall(StrCast(doc.gptInputText), GPTCallType.RUBRIC)
-                  .then(res => (doc.gptRubric = res))
-                  .catch(err => console.error('GPT call failed', err));
+            : Doc.getDescription(doc).then(desc =>
+                  gptAPICall(desc, GPTCallType.RUBRIC)
+                      .then(res => (doc.gptRubric = res))
+                      .catch(err => console.error('GPT call failed', err))
+              );
 
     /**
      * When the cards are in quiz mode in the card view, allows gpt to determine whether the user's answer was correct
@@ -128,17 +188,18 @@ export class GPTPopup extends ObservableReactComponent<object> {
      */
     generateQuizAnswerAnalysis = (doc: Doc, quizAnswer: string) =>
         this.generateRubric(doc).then(() =>
-            gptAPICall(
-                `Question: ${StrCast(doc.gptInputText)}; 
-                 UserAnswer: ${quizAnswer};
-                 Rubric: ${StrCast(doc.gptRubric)}`,
-                GPTCallType.QUIZ
-            ).then(res => {
-                this._conversationArray.push(res || 'GPT provided no answer');
-                this.onQuizRandom?.();
-            })
-            .catch(err => console.error('GPT call failed', err))
-        ) // prettier-ignore
+            Doc.getDescription(doc).then(desc => 
+                gptAPICall(
+                    `Question: ${desc}; 
+                    UserAnswer: ${quizAnswer};
+                    Rubric: ${StrCast(doc.gptRubric)}`,
+                    GPTCallType.QUIZ
+                ).then(res => {
+                    this._conversationArray.push(res || 'GPT provided no answer');
+                    this.onQuizRandom?.();
+                })
+                .catch(err => console.error('GPT call failed', err))
+        )) // prettier-ignore
 
     /**
      * Generates a response to the user's question about the docs in the collection.
@@ -146,32 +207,33 @@ export class GPTPopup extends ObservableReactComponent<object> {
      * @param userPrompt the user's input that chat will respond to
      */
     generateUserPromptResponse = (userPrompt: string) =>
-        (this._retrieveDocDescriptions ?? Promise.resolve)().then(() =>
-            gptAPICall(userPrompt, GPTCallType.TYPE).then(questionType =>
-                (() => {
-                    switch (this.questionTypeNumberToStyle(questionType)) {
-                        case GPTTypeStyle.AssignTags:
-                        case GPTTypeStyle.Filter:    return gptAPICall(this._documentDescriptions, GPTCallType.SUBSET, userPrompt);
-                        case GPTTypeStyle.SortDocs:  return gptAPICall(this._documentDescriptions, GPTCallType.SORT, userPrompt);
-                        default:                     return gptAPICall(StrCast(DocumentView.SelectedDocs().lastElement()?.gptInputText), GPTCallType.INFO, userPrompt);
-                    } // prettier-ignore
-                })().then(
-                    action(res => {
-                        // Trigger the callback with the result
-                        this.onGptResponse?.(res || 'Something went wrong :(', this.questionTypeNumberToStyle(questionType), questionType.split(' ').slice(1).join(' '));
-                        this._conversationArray.push(
-                            ![GPTTypeStyle.GeneralInfo, GPTTypeStyle.DocInfo].includes(this.questionTypeNumberToStyle(questionType))? 
-                            // Extract explanation surrounded by ------ at the top or both at the top and bottom
-                            (res.match(/------\s*([\s\S]*?)\s*(?:------|$)/) ?? [])[1]?.trim() ?? 'No explanation found' : res);
-                    })
-                ).catch(err => console.log(err))
+        gptAPICall(userPrompt, GPTCallType.TYPE).then(questionType =>
+            (async () => {
+                switch (this.questionTypeNumberToStyle(questionType)) {
+                    case GPTTypeStyle.AssignTags:
+                    case GPTTypeStyle.Filter:    return this._documentDescriptions?.then(descs => gptAPICall(descs, GPTCallType.SUBSET, userPrompt)) ?? "";
+                    case GPTTypeStyle.SortDocs:  return this._documentDescriptions?.then(descs => gptAPICall(descs, GPTCallType.SORT, userPrompt)) ?? "";
+                    default:                     return Doc.getDescription(DocumentView.SelectedDocs().lastElement()).then(desc => gptAPICall(desc, GPTCallType.INFO, userPrompt));
+                } // prettier-ignore
+            })().then(
+                action(res => {
+                    // Trigger the callback with the result
+                    this.onGptResponse?.(res || 'Something went wrong :(', this.questionTypeNumberToStyle(questionType), questionType.split(' ').slice(1).join(' '));
+                    this._conversationArray.push(
+                        [GPTTypeStyle.GeneralInfo, GPTTypeStyle.DocInfo].includes(this.questionTypeNumberToStyle(questionType)) ? res:
+                        // Extract explanation surrounded by the DocSeperator string (defined in GPT.ts) at the top or both at the top and bottom
+                        (res.match(new RegExp(`${DocSeperator}\\s*([\\s\\S]*?)\\s*(?:${DocSeperator}|$)`)) ?? [])[1]?.trim() ?? 'No explanation found'
+                    );
+                })
             ).catch(err => console.log(err))
-        ); // prettier-ignore
+        ).catch(err => console.log(err)); // prettier-ignore
 
     /**
      * Generates a Dalle image and uploads it to the server.
      */
-    generateImage = (imgDesc: string) => {
+    generateImage = (imgDesc: string, imgTarget: Doc, addToCollection?: (doc: Doc | Doc[], annotationKey?: string | undefined) => boolean) => {
+        this._imgTargetDoc = imgTarget;
+        this.addDoc = addToCollection;
         this.setImgUrls([]);
         this.setMode(GPTPopupMode.IMAGE);
         this.setVisible(true);
@@ -237,7 +299,7 @@ export class GPTPopup extends ObservableReactComponent<object> {
             _layout_fitWidth: true,
             _layout_autoHeight: true,
         });
-        this.addDoc(newDoc, this._sidebarFieldKey);
+        this.addDoc?.(newDoc, this._sidebarFieldKey);
         const anchor = AnchorMenu.Instance?.GetAnchor(undefined, false);
         if (anchor) {
             DocUtils.MakeLink(newDoc, anchor, {
@@ -270,7 +332,7 @@ export class GPTPopup extends ObservableReactComponent<object> {
                 newDoc.overlayY = NumCast(textAnchor.y) + NumCast(textAnchor._height);
                 Doc.AddToMyOverlay(newDoc);
             } else {
-                this.addToCollection?.(newDoc);
+                this.addDoc?.(newDoc);
             }
             // Create link between prompt and image
             DocUtils.MakeLink(textAnchor, newDoc, { link_relationship: 'Image Prompt' });
@@ -282,8 +344,8 @@ export class GPTPopup extends ObservableReactComponent<object> {
     gptMenu = () => (
         <div className="btns-wrapper-gpt">
             <Button
-                tooltip="Have ChatGPT sort, tag, define, or filter your documents for you!"
-                text="Modify/Sort Cards!"
+                tooltip="Ask GPT to sort, tag, define, or filter your Docs!"
+                text="Ask GPT"
                 onClick={() => this.setMode(GPTPopupMode.USER_PROMPT)}
                 color={StrCast(Doc.UserDoc().userVariantColor)}
                 type={Type.TERT}
@@ -297,8 +359,8 @@ export class GPTPopup extends ObservableReactComponent<object> {
                 }}
             />
             <Button
-                tooltip="Test your knowledge with ChatGPT!"
-                text="Quiz Cards!"
+                tooltip="Test your knowledge by verifying answers with ChatGPT"
+                text="Take Quiz"
                 onClick={() => {
                     this._conversationArray = ['Define the selected card!'];
                     this.setMode(GPTPopupMode.QUIZ_RESPONSE);
@@ -386,7 +448,14 @@ export class GPTPopup extends ObservableReactComponent<object> {
                     </div>
                 ))}
             </div>
-            {this._gptProcessing ? null : <IconButton tooltip="Generate Again" onClick={() => this.generateImage(this._imageDescription)} icon={<FontAwesomeIcon icon="redo-alt" size="lg" />} color={StrCast(Doc.UserDoc().userVariantColor)} />}
+            {this._gptProcessing ? null : (
+                <IconButton
+                    tooltip="Generate Again"
+                    onClick={() => this._imgTargetDoc && this.generateImage(this._imageDescription, this._imgTargetDoc, this._addToCollection)}
+                    icon={<FontAwesomeIcon icon="redo-alt" size="lg" />}
+                    color={StrCast(Doc.UserDoc().userVariantColor)}
+                />
+            )}
         </div>
     );
 
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index e62ca4bb8..950d9047c 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -7,7 +7,7 @@ import { CollectionViewType, DocumentType } from '../client/documents/DocumentTy
 import { scriptingGlobal, ScriptingGlobals } from '../client/util/ScriptingGlobals';
 import { afterDocDeserialize, autoObject, Deserializable, SerializationHelper } from '../client/util/SerializationHelper';
 import { undoable, UndoManager } from '../client/util/UndoManager';
-import { ClientUtils, incrementTitleCopy } from '../ClientUtils';
+import { ClientUtils, imageUrlToBase64, incrementTitleCopy } from '../ClientUtils';
 import {
     AclAdmin, AclAugment, AclEdit, AclPrivate, AclReadonly, Animation, AudioPlay, Brushed, CachedUpdates, DirectLinks,
     DocAcl, DocCss, DocData, DocLayout, DocViews, FieldKeys, FieldTuples, ForceServerWrite, Height, Highlight,
@@ -22,8 +22,9 @@ import { FieldId, RefField } from './RefField';
 import { RichTextField } from './RichTextField';
 import { listSpec } from './Schema';
 import { ComputedField, ScriptField } from './ScriptField';
-import { BoolCast, Cast, DocCast, FieldValue, NumCast, StrCast, ToConstructor, toList } from './Types';
+import { BoolCast, Cast, DocCast, FieldValue, ImageCastWithSuffix, NumCast, RTFCast, StrCast, ToConstructor, toList } from './Types';
 import { containedFieldChangedHandler, deleteProperty, GetEffectiveAcl, getField, getter, makeEditable, makeReadOnly, setter, SharingPermissions } from './util';
+import { gptImageLabel } from '../client/apis/gpt/GPT';
 
 export let ObjGetRefField: (id: string, force?: boolean) => Promise<Doc | undefined>;
 export let ObjGetRefFields: (ids: string[]) => Promise<Map<string, Doc | undefined>>;
@@ -1467,6 +1468,25 @@ export namespace Doc {
         });
     }
 
+    /**
+     * text description of a Doc. RTF documents will have just their text and pdf documents will have the first 50 words.
+     * Image documents are converted to bse64 and gpt generates a description for them. all other documents use their title.
+     * @param doc
+     * @returns
+     */
+    export function getDescription(doc: Doc) {
+        const curDescription = StrCast(doc[DocData][Doc.LayoutFieldKey(doc) + '_description']);
+        const docText = (async (tdoc:Doc) => {
+            switch (tdoc.type) {
+                case DocumentType.PDF: return curDescription || StrCast(tdoc.text).split(/\s+/).slice(0, 50).join(' '); // first 50 words of pdf text
+                case DocumentType.IMG: return curDescription || imageUrlToBase64(ImageCastWithSuffix(Doc.LayoutField(tdoc), '_o') ?? '')
+                                                .then(hrefBase64 => gptImageLabel(hrefBase64, 'Give three to five labels to describe this image.'));
+                case DocumentType.RTF: return RTFCast(tdoc[Doc.LayoutFieldKey(tdoc)]).Text;
+                default:               return StrCast(tdoc.title);
+        }}); // prettier-ignore
+        return docText(doc).then(text => (doc[DocData][Doc.LayoutFieldKey(doc) + '_description'] = text));
+    }
+
     // prettier-ignore
     export function toIcon(doc?: Doc, isOpen?: Opt<boolean>) {
         if (isOpen) return doc?.isFolder ? 'chevron-down' : 'folder-open';
diff --git a/src/fields/Types.ts b/src/fields/Types.ts
index e19673665..474882959 100644
--- a/src/fields/Types.ts
+++ b/src/fields/Types.ts
@@ -134,6 +134,10 @@ export function PDFCast(field: FieldResult, defaultVal: PdfField | null = null)
 export function ImageCast(field: FieldResult, defaultVal: ImageField | null = null) {
     return Cast(field, ImageField, defaultVal);
 }
+export function ImageCastWithSuffix(field: FieldResult, suffix: string, defaultVal: ImageField | null = null) {
+    const href = ImageCast(field, defaultVal)?.url.href;
+    return href ? `${href.split('.')[0]}${suffix}.${href.split('.')[1]}` : null;
+}
 
 export function FieldValue<T extends FieldType, U extends WithoutList<T>>(field: FieldResult<T>, defaultValue: U): WithoutList<T>;
 // eslint-disable-next-line no-redeclare
-- 
cgit v1.2.3-70-g09d2


From 5505010e14bede33a692a656515bd6ca106103f2 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 27 Feb 2025 11:06:19 -0500
Subject: added x and y margins for card view.

---
 src/client/views/PropertiesView.tsx                      |  2 +-
 src/client/views/collections/CollectionCardDeckView.scss |  7 +++++++
 src/client/views/collections/CollectionCardDeckView.tsx  | 13 ++++++++++---
 3 files changed, 18 insertions(+), 4 deletions(-)

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

diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index bed96f600..8e043baee 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -152,7 +152,7 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
         return this.selectedDoc?.isGroup;
     }
     @computed get isStack() {
-        return [CollectionViewType.Masonry, CollectionViewType.Multicolumn, CollectionViewType.Multirow, CollectionViewType.Stacking, CollectionViewType.NoteTaking, CollectionViewType.Carousel].includes(
+        return [CollectionViewType.Masonry, CollectionViewType.Multicolumn, CollectionViewType.Multirow, CollectionViewType.Stacking, CollectionViewType.NoteTaking, CollectionViewType.Card, CollectionViewType.Carousel].includes(
             this.selectedDoc?.type_collection as CollectionViewType
         );
     }
diff --git a/src/client/views/collections/CollectionCardDeckView.scss b/src/client/views/collections/CollectionCardDeckView.scss
index 06dd4627f..e6cc398af 100644
--- a/src/client/views/collections/CollectionCardDeckView.scss
+++ b/src/client/views/collections/CollectionCardDeckView.scss
@@ -9,6 +9,7 @@
         display: flex;
         transform-origin: top left;
         align-items: center;
+        position: relative;
     }
 
     button {
@@ -33,6 +34,12 @@
 .collectionCardView-cardSizeDragger {
     position: absolute;
     top: 0;
+    width: 28px;
+    height: 28px;
+    > svg {
+        width: 100%;
+        height: 100%;
+    }
 }
 
 .no-card-span {
diff --git a/src/client/views/collections/CollectionCardDeckView.tsx b/src/client/views/collections/CollectionCardDeckView.tsx
index d7f4251f3..94f85d429 100644
--- a/src/client/views/collections/CollectionCardDeckView.tsx
+++ b/src/client/views/collections/CollectionCardDeckView.tsx
@@ -127,7 +127,7 @@ export class CollectionCardView extends CollectionSubView() {
      */
     @computed get fitContentScale() {
         const length = Math.min(this.childDocsNoInk.length, this._maxRowCount);
-        return (this.childPanelWidth() * length) / this._props.PanelWidth();
+        return (this.childPanelWidth() * length) / (this._props.PanelWidth() - 2 * this.xMargin);
     }
 
     @computed get nativeScaling() {
@@ -138,6 +138,10 @@ export class CollectionCardView extends CollectionSubView() {
         return NumCast(this.layoutDoc._xMargin, Math.max(3, 0.05 * this._props.PanelWidth()));
     }
 
+    @computed get yMargin() {
+        return this._props.yPadding || NumCast(this.layoutDoc._yMargin, Math.min(5, 0.05 * this._props.PanelWidth()));
+    }
+
     @computed get cardDeckWidth() {
         return this._props.PanelWidth() - 2 * this.xMargin;
     }
@@ -403,8 +407,8 @@ export class CollectionCardView extends CollectionSubView() {
         setupMoveUpEvents(
             this,
             e,
-            (emove: PointerEvent, down: number[], delta: number[]) => {
-                this.layoutDoc._cardWidth = Math.max(10, delta[0] < 0 ? Math.floor(this.cardWidth + delta[0]) : Math.ceil(this.cardWidth + delta[0]));
+            (emove: PointerEvent) => {
+                this.layoutDoc._cardWidth = Math.max(10, this.ScreenToLocalBoxXf().transformPoint(emove.clientX, 0)[0] - this.xMargin);
                 return false;
             },
             action(() => {
@@ -509,6 +513,8 @@ export class CollectionCardView extends CollectionSubView() {
                 style={{
                     background: this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BackgroundColor) as string,
                     color: this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Color) as string,
+                    paddingLeft: this.xMargin,
+                    paddingRight: this.xMargin,
                 }}>
                 <div
                     className="collectionCardView-inner"
@@ -516,6 +522,7 @@ export class CollectionCardView extends CollectionSubView() {
                         transform: `scale(${1 / fitContentScale})`,
                         height: `${100 * fitContentScale}%`,
                         width: `${100 * fitContentScale}%`,
+                        top: this.yMargin,
                     }}>
                     <div
                         className="collectionCardView-cardwrapper"
-- 
cgit v1.2.3-70-g09d2


From bcf3b005d55ec851374049f4c188d96ffc7a7c8b Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 27 Feb 2025 14:46:20 -0500
Subject: prevent tagsview from appeqring in spreadsheet.  set default fill
 color only if nothing is selected.  place column divider at top of
 stacking/card views.  move card view flashcard view to top left independent
 of margin settings.

---
 .../views/collections/CollectionCardDeckView.tsx   |   5 +-
 .../collections/CollectionMasonryViewFieldRow.tsx  |   1 -
 .../views/collections/CollectionStackingView.scss  | 194 ++++++++++-----------
 .../views/collections/CollectionStackingView.tsx   |  45 ++---
 src/client/views/global/globalScripts.ts           |   2 +-
 src/client/views/nodes/DocumentView.tsx            |   2 +-
 6 files changed, 125 insertions(+), 124 deletions(-)

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

diff --git a/src/client/views/collections/CollectionCardDeckView.tsx b/src/client/views/collections/CollectionCardDeckView.tsx
index 94f85d429..756b37f99 100644
--- a/src/client/views/collections/CollectionCardDeckView.tsx
+++ b/src/client/views/collections/CollectionCardDeckView.tsx
@@ -540,11 +540,10 @@ export class CollectionCardView extends CollectionSubView() {
                             height: `${100 / this.nativeScaling / fitContentScale}%`,
                             width: `${100 / this.nativeScaling / fitContentScale}%`,
                             transform: `scale(${this.nativeScaling * fitContentScale})`,
-                        }}>
-                        {this.flashCardUI(this.curDoc, this.docViewProps, this.answered)}
-                    </div>
+                        }}></div>
                 </div>
 
+                {this.flashCardUI(this.curDoc, this.docViewProps, this.answered)}
                 <div
                     className="collectionCardView-cardSizeDragger"
                     onPointerDown={this.cardSizerDown}
diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
index 710c00841..a0d5f9f70 100644
--- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
+++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
@@ -258,7 +258,6 @@ export class CollectionMasonryViewFieldRow extends ObservableReactComponent<CMVF
         const stackPad = showChrome ? `0px ${this._props.parent.xMargin}px` : `${this._props.parent.yMargin}px ${this._props.parent.xMargin}px 0px ${this._props.parent.xMargin}px `;
         return this.collapsed ? null : (
             <div style={{ position: 'relative' }}>
-                {this._props.showHandle && this._props.parent._props.isContentActive() ? this._props.parent.columnDragger : null}
                 {showChrome ? (
                     <div
                         className="collectionStackingView-addDocumentButton"
diff --git a/src/client/views/collections/CollectionStackingView.scss b/src/client/views/collections/CollectionStackingView.scss
index 565536623..05ac52ff9 100644
--- a/src/client/views/collections/CollectionStackingView.scss
+++ b/src/client/views/collections/CollectionStackingView.scss
@@ -12,123 +12,123 @@
     position: relative;
     height: 100%;
     width: 100%;
-}
-
-// TODO:glr Turn this into a seperate class
-.documentButtonMenu {
-    position: relative;
-    height: fit-content;
-    border-bottom: global.$standard-border;
-    display: flex;
-    justify-content: center;
-    flex-direction: column;
-    align-items: center;
-    align-content: center;
-    padding: 5px 0 5px 0;
 
-    .documentExplanation {
-        width: 90%;
-        margin: 5px;
-        font-size: 11px;
-        color: global.$medium-blue;
-        padding: 10px;
-        border-radius: 5px;
-        border: solid 0.5px global.$medium-blue;
+    .collectionStackingView-columnDragger {
+        width: 28;
+        height: 28;
+        position: absolute;
+        margin-left: -5;
+        z-index: 10;
+        > svg {
+            width: 100%;
+            height: 100%;
+        }
     }
-}
 
-.collectionStackingView,
-.collectionMasonryView {
-    height: 100%;
-    width: 100%;
-    position: absolute;
-    top: 0;
-    overflow-y: auto;
-    overflow-x: hidden;
-    flex-wrap: wrap;
-    transition: top 0.5s;
-
-    > div {
+    // TODO:glr Turn this into a seperate class
+    .documentButtonMenu {
         position: relative;
-        display: block;
-    }
-
-    .collectionStackingViewFieldColumn {
+        height: fit-content;
+        border-bottom: global.$standard-border;
         display: flex;
+        justify-content: center;
         flex-direction: column;
+        align-items: center;
+        align-content: center;
+        padding: 5px 0 5px 0;
+
+        .documentExplanation {
+            width: 90%;
+            margin: 5px;
+            font-size: 11px;
+            color: global.$medium-blue;
+            padding: 10px;
+            border-radius: 5px;
+            border: solid 0.5px global.$medium-blue;
+        }
     }
 
-    .collectionSchemaView-previewDoc {
+    .collectionStackingView,
+    .collectionMasonryView {
         height: 100%;
+        width: 100%;
         position: absolute;
-    }
+        top: 0;
+        overflow-y: auto;
+        overflow-x: hidden;
+        flex-wrap: wrap;
+        transition: top 0.5s;
 
-    .collectionStackingView-docView-container {
-        width: 45%;
-        margin: 5% 2.5%;
-        padding-left: 2.5%;
-        height: auto;
-    }
+        > div {
+            position: relative;
+            display: block;
+        }
 
-    .collectionStackingView-flexCont {
-        display: flex;
-        flex-direction: row;
-        flex-wrap: wrap;
-        align-items: center;
-    }
+        .collectionStackingViewFieldColumn {
+            display: flex;
+            flex-direction: column;
+        }
 
-    .collectionStackingView-masonrySingle,
-    .collectionStackingView-masonryGrid {
-        width: 100%;
-        display: grid;
-        top: 0;
-        left: 0;
-    }
+        .collectionSchemaView-previewDoc {
+            height: 100%;
+            position: absolute;
+        }
 
-    .collectionStackingView-masonrySingle {
-        height: 100%;
-        position: absolute;
-    }
+        .collectionStackingView-docView-container {
+            width: 45%;
+            margin: 5% 2.5%;
+            padding-left: 2.5%;
+            height: auto;
+        }
 
-    .collectionStackingView-masonryGrid {
-        margin: auto;
-        height: max-content;
-        position: relative;
-        grid-auto-rows: 0px;
-    }
+        .collectionStackingView-flexCont {
+            display: flex;
+            flex-direction: row;
+            flex-wrap: wrap;
+            align-items: center;
+        }
 
-    .collectionStackingView-masonrySingle {
-        width: 100%;
-        height: 100%;
-        position: absolute;
-        display: flex;
-        flex-direction: column;
-        top: 0;
-        left: 0;
-        width: 100%;
-        position: absolute;
-    }
+        .collectionStackingView-masonrySingle,
+        .collectionStackingView-masonryGrid {
+            width: 100%;
+            display: grid;
+            top: 0;
+            left: 0;
+        }
 
-    .collectionStackingView-description {
-        font-size: 100%;
-        margin-bottom: 1vw;
-        padding: 10px;
-        height: 2vw;
-        width: 100%;
-        font-family: global.$sans-serif;
-        background: global.$dark-gray;
-        color: global.$white;
-    }
+        .collectionStackingView-masonrySingle {
+            height: 100%;
+            position: absolute;
+        }
 
-    .collectionStackingView-columnDragger {
-        width: 28;
-        height: 28;
-        position: absolute;
-        margin-left: -5;
-        z-index: 10;
-        > svg {
+        .collectionStackingView-masonryGrid {
+            margin: auto;
+            height: max-content;
+            position: relative;
+            grid-auto-rows: 0px;
+        }
+
+        .collectionStackingView-masonrySingle {
             width: 100%;
             height: 100%;
+            position: absolute;
+            display: flex;
+            flex-direction: column;
+            top: 0;
+            left: 0;
+            width: 100%;
+            position: absolute;
+        }
+
+        .collectionStackingView-description {
+            font-size: 100%;
+            margin-bottom: 1vw;
+            padding: 10px;
+            height: 2vw;
+            width: 100%;
+            font-family: global.$sans-serif;
+            background: global.$dark-gray;
+            color: global.$white;
         }
     }
 
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 0a25882ff..e8fa8b01e 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -452,7 +452,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
     };
     @action
     onDividerMove = (e: PointerEvent) => {
-        this.Document._layout_columnWidth = Math.max(10, this.ScreenToLocalBoxXf().transformPoint(e.clientX, 0)[0] - this.xMargin);
+        this.Document._layout_columnWidth = Math.max(10, (this._props.DocumentView?.().screenToViewTransform().transformPoint(e.clientX, 0)[0] ?? 0) - this.xMargin);
         return false;
     };
 
@@ -606,26 +606,29 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
         }
         const rows = () => (!this.isStackingView ? 1 : Math.max(1, Math.min(docList.length, Math.floor((this._props.PanelWidth() - 2 * this.xMargin) / (this.columnWidth + this.gridGap)))));
         return (
-            <div style={{ top: this.yMargin }}>
-                <CollectionMasonryViewFieldRow
-                    showHandle={first}
-                    Document={this.Document}
-                    chromeHidden={this.chromeHidden}
-                    pivotField={this.pivotField}
-                    refList={this._refList}
-                    key={heading ? heading.heading : ''}
-                    rows={rows}
-                    headings={this.headings}
-                    heading={heading ? heading.heading : ''}
-                    headingObject={heading}
-                    docList={docList}
-                    parent={this}
-                    type={type}
-                    createDropTarget={this.createDashEventsTarget}
-                    screenToLocalTransform={this.ScreenToLocalBoxXf}
-                    setDocHeight={this.setDocHeight}
-                />
-            </div>
+            <>
+                {this._props.isContentActive() && !this.isStackingView ? this.columnDragger : null}
+                <div style={{ top: this.yMargin }}>
+                    <CollectionMasonryViewFieldRow
+                        showHandle={first}
+                        Document={this.Document}
+                        chromeHidden={this.chromeHidden}
+                        pivotField={this.pivotField}
+                        refList={this._refList}
+                        key={heading ? heading.heading : ''}
+                        rows={rows}
+                        headings={this.headings}
+                        heading={heading ? heading.heading : ''}
+                        headingObject={heading}
+                        docList={docList}
+                        parent={this}
+                        type={type}
+                        createDropTarget={this.createDashEventsTarget}
+                        screenToLocalTransform={this.ScreenToLocalBoxXf}
+                        setDocHeight={this.setDocHeight}
+                    />
+                </div>
+            </>
         );
     };
 
diff --git a/src/client/views/global/globalScripts.ts b/src/client/views/global/globalScripts.ts
index b455f76e4..835c28daa 100644
--- a/src/client/views/global/globalScripts.ts
+++ b/src/client/views/global/globalScripts.ts
@@ -127,7 +127,7 @@ ScriptingGlobals.add(function setBackgroundColor(color?: string, checkResult?: b
             const contentFrameNumber = Cast(selView.Document?._currentFrame, 'number', layoutFrameNumber ?? null); // frame number that content is at which determines what content is displayed
             return CollectionFreeFormDocumentView.getStringValues(selView?.Document, contentFrameNumber)[fieldKey] || defaultFill();
         }
-        setDefaultFill(color ?? 'transparent');
+        !selectedViews.length && setDefaultFill(color ?? 'transparent');
         selectedViews.forEach(dv => {
             const fieldKey = dv.Document._layout_isSvg ? 'fillColor' : 'backgroundColor';
             const layoutFrameNumber = Cast(dv.containerViewPath?.().lastElement()?.Document?._currentFrame, 'number'); // frame number that container is at which determines layout frame values
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index b40ead03a..5f5dd1210 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -780,7 +780,7 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document
                             transform: `scale(${this.uiBtnScaling})`,
                             bottom: Number.isNaN(this.maxWidgetSize) ? undefined : this.maxWidgetSize,
                         }}>
-                        {this._props.DocumentView?.() ? <TagsView Views={[this._props.DocumentView?.()]} /> : null}
+                        {this._props.DocumentView?.() && !this._props.docViewPath().slice(-2)[0].ComponentView?.isUnstyledView?.() ? <TagsView Views={[this._props.DocumentView?.()]} /> : null}
                     </div>
                 ) : (
                     <>
-- 
cgit v1.2.3-70-g09d2

