aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ClientUtils.ts34
-rw-r--r--src/client/apis/gpt/GPT.ts12
-rw-r--r--src/client/documents/Documents.ts10
-rw-r--r--src/client/util/CurrentUserUtils.ts92
-rw-r--r--src/client/views/MainView.tsx2
-rw-r--r--src/client/views/collections/CollectionCardDeckView.scss43
-rw-r--r--src/client/views/collections/CollectionCardDeckView.tsx415
-rw-r--r--src/client/views/collections/CollectionCarouselView.tsx2
-rw-r--r--src/client/views/global/globalScripts.ts283
-rw-r--r--src/client/views/nodes/ChatBox/MessageComponent.scss10
-rw-r--r--src/client/views/nodes/ChatBox/MessageComponent.tsx110
-rw-r--r--src/client/views/nodes/ComparisonBox.scss2
-rw-r--r--src/client/views/nodes/ComparisonBox.tsx142
-rw-r--r--src/client/views/nodes/FontIconBox/FontIconBox.tsx2
-rw-r--r--src/client/views/nodes/PDFBox.tsx8
-rw-r--r--src/client/views/pdf/AnchorMenu.tsx2
-rw-r--r--src/client/views/pdf/GPTPopup/GPTPopup.scss1
-rw-r--r--src/client/views/pdf/GPTPopup/GPTPopup.tsx152
-rw-r--r--src/client/views/pdf/PDFViewer.tsx2
19 files changed, 919 insertions, 405 deletions
diff --git a/src/ClientUtils.ts b/src/ClientUtils.ts
index d03ae1486..8e6cd78d0 100644
--- a/src/ClientUtils.ts
+++ b/src/ClientUtils.ts
@@ -238,6 +238,40 @@ export namespace ClientUtils {
return 'rgba(' + col.r + ',' + col.g + ',' + col.b + (col.a !== undefined ? ',' + col.a : '') + ')';
}
+ export function hexToHsv(hex: string): [number, number, number] {
+ if (!hex) return [0, 0, 0]; // Default to black if hex is not defined
+ const r = parseInt(hex.slice(1, 3), 16) / 255;
+ const g = parseInt(hex.slice(3, 5), 16) / 255;
+ const b = parseInt(hex.slice(5, 7), 16) / 255;
+ const max = Math.max(r, g, b),
+ min = Math.min(r, g, b);
+ const d = max - min;
+ let h: number;
+ const s = max === 0 ? 0 : d / max;
+ const v = max;
+
+ switch (max) {
+ case min:
+ h = 0;
+ break;
+ case r:
+ h = (g - b) / d + (g < b ? 6 : 0);
+ break;
+ case g:
+ h = (b - r) / d + 2;
+ break;
+ case b:
+ h = (r - g) / d + 4;
+ break;
+ default:
+ h = 0;
+ break;
+ }
+ h /= 6;
+ return [h, s, v];
+ };
+
+
export function HSLtoRGB(h: number, s: number, l: number) {
// Must be fractions of 1
// s /= 100;
diff --git a/src/client/apis/gpt/GPT.ts b/src/client/apis/gpt/GPT.ts
index 05007960d..40af70d99 100644
--- a/src/client/apis/gpt/GPT.ts
+++ b/src/client/apis/gpt/GPT.ts
@@ -42,8 +42,9 @@ const callTypeMap: { [type: string]: GPTCallOpts } = {
sort: {
model: 'gpt-4o',
maxTokens: 2048,
- temp: 0.5,
- prompt: "I'm going to give you a list of descriptions. Each one is seperated by ====== on either side. They will vary in length, so make sure to only seperate when you see ======. Sort them into lists by shared content. MAKE SURE EACH DESCRIPTOR IS IN ONLY ONE LIST. Generate only the list with each list seperated by ====== with the elements seperated by ~~~~~~. Try to do around 4 groups, but a little more or less is ok.",
+ 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 shared content. 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: "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 `======`. Sort them into lists by shared content. Make sure each description is in only one list. Each list should be separated by `======` with the elements within it separated by `~~~~~~`. Immediately afterward, surrounded by `------` on BOTH SIDES, provide some insight into your reasoning for the way you sorted. It is VERY important that you format it exactly as described, ensuring the proper number of `=` `~` and `-` (6 of each) and no commas.Try to create around 4 groups, but a little more or less is ok. Also, I may provide some more insight after this colon:"
},
describe: { model: 'gpt-4-vision-preview', maxTokens: 2048, temp: 0, prompt: 'Describe these images in 3-5 words' },
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.' },
@@ -70,7 +71,12 @@ const gptAPICall = async (inputTextIn: string, callType: GPTCallType, prompt?: a
try {
lastCall = inputText;
- const usePrompt = prompt ? opts.prompt + prompt : opts.prompt;
+ // const configuration: ClientOptions = {
+ // apiKey: process.env.OPENAI_KEY,
+ // dangerouslyAllowBrowser: true,
+ // };
+
+ const usePrompt = prompt ? prompt + opts.prompt : opts.prompt;
const messages: ChatCompletionMessageParam[] = [
{ role: 'system', content: usePrompt },
{ role: 'user', content: inputText },
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 449347403..c75c10eb8 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -479,8 +479,14 @@ export class DocumentOptions {
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_customField?: STRt = new StrInfo('field key used for sorting cards');
- cardSort_visibleSortGroups?: List<number>; // which sorting values are being filtered (shown)
+ // cardSortForDropDown?: STRt = new StrInfo('needed for dropdown and i dont know why')
+ // cardSort_customField?: STRt = new StrInfo('field key used for sorting cards');
+ cardSort_activeIcons?: List<string>; //icons each card is tagges with
+ // cardSort_visibleSortGroups?: List<string>; // which sorting values are being filtered (shown)
+
+ // cardSort_visibleSortGroups?: List<number>; // which sorting values are being filtered (shown)
+ cardSort_isDesc?: BOOLt = new BoolInfo('whether the cards are sorted ascending or descending');
+ // test?: STRt = new StrInfo('testing for filtering')
}
export const DocOptions = new DocumentOptions();
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index cb3d9df62..f42c15c14 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -667,32 +667,64 @@ pie title Minerals in my tap water
}
static cardTools(): Button[] {
return [
+ // { btnList: new List<string>(["Time", "Type", "Color", "Chat GPT", "Custom 1", "Custom 2", "Custom 3" ]),
+ // title: "Sort Type", toolTip: "Card Sort Type", btnType: ButtonType.DropdownList, ignoreClick: true, width: 100, scripts: { script: '{ return setCardSort(value, _readOnly_); }'}},
+ // { 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: "Time", icon:"hourglass-half", toolTip:"Sort by most recent document creation", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"time", funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
{ title: "Type", icon:"eye", toolTip:"Sort by document type", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"docType",funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
{ title: "Color", icon:"palette", toolTip:"Sort by document color", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"color", funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
+ { title: "AI Sort", icon:"robot", toolTip:"Have Chat GPT sort your cards for you !", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"chat", 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: "Sort", toolTip: "Manage sort order / lock status", icon: "sort" , btnType: ButtonType.MultiToggleButton, toolType:"alignment", ignoreClick: true,
+ subMenu: [
+ { title: "Ascending", toolTip: "Sort the cards in ascending order", btnType: ButtonType.ToggleButton, icon: "sort-up", toolType:"up", ignoreClick: true, scripts: {onClick: '{ return showFreeform(this.toolType, _readOnly_);}' }},
+ { title: "Descending", toolTip: "Sort the cards in descending order",btnType: ButtonType.ToggleButton, icon: "sort-down",toolType:"down",ignoreClick: true, scripts: {onClick: '{ return showFreeform(this.toolType, _readOnly_);}'} },
+ ]},
+
+ { title: "Filter", icon:"Filter", toolTip:"Filter cards by tags", width: 150, subMenu: this.cardGroupTools(), expertMode: false, toolType:CollectionViewType.Card, funcs: {hidden: `!SelectedDocType(this.toolType, this.expertMode)`, linearView_IsOpen: `SelectedDocType(this.toolType, this.expertMode)`} },
+
+
+
+
+
+ // // { title: "AIs", icon:"Visibility", toolTip:"Filter AI labels", subMenu: this.cardGroupTools("robot"), expertMode: false, toolType:CollectionViewType.Card, funcs: {hidden:`!showFreeform("chat", true)`, linearView_IsOpen: `SelectedDocType(this.toolType, this.expertMode)`} },
+ // // { title: "Custom", icon:"heart", toolTip:"Add Like labels", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"like", funcs: {hidden:`showFreeform ("like", true)`},scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
+ // { title: "1st", icon:"Visibility", toolTip:"Filter likes", width: 150, subMenu: this.cardGroupTools("heart"), expertMode: false, toolType:CollectionViewType.Card, funcs: {hidden:`!showFreeform("like", true)`, linearView_IsOpen: `SelectedDocType(this.toolType, this.expertMode)`} },
+ // // { title: "Custom", icon:"star", toolTip:"Add Star labels", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"star", funcs: {hidden:`showFreeform ("star", true)`},scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
+ // { title: "2nd", icon:"Visibility", toolTip:"Filter stars", width: 150, subMenu: this.cardGroupTools("star"), expertMode: false, toolType:CollectionViewType.Card, funcs: {hidden:`!showFreeform("star", true)`, linearView_IsOpen: `SelectedDocType(this.toolType, this.expertMode)`} },
+ // // { title: "Custom", icon:"cloud", toolTip:"Add Idea labels", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"idea", funcs: {hidden:`showFreeform ("idea", true)`},scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
+ // { title: "3rd", icon:"Visibility", toolTip:"Filter ideas", width: 150, subMenu: this.cardGroupTools("cloud"), expertMode: false, toolType:CollectionViewType.Card, funcs: {hidden:`!showFreeform("idea", true)`, linearView_IsOpen: `SelectedDocType(this.toolType, this.expertMode)`} },
]
}
- static labelTools(): Button[] {
- return [
- { title: "AI", icon:"robot", toolTip:"Add AI labels", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"chat", funcs: {hidden:`showFreeform ("chat", true)`},scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
- { title: "AIs", icon:"AI Sort", toolTip:"Filter AI labels", subMenu: this.cardGroupTools("chat"), expertMode: false, toolType:CollectionViewType.Card, funcs: {hidden:`!showFreeform("chat", true)`, linearView_IsOpen: `SelectedDocType(this.toolType, this.expertMode)`} },
- { title: "Like", icon:"heart", toolTip:"Add Like labels", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"like", funcs: {hidden:`showFreeform ("like", true)`},scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
- { title: "Likes", icon:"Likes", toolTip:"Filter likes", width: 10, subMenu: this.cardGroupTools("heart"), expertMode: false, toolType:CollectionViewType.Card, funcs: {hidden:`!showFreeform("like", true)`, linearView_IsOpen: `SelectedDocType(this.toolType, this.expertMode)`} },
- { title: "Star", icon:"star", toolTip:"Add Star labels", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"star", funcs: {hidden:`showFreeform ("star", true)`},scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
- { title: "Stars", icon:"Stars", toolTip:"Filter stars", width: 80, subMenu: this.cardGroupTools("star"), expertMode: false, toolType:CollectionViewType.Card, funcs: {hidden:`!showFreeform("star", true)`, linearView_IsOpen: `SelectedDocType(this.toolType, this.expertMode)`} },
- { title: "Idea", icon:"satellite", toolTip:"Add Idea labels", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"idea", funcs: {hidden:`showFreeform ("idea", true)`},scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
- { title: "Ideas", icon:"Ideas", toolTip:"Filter ideas", width: 80, subMenu: this.cardGroupTools("satellite"), expertMode: false, toolType:CollectionViewType.Card, funcs: {hidden:`!showFreeform("idea", true)`, linearView_IsOpen: `SelectedDocType(this.toolType, this.expertMode)`} },
- ]
- }
- static cardGroupTools(icon: string): Button[] {
+ // static labelTools(): Button[] {
+ // return [
+ // // { title: "Smart", icon:"robot", toolTip:"Have ChatGPT Label and sort your cards for you!", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"chat", funcs: {hidden:`showFreeform ("chat", true)`},scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
+ // { title: "AIs", icon:"AI Sort", toolTip:"Filter AI labels", subMenu: this.cardGroupTools("robot"), expertMode: false, toolType:CollectionViewType.Card, funcs: {hidden:`!showFreeform("chat", true)`, linearView_IsOpen: `SelectedDocType(this.toolType, this.expertMode)`} },
+ // // { title: "Custom", icon:"heart", toolTip:"Add Like labels", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"like", funcs: {hidden:`showFreeform ("like", true)`},scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
+ // { title: "1st", icon:"1st", toolTip:"Filter likes", width: 10, subMenu: this.cardGroupTools("heart"), expertMode: false, toolType:CollectionViewType.Card, funcs: {hidden:`!showFreeform("like", true)`, linearView_IsOpen: `SelectedDocType(this.toolType, this.expertMode)`} },
+ // // { title: "Custom", icon:"star", toolTip:"Add Star labels", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"star", funcs: {hidden:`showFreeform ("star", true)`},scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
+ // { title: "2nd", icon:"2nd", toolTip:"Filter stars", width: 80, subMenu: this.cardGroupTools("star"), expertMode: false, toolType:CollectionViewType.Card, funcs: {hidden:`!showFreeform("star", true)`, linearView_IsOpen: `SelectedDocType(this.toolType, this.expertMode)`} },
+ // // { title: "Custom", icon:"cloud", toolTip:"Add Idea labels", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"idea", funcs: {hidden:`showFreeform ("idea", true)`},scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
+ // { title: "3rd", icon:"3rd", toolTip:"Filter ideas", width: 80, subMenu: this.cardGroupTools("cloud"), expertMode: false, toolType:CollectionViewType.Card, funcs: {hidden:`!showFreeform("idea", true)`, linearView_IsOpen: `SelectedDocType(this.toolType, this.expertMode)`} },
+ // ]
+ // }
+ static cardGroupTools(): Button[] {
return [
- { title: "1", icon, toolTip:"Click to toggle visibility", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"1", funcs: {hidden:`!cardHasLabel(this.toolType)`}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
- { title: "2", icon, toolTip:"Click to toggle visibility", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"2", funcs: {hidden:`!cardHasLabel(this.toolType)`}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
- { title: "3", icon, toolTip:"Click to toggle visibility", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"3", funcs: {hidden:`!cardHasLabel(this.toolType)`}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
- { title: "4", icon, toolTip:"Click to toggle visibility", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"4", funcs: {hidden:`!cardHasLabel(this.toolType)`}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
- { title: "5", icon, toolTip:"Click to toggle visibility", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"5", funcs: {hidden:`!cardHasLabel(this.toolType)`}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
- { title: "6", icon, toolTip:"Click to toggle visibility", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"6", funcs: {hidden:`!cardHasLabel(this.toolType)`}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
- { title: "7", icon, toolTip:"Click to toggle visibility", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"7", funcs: {hidden:`!cardHasLabel(this.toolType)`}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
+ { title: "Star", icon: "star", toolTip:"Click to toggle the star group's visibility", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"star", funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
+ { title: "Heart", icon: "heart", toolTip:"Click to toggle the heart group's visibility", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"heart", funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
+ { title: "Bolt", icon: "bolt", toolTip:"Click to toggle the bolt group's visibility", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"bolt", funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
+ { title: "Cloud", icon: "cloud", toolTip:"Click to toggle the cloud group's visibility", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"cloud", funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
+
+
+ // { title: "Group 1", icon, toolTip:"Click to toggle group 1's visibility", btnType: ButtonType.ToggleButton, width: 40, expertMode: false, toolType:"1", funcs: {hidden:`!cardHasLabel(this.toolType)`}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
+ // { title: "Group 2", icon, toolTip:"Click to toggle group 2's visibility", btnType: ButtonType.ToggleButton, width: 40, expertMode: false, toolType:"2", funcs: {hidden:`!cardHasLabel(this.toolType)`}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
+ // { title: "Group 3", icon, toolTip:"Click to toggle group 3's visibility", btnType: ButtonType.ToggleButton, width: 40, expertMode: false, toolType:"3", funcs: {hidden:`!cardHasLabel(this.toolType)`}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
+ // { title: "Group 4", icon, toolTip:"Click to toggle group 4's visibility", btnType: ButtonType.ToggleButton, width: 40, expertMode: false, toolType:"4", funcs: {hidden:`!cardHasLabel(this.toolType)`}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
+ // { title: "", icon, toolTip:"Click to toggle group 5's visibility", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"5", funcs: {hidden:`!cardHasLabel(this.toolType)`}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
+ // { title: "", icon, toolTip:"Click to toggle group 6's visibility", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"6", funcs: {hidden:`!cardHasLabel(this.toolType)`}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
+ // { title: "", icon, toolTip:"Click to toggle group 7's visibility", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"7", funcs: {hidden:`!cardHasLabel(this.toolType)`}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
]
}
static viewTools(): Button[] {
@@ -801,8 +833,22 @@ pie title Minerals in my tap water
{ 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: "Sort", toolTip: "Card sort", 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: "Label", icon: "Label", 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: "SortType", btnList: new List<string>(["Time", "Type", "Color", "ChatGPT", "Custom 1", "Custom 2", "Custom 3" ]),
+ // toolTip: "Card Sort Type", btnType: ButtonType.DropdownList, ignoreClick: true, width: 100, scripts: { script: '{ return showFreeform(value, _readOnly_); }' },
+ // // funcs: {hidden: `!SelectedDocType("card", this.expertMode)`}
+ // },
+ // { title: "Visibility", icon:"Visibility", toolTip:"Filter AI labels", subMenu: this.cardGroupTools("robot"), expertMode: false, toolType:CollectionViewType.Card, funcs: {hidden:`!showFreeform("chat", true)`, width: 100, linearView_IsOpen: `SelectedDocType(this.toolType, this.expertMode)`} },
+ // // { title: "Custom", icon:"heart", toolTip:"Add Like labels", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"like", funcs: {hidden:`showFreeform ("like", true)`},scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
+ // { title: "Visibility", icon:"Visibility", toolTip:"Filter likes", width: 10, subMenu: this.cardGroupTools("heart"), expertMode: false, toolType:CollectionViewType.Card, funcs: {hidden:`!showFreeform("like", true)`, width: 100, linearView_IsOpen: `SelectedDocType(this.toolType, this.expertMode)`} },
+ // // { title: "Custom", icon:"star", toolTip:"Add Star labels", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"star", funcs: {hidden:`showFreeform ("star", true)`},scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
+ // { title: "Visibility", icon:"Visibility", toolTip:"Filter stars", width: 80, subMenu: this.cardGroupTools("star"), expertMode: false, toolType:CollectionViewType.Card, funcs: {hidden:`!showFreeform("star", true)`, width: 100, linearView_IsOpen: `SelectedDocType(this.toolType, this.expertMode)`} },
+ // // { title: "Custom", icon:"cloud", toolTip:"Add Idea labels", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"idea", funcs: {hidden:`showFreeform ("idea", true)`},scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
+ // { title: "Visibility", icon:"Visibility", toolTip:"Filter ideas", width: 80, subMenu: this.cardGroupTools("cloud"), expertMode: false, toolType:CollectionViewType.Card, funcs: {hidden:`!showFreeform("idea", true)`,width: 100, linearView_IsOpen: `SelectedDocType(this.toolType, this.expertMode)`} },
+
+ { title: "Card", icon: "Card", toolTip: "Card View Tools", subMenu: CurrentUserUtils.cardTools(), expertMode: false, toolType:CollectionViewType.Card, funcs: {hidden: `!SelectedDocType(this.toolType, this.expertMode)`, linearView_IsOpen: `SelectedDocType(this.toolType, this.expertMode)`} }, // Always available
+ // { title: "Create", icon: "Create", toolTip: "Assign card labels", subMenu: CurrentUserUtils.labelTools(), expertMode: false, toolType:CollectionViewType.Card, funcs: {hidden: `!SelectedDocType(this.toolType, this.expertMode)`, linearView_IsOpen: `SelectedDocType(this.toolType, this.expertMode)`} }, // Always available
{ title: "Web", icon: "Web", toolTip: "Web functions", subMenu: CurrentUserUtils.webTools(), expertMode: false, toolType:DocumentType.WEB, funcs: {hidden: `!SelectedDocType(this.toolType, this.expertMode)`, linearView_IsOpen: `SelectedDocType(this.toolType, this.expertMode)`} }, // Only when Web is selected
{ title: "Video", icon: "Video", toolTip: "Video functions", subMenu: CurrentUserUtils.videoTools(), expertMode: false, toolType:DocumentType.VID, funcs: {hidden: `!SelectedDocType(this.toolType, this.expertMode)`, linearView_IsOpen: `SelectedDocType(this.toolType, this.expertMode)`} }, // Only when video is selected
{ title: "Image", icon: "Image", toolTip: "Image functions", subMenu: CurrentUserUtils.imageTools(), expertMode: false, toolType:DocumentType.IMG, funcs: {hidden: `!SelectedDocType(this.toolType, this.expertMode)`, linearView_IsOpen: `SelectedDocType(this.toolType, this.expertMode)`} }, // Only when image is selected
@@ -1116,4 +1162,4 @@ ScriptingGlobals.add(function importDocument() { return CurrentUserUtils.import
// eslint-disable-next-line prefer-arrow-callback
ScriptingGlobals.add(function setInkToolDefaults() { Doc.ActiveTool = InkTool.None; });
// eslint-disable-next-line prefer-arrow-callback
-ScriptingGlobals.add(function getSharingDoc() { return Doc.SharingDoc() });
+ScriptingGlobals.add(function getSharingDoc() { return Doc.SharingDoc() }); \ No newline at end of file
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 716edc22d..7ad5cd88c 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -549,6 +549,8 @@ export class MainView extends ObservableReactComponent<{}> {
fa.faRobot,
fa.faSatellite,
fa.faStar,
+ fa.faCloud,
+ fa.faBolt
]
);
}
diff --git a/src/client/views/collections/CollectionCardDeckView.scss b/src/client/views/collections/CollectionCardDeckView.scss
index a089b248d..eb1b456f5 100644
--- a/src/client/views/collections/CollectionCardDeckView.scss
+++ b/src/client/views/collections/CollectionCardDeckView.scss
@@ -26,23 +26,41 @@
display: flex;
padding: 3px;
// width: 300px;
+ // height:100px;
+ pointer-events: none; /* This ensures the container does not capture hover events */
+
background-color: rgb(218, 218, 218); /* Background color of the container */
border-radius: 50px; /* Rounds the corners of the container */
- transform: translateY(75px);
+ transform: translateY(25px);
// box-shadow: 0 4px 8px rgba(0,0,0,0.1); /* Optional: Adds shadow for depth */
align-items: center; /* Centers buttons vertically */
justify-content: start; /* Centers buttons horizontally */
+
+ button {
+ pointer-events: auto; /* Re-enable pointer events for the buttons */
+
+ width: 70px;
+ height: 70px;
+ border-radius: 50%;
+ background-color: $dark-gray;
+ // border-color: $medium-blue;
+ margin: 5px; // transform: translateY(-50px);
+ background-color: transparent;
+ }
}
-button {
- width: 35px;
- height: 35px;
- border-radius: 50%;
- background-color: $dark-gray;
- // border-color: $medium-blue;
- margin: 5px; // transform: translateY(-50px);
+.no-card-span{
+ position: relative;
+ width: fit-content;
+ text-align: center;
+ font-size: 65px;
+
+
+
}
+
+
// button:hover {
// transform: translateY(-50px);
// }
@@ -74,11 +92,18 @@ button {
flex-direction: column;
}
+// .card-item:hover {
+// box-shadow: 0 20px 20px $medium-blue;
+// transform: scale(1.05);
+
+
+// }
+
.card-item-inactive {
opacity: 0.5;
}
.card-item-active {
- position: absolute;
+ // position: absolute;
z-index: 100;
}
diff --git a/src/client/views/collections/CollectionCardDeckView.tsx b/src/client/views/collections/CollectionCardDeckView.tsx
index de46180e6..60befebec 100644
--- a/src/client/views/collections/CollectionCardDeckView.tsx
+++ b/src/client/views/collections/CollectionCardDeckView.tsx
@@ -2,8 +2,8 @@ import { IReactionDisposer, ObservableMap, action, computed, makeObservable, obs
import { observer } from 'mobx-react';
import * as React from 'react';
import { ClientUtils, DashColor, returnFalse, returnZero } from '../../../ClientUtils';
-import { numberRange } from '../../../Utils';
-import { Doc, NumListCast } from '../../../fields/Doc';
+import { emptyFunction, numberRange } from '../../../Utils';
+import { Doc, NumListCast, StrListCast } from '../../../fields/Doc';
import { DocData } from '../../../fields/DocSymbols';
import { Id } from '../../../fields/FieldSymbols';
import { BoolCast, Cast, DateCast, NumCast, RTFCast, ScriptCast, StrCast } from '../../../fields/Types';
@@ -19,12 +19,22 @@ import { DocumentView } from '../nodes/DocumentView';
import { GPTPopup, GPTPopupMode } from '../pdf/GPTPopup/GPTPopup';
import './CollectionCardDeckView.scss';
import { CollectionSubView } from './CollectionSubView';
+import { FieldsDropdown } from '../FieldsDropdown';
+import { Button, IconButton } from 'browndash-components';
+import { faStar } from '@fortawesome/free-solid-svg-icons';
+import { FaStar, FaHeart, FaRobot, FaCloud } from 'react-icons/fa';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { SettingsManager } from '../../util/SettingsManager';
+import { Tooltip } from '@mui/material';
+import { dropActionType } from '../../util/DropActionTypes';
+import { List } from '../../../fields/List';
enum cardSortings {
Time = 'time',
Type = 'type',
Color = 'color',
Custom = 'custom',
+ Chat = 'chat',
None = '',
}
@observer
@@ -35,10 +45,15 @@ export class CollectionCardView extends CollectionSubView() {
private _textToDoc = new Map<string, Doc>();
@observable _forceChildXf = false;
- @observable _isLoading = false;
+ // @observable _isLoading = false;
@observable _hoveredNodeIndex = -1;
@observable _docRefs = new ObservableMap<Doc, DocumentView>();
+ _draggerRef = React.createRef<HTMLDivElement>();
@observable _maxRowCount = 10;
+ @observable _docDraggedIndex: number = -1;
+ @observable _isACardBeingDragged: boolean = false;
+ @observable overIndex: number = -1;
+
static getButtonGroup(groupFieldKey: 'chat' | 'star' | 'idea' | 'like', doc: Doc): number | undefined {
return Cast(doc[groupFieldKey], 'number', null);
@@ -61,23 +76,32 @@ export class CollectionCardView extends CollectionSubView() {
}
};
- protected createDashEventsTarget = (ele: HTMLDivElement | null) => {
- this._dropDisposer?.();
- if (ele) {
- this._dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.layoutDoc);
- }
- };
constructor(props: any) {
super(props);
makeObservable(this);
+ this.setRegenerateCallback();
}
+ setRegenerateCallback() {
+ GPTPopup.Instance.setRegenerateCallback(this.childPairStringListAndUpdateSortDesc);
+ }
+
+ @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) => this.processGptOutput(sortResult);
+ };
+
componentDidMount(): void {
+ this.Document.childFilters_boolean = 'OR'
this._disposers.sort = reaction(
- () => ({ cardSort: this.cardSort, field: this.cardSort_customField }),
- ({ cardSort, field }) => (cardSort === cardSortings.Custom && field === 'chat' ? this.openChatPopup() : GPTPopup.Instance.setVisible(false))
+ () => ({ cardSort: this.cardSort }),
+ ({ cardSort}) => (cardSort === cardSortings.Chat ? this.openChatPopup() : GPTPopup.Instance.setVisible(false))
);
+
+
}
componentWillUnmount() {
@@ -92,6 +116,7 @@ export class CollectionCardView extends CollectionSubView() {
@computed get cardSort() {
return StrCast(this.Document.cardSort) as any as cardSortings;
}
+
/**
* how much to scale down the contents of the view so that everything will fit
*/
@@ -100,42 +125,31 @@ export class CollectionCardView extends CollectionSubView() {
return (this._childDocumentWidth * length) / this._props.PanelWidth();
}
- @computed get translateWrapperX() {
- let translate = 0;
-
- if (this.inactiveDocs().length !== this.childDocsWithoutLinks.length && this.inactiveDocs().length < 10) {
- translate += this.panelWidth() / 2;
- }
- return translate;
- }
-
/**
* The child documents to be rendered-- either all of them except the Links or the docs in the currently active
* custom group
*/
@computed get childDocsWithoutLinks() {
const regularDocs = this.childDocs.filter(l => l.type !== DocumentType.LINK);
- const activeGroups = NumListCast(this.Document.cardSort_visibleSortGroups);
+ const activeGroups = StrListCast(this.Document.cardSort_visibleSortGroups);
- if (activeGroups.length > 0 && this.cardSort === cardSortings.Custom) {
+ if (activeGroups.length > 0) {
return regularDocs.filter(doc => {
- // Get the group number for the current index
- const groupNumber = CollectionCardView.getButtonGroup(this.cardSort_customField, doc);
- // Check if the group number is in the active groups
- return groupNumber !== undefined && activeGroups.includes(groupNumber);
- });
+ const activeTags = StrListCast(doc.cardSort_activeIcons)
+ return activeTags !== undefined && activeTags.some(tag => activeGroups.includes(tag));
+ })
}
// Default return for non-custom cardSort or other cases, filtering out links
return regularDocs;
}
+
+
/**
- * Determines the order in which the cards will be rendered depending on the current sort type
+ * Number of rows of cards to be rendered
*/
- @computed get sortedDocs() {
- return this.sort(this.childDocsWithoutLinks, this.cardSort, BoolCast(this.layoutDoc.sortDesc));
- }
+ @computed get numRows() { return Math.ceil(this.sortedDocs.length / 10); }
@action
setHoveredNodeIndex = (index: number) => {
@@ -170,6 +184,8 @@ export class CollectionCardView extends CollectionSubView() {
* @returns
*/
rotate = (amCards: number, index: number) => {
+ if (amCards == 1) return 0;
+
const possRotate = -30 + index * (30 / ((amCards - (amCards % 2)) / 2));
const stepMag = Math.abs(-30 + (amCards / 2 - 1) * (30 / ((amCards - (amCards % 2)) / 2)));
@@ -204,27 +220,76 @@ export class CollectionCardView extends CollectionSubView() {
return Math.abs(stepMag * (apex - index - 1)) - rowOffset;
};
- /**
- * Translates the selected node to the middle fo the screen
- * @param index
- * @returns
- */
- translateSelected = (index: number): number => {
- // if (this.isSelected(index)) {
- const middleOfPanel = this._props.PanelWidth() / 2;
- const scaledNodeWidth = this.panelWidth() * 1.25;
+ findCardDropIndex = (mouseX: number, mouseY: number, direction: 'left' | 'right') => {
- // Calculate the position of the node's left edge before scaling
- const nodeLeftEdge = index * this.panelWidth();
- // Find the center of the node after scaling
- const scaledNodeCenter = nodeLeftEdge + scaledNodeWidth / 2;
+ const amCardsTotal = this.sortedDocs.length
+ let index = 0;
+ const cardWidth = amCardsTotal < this._maxRowCount ? this._props.PanelWidth() / amCardsTotal : this._props.PanelWidth() / this._maxRowCount;
+
+ // Calculate the adjusted X position accounting for the initial offset
+ let adjustedX = mouseX
- // Calculate the translation needed to align the scaled node's center with the panel's center
- const translation = middleOfPanel - scaledNodeCenter - scaledNodeWidth - scaledNodeWidth / 4;
+ const amRows = Math.ceil(amCardsTotal / this._maxRowCount);
+ const rowHeight = this._props.PanelHeight( ) / amRows
+ const currRow = Math.floor((mouseY - 100) / rowHeight) //rows start at 0
- return translation;
+ if (adjustedX < 0) {
+ return 0; // Before the first column
+ }
+
+ if (amCardsTotal < this._maxRowCount) {
+ index = Math.floor(adjustedX / cardWidth);
+ }
+
+ else if (currRow != amRows -1 ){
+ index = Math.floor(adjustedX / cardWidth) + (currRow * this._maxRowCount)
+ }
+
+ else {
+ // console.log(amRows + "am rows")
+ const rowAmCards = amCardsTotal - (currRow * this._maxRowCount)
+ const offset = ((this._maxRowCount - rowAmCards ) / 2) * cardWidth
+ adjustedX = mouseX - offset
+
+ index = Math.floor(adjustedX / cardWidth) + (currRow * this._maxRowCount);
+ }
+ return index;
+ };
+
+ @action
+ onPointerMove = (e: React.PointerEvent<HTMLDivElement>) => {
+ if (DragManager.docsBeingDragged.length != 0 ) {
+ this._isACardBeingDragged = true
+
+ const direction = e.movementX > 0 ? 'right' : 'left';
+ const newIndex = this.findCardDropIndex(e.clientX, e.clientY, direction);
+
+ if (newIndex !== this._docDraggedIndex && newIndex != -1) {
+ this._docDraggedIndex = newIndex;
+ }
+ }
};
+
+ onInternalDrop = (e: Event, de: DragManager.DropEvent) => {
+ if (de.complete.docDragData) {
+ this._isACardBeingDragged = false;
+ this._docDraggedIndex = -1;
+ e.stopPropagation()
+ const draggedDocs = de.complete.docDragData?.draggedDocuments;
+ return true;
+ }
+ return false;
+ };
+
+
+ @computed get sortedDocs() {
+ // console.log("hi hi hi")
+ // console.log(this.layoutDoc.cardSort_isDesc + "layoutdoc desc")
+ console.log(this.cardSort + "card sort")
+
+ return this.sort(this.childDocsWithoutLinks, this.cardSort, BoolCast(this.layoutDoc.cardSort_isDesc), this._docDraggedIndex);
+ }
/**
* 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
@@ -233,38 +298,65 @@ export class CollectionCardView extends CollectionSubView() {
* @param isDesc
* @returns
*/
- sort = (docs: Doc[], sortType: cardSortings, isDesc: boolean) => {
- if (sortType === cardSortings.None) return docs;
- docs.sort((docA, docB) => {
- const [typeA, typeB] = (() => {
+ sort = (docs: Doc[], sortType: cardSortings, isDesc: boolean, dragIndex: number) => {
+ // if (sortType === cardSortings.None) return docs;
+
+ // if(sortType !== cardSortings.None){
+ docs.sort((docA, docB) => {
+
+ const [typeA, typeB] = (() => {
switch (sortType) {
case cardSortings.Time:
- return [DateCast(docA.author_date)?.date ?? Date.now(),
- DateCast(docB.author_date)?.date ?? Date.now()];
+ return [DateCast(docA.author_date)?.date ?? Date.now(),
+ DateCast(docB.author_date)?.date ?? Date.now()];
case cardSortings.Color:
- return [DashColor(StrCast(docA.backgroundColor)).hsv().toString(), // If docA.type is undefined, use an empty string
- DashColor(StrCast(docB.backgroundColor)).hsv().toString()]; // If docB.type is undefined, use an empty string
+ return [ClientUtils.hexToHsv(StrCast(docA.backgroundColor)),
+ ClientUtils.hexToHsv(StrCast(docB.backgroundColor))];
case cardSortings.Custom:
- return [CollectionCardView.getButtonGroup(this.cardSort_customField, docA)??0,
- CollectionCardView.getButtonGroup(this.cardSort_customField, docB)??0];
- default: return [StrCast(docA.type), // If docA.type is undefined, use an empty string
- StrCast(docB.type)]; // If docB.type is undefined, use an empty string
- } // prettier-ignore
+ return [CollectionCardView.getButtonGroup(this.cardSort_customField, docA) ?? 9999,
+ CollectionCardView.getButtonGroup(this.cardSort_customField, docB) ?? 9999];
+ case cardSortings.Chat:
+ return [NumCast(docA.chat) ?? 9999,
+ NumCast(docB.chat) ?? 9999]
+
+ default:
+ return [StrCast(docA.type), StrCast(docB.type)]
+ }
})();
-
+
+ // console.log(`Sorting ${sortType}: ${typeA} vs ${typeB}`);
const out = typeA < typeB ? -1 : typeA > typeB ? 1 : 0;
- return isDesc ? -out : out; // Reverse the sort order if descending is true
- });
+ // console.log(`Comparison result: ${out} (isDesc: ${isDesc})`);
+ if (isDesc){
+ return out
+ }
+
+ return -out
+
+ });
+ // }
+
+ if (dragIndex != -1) {
+ const draggedDoc = DragManager.docsBeingDragged[0];
+ const originalIndex = docs.findIndex(doc => doc === draggedDoc);
+
+ docs.splice(originalIndex, 1);
+ docs.splice(dragIndex, 0, draggedDoc);
+ }
+
return docs;
};
+
+
+
displayDoc = (doc: Doc, screenToLocalTransform: () => Transform) => (
<DocumentView
// eslint-disable-next-line react/jsx-props-no-spreading
{...this._props}
ref={action((r: DocumentView) => r?.ContentDiv && this._docRefs.set(doc, r))}
- Document={doc}
+ Document={doc}
NativeWidth={returnZero}
NativeHeight={returnZero}
fitWidth={returnFalse}
@@ -273,10 +365,13 @@ export class CollectionCardView extends CollectionSubView() {
LayoutTemplate={this._props.childLayoutTemplate}
LayoutTemplateString={this._props.childLayoutString}
ScreenToLocalTransform={screenToLocalTransform} // makes sure the box wrapper thing is in the right spot
- isContentActive={this.isChildContentActive}
+ isContentActive={emptyFunction}
isDocumentActive={this._props.childDocumentsActive?.() || this.Document._childDocumentsActive ? this._props.isDocumentActive : this.isContentActive}
PanelWidth={this.panelWidth}
PanelHeight={this.panelHeight(doc)}
+ dragAction={(this.Document.childDragAction ?? this._props.childDragAction) as dropActionType}
+ dontHideOnDrag
+ // pointerEvents={this.blockPointerEventsWhenDragging(doc)}
/>
);
@@ -286,11 +381,11 @@ export class CollectionCardView extends CollectionSubView() {
* @returns
*/
overflowAmCardsCalc = (index: number) => {
- if (this.inactiveDocs().length < this._maxRowCount) {
- return this.inactiveDocs().length;
+ if (this.sortedDocs.length < this._maxRowCount) {
+ return this.sortedDocs.length;
}
// 13 - 3 = 10
- const totalCards = this.inactiveDocs().length;
+ const totalCards = this.sortedDocs.length;
// if 9 or less
if (index < totalCards - (totalCards % 10)) {
return this._maxRowCount;
@@ -323,21 +418,17 @@ export class CollectionCardView extends CollectionSubView() {
* @returns
*/
calculateTranslateY = (isHovered: boolean, isSelected: boolean, realIndex: number, amCards: number, calcRowIndex: number) => {
- if (isSelected) return 50 * this.fitContentScale;
- const trans = isHovered ? this.translateHover(realIndex) : 0;
+ const rowHeight = this._props.PanelHeight() * this.fitContentScale / this.numRows;
+ const rowIndex = Math.trunc(realIndex / this._maxRowCount);
+ const rowToCenterShift = (this.numRows / 2) - rowIndex;
+ if (isSelected) return rowToCenterShift * rowHeight - rowHeight / 2;
+ if (amCards == 1) return 50 * this.fitContentScale;
+ // const trans = isHovered ? this.translateHover(realIndex) : 0;
+ const trans = 0;
return trans + this.translateY(amCards, calcRowIndex, realIndex);
};
- /**
- * Toggles the buttons between on and off when creating custom sort groupings/changing those created by gpt
- * @param childPairIndex
- * @param buttonID
- * @param doc
- */
- toggleButton = undoable((buttonID: number, doc: Doc) => {
- this.cardSort_customField && (doc[this.cardSort_customField] = buttonID);
- }, 'toggle custom button');
-
+
/**
* 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
@@ -378,6 +469,8 @@ export class CollectionCardView extends CollectionSubView() {
return response; // Return the response from gptImageLabel
} catch (error) {
console.log('bad things have happened');
+
+ console.log(error);
}
return '';
};
@@ -386,22 +479,38 @@ export class CollectionCardView extends CollectionSubView() {
* Converts the gpt output into a hashmap that can be used for sorting. lists are seperated by ==== while elements within the list are seperated by ~~~~~~
* @param gptOutput
*/
- processGptOutput = (gptOutput: string) => {
+ @action processGptOutput = (gptOutput: string) => {
+
+ console.log("HIIII")
+ console.log(StrCast(this.Document.cardSort) + "cardSort")
// Split the string into individual list items
const listItems = gptOutput.split('======').filter(item => item.trim() !== '');
+ // console.log(listItems + " LISTT");
+
+ // Debug: print the map contents
+ // console.log("Map contents:", Array.from(this._textToDoc.entries()));
+
listItems.forEach((item, index) => {
- // Split the item by '~~~~~~' to get all descriptors
- const parts = item.split('~~~~~~').map(part => part.trim());
-
- parts.forEach(part => {
- // Find the corresponding Doc in the textToDoc map
- const doc = this._textToDoc.get(part);
- if (doc) {
- doc.chat = index;
- }
- });
+ // Normalize the item (trim whitespace)
+ const normalizedItem = item.trim();
+ // console.log("Normalized item:", normalizedItem);
+
+ // Find the corresponding Doc in the textToDoc map
+ const doc = this._textToDoc.get(normalizedItem);
+ // console.log("DOC:", doc);
+ // console.log("ITEM:", normalizedItem);
+
+ if (doc) {
+ doc.chat = index;
+ } else {
+ console.warn(`No matching document found for item: ${normalizedItem}`);
+ }
});
- };
+
+ }
+
+ // );
+ // };
/**
* Opens up the chat popup and starts the process for smart sorting.
*/
@@ -416,40 +525,109 @@ export class CollectionCardView extends CollectionSubView() {
/**
* Renders the buttons to customize sorting depending on which group the card belongs to and the amount of total groups
- * @param childPairIndex
* @param doc
+ * @param cardSort
* @returns
*/
- renderButtons = (doc: Doc, cardSort: cardSortings) => {
- if (cardSort !== cardSortings.Custom) return '';
- const amButtons = Math.max(4, this.childDocs?.reduce((set, d) => this.cardSort_customField && set.add(NumCast(d[this.cardSort_customField])), new Set<number>()).size ?? 0);
- const activeButtonIndex = CollectionCardView.getButtonGroup(this.cardSort_customField, doc);
- const totalWidth = amButtons * 35 + amButtons * 2 * 5 + 6;
+ renderButtons = (doc: Doc, cardSort: cardSortings): JSX.Element | null => {
+ // if (cardSort !== cardSortings.Custom) return null;
+
+ const amButtons = 4
+
+ // const amButtons = Math.max(
+ // 4,
+ // this.childDocs?.reduce((set, d) => {
+ // if (this.cardSort_customField) {
+ // set.add(NumCast(d[this.cardSort_customField]));
+ // }
+ // return set;
+ // }, new Set<number>()).size ?? 0
+ // );
+
+ // const activeButtonIndex = CollectionCardView.getButtonGroup(this.cardSort_customField, doc);
+
+ const totalWidth = amButtons * 72 + amButtons * 2 * 5 + 6;
+
+ const iconMap: { [key: number]: any } = {
+ 0: 'star',
+ 1: 'heart',
+ 2: 'cloud',
+ 3: 'bolt'
+ };
+
return (
- <div className="card-button-container" style={{ width: `${totalWidth}px` }}>
+ <div className="card-button-container" style={{ width: `${totalWidth}px`, fontSize: '50px' }}>
{numberRange(amButtons).map(i => (
- // eslint-disable-next-line jsx-a11y/control-has-associated-label
- <button
- key={i}
- type="button"
- style={{ backgroundColor: activeButtonIndex === i ? '#4476f7' : '#323232' }} //
- onClick={() => this.toggleButton(i, doc)}
- />
+ <Tooltip key={i} title={<div className="dash-tooltip">Click to add/remove this card from the {iconMap[i]} group</div>}>
+ <button type="button" onClick={() => this.toggleButton(doc, iconMap[i] )}>
+ {this.getButtonIcon(doc, iconMap[i])}
+ </button>
+ </Tooltip>
))}
</div>
);
};
+
+ /**
+ * Toggles the buttons between on and off when creating custom sort groupings/changing those created by gpt
+ * @param childPairIndex
+ * @param buttonID
+ * @param doc
+ */
+ toggleButton = undoable((doc: Doc, icon: string) => {
+
+
+
+ // this.cardSort_customField && (doc[this.cardSort_customField] = buttonID);
+
+ // doc.cardSort_activeIcons = new List<string>()
+
+
+ // const list = StrListCast(doc.cardSort_activeIcons);
+ // doc.cardSort_activeIcons = new List<string>(list.includes(icon) ? list.filter(d => d !== icon) : [...list, icon]);
+
+ BoolCast(doc[icon]) ? doc[icon] = false : doc[icon] = true
+
+
+
+ // StrListCast(doc.cardSort_activeIcons).push(iconMap[buttonID])
+ }, 'toggle card tag');
+
+
+ getButtonIcon = (doc: Doc, icon: any): JSX.Element => {
+
+ // const isActive = StrListCast(doc.cardSort_activeIcons).includes(icon)
+ const isActive = doc[icon]
+
+ // console.log(StrListCast(doc.cardSort_activeIcons))
+ const color = isActive ? '#4476f7' : '#323232';
+
+ return <FontAwesomeIcon icon={icon} size="lg" style={{ color }} />;
+ };
+
/**
* Actually renders all the cards
*/
renderCards = () => {
const anySelected = this.childDocs.some(doc => DocumentView.SelectedDocs().includes(doc));
+ const isEmpty = this.childDocsWithoutLinks.length === 0;
+ const isDesc = BoolCast(this.Document.cardSort_isDesc)
+
+ if (isEmpty) {
+ return (
+ <span className="no-card-span" style={{ width: `100%` }}>
+ Sorry ! There are no cards in this group
+ </span>
+ );
+ }
+
// Map sorted documents to their rendered components
return this.sortedDocs.map((doc, index) => {
- const realIndex = this.sortedDocs.filter(sortDoc => !DocumentView.SelectedDocs().includes(sortDoc)).indexOf(doc);
+ const realIndex = this.sortedDocs.indexOf(doc);
const calcRowIndex = this.overflowIndexCalc(realIndex);
const amCards = this.overflowAmCardsCalc(realIndex);
const isSelected = DocumentView.SelectedDocs().includes(doc);
+ const isDragging = DragManager.docsBeingDragged.includes(doc);
const childScreenToLocal = () => {
this._forceChildXf;
@@ -460,6 +638,12 @@ export class CollectionCardView extends CollectionSubView() {
.scale(1 / scale).rotate(!isSelected ? -this.rotate(amCards, calcRowIndex) : 0); // prettier-ignore
};
+ 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;
+ }
return (
<div
key={doc[Id]}
@@ -472,17 +656,17 @@ export class CollectionCardView extends CollectionSubView() {
SnappingManager.SetIsResizing(undefined);
this._forceChildXf = !this._forceChildXf;
}),
- 700
+ 900
);
}}
style={{
width: this.panelWidth(),
- height: 'max-content', // this.panelHeight(childPair.layout)(),
+ height: 'max-content',
transform: `translateY(${this.calculateTranslateY(this._hoveredNodeIndex === index, isSelected, realIndex, amCards, calcRowIndex)}px)
- translateX(${isSelected ? this.translateSelected(calcRowIndex) : this.translateOverflowX(realIndex, amCards)}px)
+ translateX(calc(${(isSelected ? translateIfSelected() : 0) + "% + " + this.translateOverflowX(realIndex, amCards)+"px"}))
rotate(${!isSelected ? this.rotate(amCards, calcRowIndex) : 0}deg)
- scale(${isSelected ? 1.25 : 1})`,
- }}
+ scale(${isSelected ? 2 : this._hoveredNodeIndex === index ? 1.05 : 1})`,
+ }}
onMouseEnter={() => this.setHoveredNodeIndex(index)}>
{this.displayDoc(doc, childScreenToLocal)}
{this.renderButtons(doc, this.cardSort)}
@@ -490,11 +674,13 @@ export class CollectionCardView extends CollectionSubView() {
);
});
};
+
render() {
return (
<div
+ onPointerMove={e => this.onPointerMove(e)}
className="collectionCardView-outer"
- ref={this.createDashEventsTarget}
+ ref={(ele: HTMLDivElement | null) => this.createDashEventsTarget(ele)}
style={{
background: this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BackgroundColor),
color: this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Color),
@@ -502,8 +688,9 @@ export class CollectionCardView extends CollectionSubView() {
<div
className="card-wrapper"
style={{
- transform: ` scale(${1 / this.fitContentScale}) translateX(${this.translateWrapperX}px)`,
+ transform: ` scale(${1 / this.fitContentScale})`,
height: `${100 * this.fitContentScale}%`,
+ gridAutoRows: `${100/this.numRows}%`
}}
onMouseLeave={() => this.setHoveredNodeIndex(-1)}>
{this.renderCards()}
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index 2adad68e0..4884db709 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -33,7 +33,7 @@ enum practiceVal {
export class CollectionCarouselView extends CollectionSubView() {
private _dropDisposer?: DragManager.DragDropDisposer;
get practiceField() { return this.fieldKey + "_practice"; } // prettier-ignore
- get starField() { return this.fieldKey + "_star"; } // prettier-ignore
+ get starField() { return "star"; } // prettier-ignore
constructor(props: any) {
super(props);
diff --git a/src/client/views/global/globalScripts.ts b/src/client/views/global/globalScripts.ts
index 7730ed385..2ecde0eee 100644
--- a/src/client/views/global/globalScripts.ts
+++ b/src/client/views/global/globalScripts.ts
@@ -36,10 +36,12 @@ import { ImageBox } from '../nodes/ImageBox';
import { VideoBox } from '../nodes/VideoBox';
import { WebBox } from '../nodes/WebBox';
import { RichTextMenu } from '../nodes/formattedText/RichTextMenu';
-import { NumListCast } from '../../../fields/Doc';
+import { NumListCast, StrListCast } from '../../../fields/Doc';
import { List } from '../../../fields/List';
-
+import { CollectionViewType } from '../../documents/DocumentTypes';
// import { InkTranscription } from '../InkTranscription';
+import { Docs } from '../../documents/Documents';
+import { CollectionSubView } from '../collections/CollectionSubView';
// eslint-disable-next-line prefer-arrow-callback
ScriptingGlobals.add(function IsNoneSelected() {
@@ -137,20 +139,25 @@ ScriptingGlobals.add(function toggleOverlay(checkResult?: boolean) {
// eslint-disable-next-line prefer-arrow-callback
ScriptingGlobals.add(function showFreeform(attr: 'center' | 'grid' | 'snaplines' | 'clusters' | 'viewAll' | 'fitOnce', checkResult?: boolean, persist?: boolean) {
const selected = DocumentView.SelectedDocs().lastElement();
+
+ function isAttrFiltered(attr: string) {
+ return StrListCast(selected._childFilters).some(filter => filter.includes(attr));
+ }
+
// prettier-ignore
- const map: Map<'flashcards' | 'center' | 'grid' | 'snaplines' | 'clusters' | 'arrange' | 'viewAll' | 'fitOnce' | 'time' | 'docType' | 'color' | 'links' | 'like' | 'star' | 'idea' | 'chat' | '1' | '2' | '3' | '4',
+ const map: Map<'flashcards' | 'center' | 'grid' | 'snaplines' | 'clusters' | 'arrange' | 'viewAll' | 'fitOnce' | 'time' | 'docType' | 'color' | 'heart' | 'star' | 'cloud' | 'bolt' | 'chat' | '1' | '2' | '3' | '4' | 'up' | 'down' | 'pile',
{
waitForRender?: boolean;
checkResult: (doc: Doc) => any;
setDoc: (doc: Doc, dv: DocumentView) => void;
}> = new Map([
['grid', {
- checkResult: (doc:Doc) => BoolCast(doc?._freeform_backgroundGrid, false),
- setDoc: (doc:Doc) => { doc._freeform_backgroundGrid = !doc._freeform_backgroundGrid; },
+ checkResult: (doc: Doc) => BoolCast(doc?._freeform_backgroundGrid, false),
+ setDoc: (doc: Doc) => { doc._freeform_backgroundGrid = !doc._freeform_backgroundGrid; },
}],
['snaplines', {
- checkResult: (doc:Doc) => BoolCast(doc?._freeform_snapLines, false),
- setDoc: (doc:Doc) => { doc._freeform_snapLines = !doc._freeform_snapLines; },
+ checkResult: (doc: Doc) => BoolCast(doc?._freeform_snapLines, false),
+ setDoc: (doc: Doc) => { doc._freeform_snapLines = !doc._freeform_snapLines; },
}],
['viewAll', {
checkResult: (doc: Doc) => BoolCast(doc?._freeform_fitContentsToBox, false),
@@ -161,13 +168,13 @@ ScriptingGlobals.add(function showFreeform(attr: 'center' | 'grid' | 'snaplines'
},
}],
['center', {
- checkResult: (doc:Doc) => BoolCast(doc?._stacking_alignCenter, false),
- setDoc: (doc:Doc) => { doc._stacking_alignCenter = !doc._stacking_alignCenter; },
+ checkResult: (doc: Doc) => BoolCast(doc?._stacking_alignCenter, false),
+ setDoc: (doc: Doc) => { doc._stacking_alignCenter = !doc._stacking_alignCenter; },
}],
['clusters', {
waitForRender: true, // flags that undo batch should terminate after a re-render giving the script the chance to fire
- checkResult: (doc:Doc) => BoolCast(doc?._freeform_useClusters, false),
- setDoc: (doc:Doc) => { doc._freeform_useClusters = !doc._freeform_useClusters; },
+ 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),
@@ -175,66 +182,115 @@ ScriptingGlobals.add(function showFreeform(attr: 'center' | 'grid' | 'snaplines'
}],
['time', {
checkResult: (doc: Doc) => StrCast(doc?.cardSort) === "time",
- setDoc: (doc: Doc, dv: DocumentView) => doc.cardSort = "time",
+ setDoc: (doc: Doc, dv: DocumentView) => doc.cardSort === "time" ? doc.cardSort = '' : doc.cardSort = 'time',
}],
['docType', {
checkResult: (doc: Doc) => StrCast(doc?.cardSort) === "type",
- setDoc: (doc: Doc, dv: DocumentView) => doc.cardSort = "type",
+ setDoc: (doc: Doc, dv: DocumentView) => doc.cardSort === "type" ? doc.cardSort = '' : doc.cardSort = 'type',
}],
['color', {
checkResult: (doc: Doc) => StrCast(doc?.cardSort) === "color",
- setDoc: (doc: Doc, dv: DocumentView) => doc.cardSort = "color",
- }],
- ['links', {
- checkResult: (doc: Doc) => StrCast(doc?.cardSort) === "links",
- setDoc: (doc: Doc, dv: DocumentView) => doc.cardSort = "links",
+ setDoc: (doc: Doc, dv: DocumentView) => doc.cardSort === "color" ? doc.cardSort = '' : doc.cardSort = 'color',
}],
- ['like', {
- checkResult: (doc: Doc) => StrCast(doc?.cardSort) === "custom" && StrCast(doc?.cardSort_customField) === "like",
+ ['heart', {
+ checkResult: (doc: Doc) => isAttrFiltered('heart'),
setDoc: (doc: Doc, dv: DocumentView) => {
- doc.cardSort = "custom";
- doc.cardSort_customField = "like";
- doc.cardSort_visibleSortGroups = new List<number>();
+ isAttrFiltered('heart') ? Doc.setDocFilter(doc, 'heart', true, 'remove') : Doc.setDocFilter(doc, 'heart', true, 'match');
+
}
}],
['star', {
- checkResult: (doc: Doc) => StrCast(doc?.cardSort) === "custom" && StrCast(doc?.cardSort_customField) === "star",
+ checkResult: (doc: Doc) => isAttrFiltered('star'),
+
+ setDoc: (doc: Doc, dv: DocumentView) => {
+ isAttrFiltered('star') ? Doc.setDocFilter(doc, 'star', true, 'remove') : Doc.setDocFilter(doc, 'star', true, 'match');
+ }
+ }],
+ ['bolt', {
+ checkResult: (doc: Doc) => isAttrFiltered('bolt'),
setDoc: (doc: Doc, dv: DocumentView) => {
- doc.cardSort = "custom";
- doc.cardSort_customField = "star";
- doc.cardSort_visibleSortGroups = new List<number>();
+ isAttrFiltered('bolt') ? Doc.setDocFilter(doc, 'bolt', true, 'remove') : Doc.setDocFilter(doc, 'bolt', true, 'match');
+
}
}],
- ['idea', {
- checkResult: (doc: Doc) => StrCast(doc?.cardSort) === "custom" && StrCast(doc?.cardSort_customField) === "idea",
+ ['cloud', {
+ checkResult: (doc: Doc) => isAttrFiltered('cloud'),
setDoc: (doc: Doc, dv: DocumentView) => {
- doc.cardSort = "custom";
- doc.cardSort_customField = "idea";
- doc.cardSort_visibleSortGroups = new List<number>();
+ isAttrFiltered('cloud') ? Doc.setDocFilter(doc, 'cloud', true, 'remove') : Doc.setDocFilter(doc, 'cloud', true, 'match');
+
}
}],
['chat', {
- checkResult: (doc: Doc) => StrCast(doc?.cardSort) === "custom" && StrCast(doc?.cardSort_customField) === "chat",
+ checkResult: (doc: Doc) => {
+
+ if (StrCast(doc?.cardSort) === "chat"){
+ return true
+ }} ,
setDoc: (doc: Doc, dv: DocumentView) => {
- doc.cardSort = "custom";
- doc.cardSort_customField = "chat";
- doc.cardSort_visibleSortGroups = new List<number>();
+ doc.cardSort === "chat" ? doc.cardSort = '' : doc.cardSort = 'chat';
},
}],
- ]);
- for (let i = 0; i < 8; i++) {
- map.set((i + 1 + '') as any, {
- checkResult: (doc: Doc) => NumListCast(doc?.cardSort_visibleSortGroups).includes(i),
+ ['up', {
+ checkResult: (doc: Doc) => BoolCast(!doc?.cardSort_isDesc),
setDoc: (doc: Doc, dv: DocumentView) => {
- const list = NumListCast(doc.cardSort_visibleSortGroups);
- doc.cardSort_visibleSortGroups = new List<number>(list.includes(i) ? list.filter(d => d !== i) : [...list, i]);
+ doc.cardSort_isDesc = false;
},
- });
- }
+ }],
+ ['down', {
+ checkResult: (doc: Doc) => BoolCast(doc?.cardSort_isDesc),
+ setDoc: (doc: Doc, dv: DocumentView) => {
+ doc.cardSort_isDesc = true;
+ },
+ }],
+ ['pile', {
+ checkResult: (doc: Doc) => doc._type_collection == CollectionViewType.Freeform,
+ setDoc: (doc: Doc, dv: DocumentView) => {
+ doc._type_collection = CollectionViewType.Freeform;
+ const newCol = Docs.Create.CarouselDocument(DocListCast(doc[Doc.LayoutFieldKey(doc)]), {
+ _width: 250,
+ _height: 200,
+ _layout_fitWidth: false,
+ _layout_autoHeight: true,
+ });
+
+
+ const iconMap: { [key: number]: any } = {
+ 0: 'star',
+ 1: 'heart',
+ 2: 'cloud',
+ 3: 'bolt'
+ };
+
+ for (let i=0; i<4; i++){
+ if (isAttrFiltered(iconMap[i])){
+ newCol[iconMap[i]] = true
+ }
+ }
+
+ // if (doc.type == DocumentType.COL ) {
+ newCol && dv.ComponentView?.addDocument?.(newCol);
+ dv.ComponentView?._props.focus(newCol, {willPan: true , willZoomCentered: true, zoomScale: 200} )
+
+ // }
+ // doc as CollectionFreeFormView
+ // doc as typeof CollectionSubView
+
+ // doc._props
+
+ // CollectionFreeFormView.addDocumemnt
+
+ // doc?.addDocument; //this._props.addDocument(newCol)
+ },
+ }],
+ ]);
if (checkResult) {
return map.get(attr)?.checkResult(selected);
}
+ const filters = StrListCast(selected._childFilters).concat(StrListCast(selected?._childFiltersByRanges).filter((filter, i) => !(i % 3)));
+
+ // console.log(filters.some(filter => filter.includes('star'))+ "SUOOOOPPP")
+
const batch = map.get(attr)?.waitForRender ? UndoManager.StartBatch('set freeform attribute') : { end: () => {} };
DocumentView.Selected().map(dv => map.get(attr)?.setDoc(dv.layoutDoc, dv));
setTimeout(() => batch.end(), 100);
@@ -247,6 +303,144 @@ ScriptingGlobals.add(function cardHasLabel(label: string) {
return labelNum < 4 || (selected && DocListCast(selected[Doc.LayoutFieldKey(selected)]).some(doc => doc[StrCast(selected.cardSort_customField)] == labelNum));
}, '');
+// ScriptingGlobals.add(function setCardSort(attr: "Time" | "Type"| "Color"| "ChatGPT"| "Custom 1"| "Custom 2"| "Custom 3", value?: any, checkResult?: boolean) {
+// // const editorView = RichTextMenu.Instance?.TextView?.EditorView;
+// const selected = DocumentView.SelectedDocs().lastElement();
+
+// // prettier-ignore
+// const map: Map<"Time" | "Type"| "Color"| "ChatGPT"| "Custom 1"| "Custom 2"| "Custom 3", { checkResult: (doc: Doc) => any; setDoc: (doc: Doc) => void;}> = new Map([
+// ['Time', {
+
+// checkResult: (doc: Doc) => {StrCast(doc?.cardSort);
+// console.log(StrCast(doc?.cardSort + "card sort"))},
+// setDoc: (doc: Doc) => {doc.cardSort = "time"
+// console.log("hewwo")}
+
+// ,
+// }],
+// ['Type', {
+// checkResult: (doc: Doc) => StrCast(doc?.cardSort),
+// setDoc: (doc: Doc) => doc.cardSort = "type",
+// }],
+// ['Color', {
+// checkResult: (doc: Doc) => StrCast(doc?.cardSort),
+// setDoc: (doc: Doc) => doc.cardSort = "color",
+// }],
+// // ['links', {
+// // checkResult: (doc: Doc) => StrCast(doc?.cardSort) === "links",
+// // setDoc: (doc: Doc) => doc.cardSort = "links",
+// // }],
+// ['Custom 1', {
+// checkResult: (doc: Doc) => StrCast(doc?.cardSort) + " 1",
+// setDoc: (doc: Doc) => {
+// doc.cardSort = "custom";
+// doc.cardSort_customField = "like";
+// doc.cardSort_visibleSortGroups = new List<number>();
+// }
+// }],
+// ['Custom 2', {
+// checkResult: (doc: Doc) => StrCast(doc?.cardSort) + " 2",
+// setDoc: (doc: Doc) => {
+// doc.cardSort = "custom";
+// doc.cardSort_customField = "star";
+// doc.cardSort_visibleSortGroups = new List<number>();
+// }
+// }],
+// ['Custom 3', {
+// checkResult: (doc: Doc) => StrCast(doc?.cardSort) + " 3",
+// setDoc: (doc: Doc) => {
+// doc.cardSort = "custom";
+// doc.cardSort_customField = "idea";
+// doc.cardSort_visibleSortGroups = new List<number>();
+// }
+// }],
+// ['ChatGPT', {
+// checkResult: (doc: Doc) => StrCast(doc?.cardSort_customField),
+// setDoc: (doc: Doc) => {
+// doc.cardSort = "custom";
+// doc.cardSort_customField = "chat";
+// doc.cardSort_visibleSortGroups = new List<number>();
+// },
+// }],
+// ]);
+
+// for (let i = 0; i < 8; i++) {
+// map.set((i + 1 + '') as any, {
+// checkResult: (doc: Doc) => NumListCast(doc?.cardSort_visibleSortGroups).includes(i),
+// setDoc: (doc: Doc) => {
+// const list = NumListCast(doc.cardSort_visibleSortGroups);
+// doc.cardSort_visibleSortGroups = new List<number>(list.includes(i) ? list.filter(d => d !== i) : [...list, i]);
+// },
+// });
+// }
+
+// if (checkResult) {
+// console.log(attr + "attricute")
+// console.log(map.get(attr)?.checkResult(selected) + "check result")
+// return map.get(attr)?.checkResult(selected);
+// }
+
+// console.log(attr + "attricute lol")
+
+// // const batch = map.get(attr)?.waitForRender ? UndoManager.StartBatch('set freeform attribute') : { end: () => {} };
+// DocumentView.Selected().map(dv => map.get(attr)?.setDoc(dv.layoutDoc));
+// // setTimeout(() => batch.end(), 100);
+// return undefined;
+
+// // map.get(attr)?.setDoc?.();
+// // return undefined;
+// });
+
+ScriptingGlobals.add(function setCardSort(value?: any, checkResult?: boolean) {
+ // const editorView = RichTextMenu.Instance?.TextView?.EditorView;
+ const selected = DocumentView.SelectedDocs().lastElement();
+
+ if (checkResult) {
+ // console.log(attr + "attricute")
+ // console.log(map.get(attr)?.checkResult(selected) + "check result")
+ console.log(StrCast(selected?.cardSort) + 'check');
+ const hi = StrCast(selected?.cardSort);
+ return StrCast(selected?.cardSortForDropDown) ?? 'Time';
+ }
+
+ function docFields(doc: Doc): void {
+ switch (value) {
+ case 'Custom 1':
+ doc.cardSort_customField = 'like';
+ break;
+ case 'Custom 2':
+ doc.cardSort_customField = 'star';
+ break;
+ case 'Custom 3':
+ doc.cardSort_customField = 'idea';
+ break;
+ case 'Chat GPT':
+ doc.cardSort = 'custom';
+ doc.cardSort_customField = 'chat';
+ break;
+ default:
+ break;
+ }
+
+ doc.cardSort_visibleSortGroups = new List<number>();
+ }
+
+ // const batch = map.get(attr)?.waitForRender ? UndoManager.StartBatch('set freeform attribute') : { end: () => {} };
+ DocumentView.Selected().map(dv => {
+ dv.Document.cardSortForDropDown = value;
+
+ if (value != 'Chat GPT') {
+ dv.Document.cardSort = value.trim().split(/\s+/)[0].toLowerCase();
+ }
+ docFields(dv.Document);
+ });
+
+ return undefined;
+
+ // map.get(attr)?.setDoc?.();
+ // return undefined;
+});
+
// ScriptingGlobals.add(function setCardSortAttr(attr: 'time' | 'docType' | 'color', value: any, checkResult?: boolean) {
// // const editorView = RichTextMenu.Instance?.TextView?.EditorView;
// const selected = SelectionManager.Docs.lastElement();
@@ -313,6 +507,7 @@ ScriptingGlobals.add(function setFontAttr(attr: 'font' | 'fontColor' | 'highligh
]);
if (checkResult) {
+ // console.log(map.get(attr)?.checkResult() + "font check result")
return map.get(attr)?.checkResult();
}
map.get(attr)?.setDoc?.();
diff --git a/src/client/views/nodes/ChatBox/MessageComponent.scss b/src/client/views/nodes/ChatBox/MessageComponent.scss
new file mode 100644
index 000000000..6fcc0e5e7
--- /dev/null
+++ b/src/client/views/nodes/ChatBox/MessageComponent.scss
@@ -0,0 +1,10 @@
+MessageComponent-citation {
+ color: lightblue;
+ vertical-align: super;
+ font-size: smaller;
+}
+MessageComponent-file_path {
+ color: lightblue;
+ vertical-align: baseline;
+ font-size: inherit;
+}
diff --git a/src/client/views/nodes/ChatBox/MessageComponent.tsx b/src/client/views/nodes/ChatBox/MessageComponent.tsx
index fced0b4d5..f27a18891 100644
--- a/src/client/views/nodes/ChatBox/MessageComponent.tsx
+++ b/src/client/views/nodes/ChatBox/MessageComponent.tsx
@@ -1,11 +1,25 @@
+/* eslint-disable jsx-a11y/control-has-associated-label */
/* eslint-disable react/require-default-props */
-import React from 'react';
-import { observer } from 'mobx-react';
import { MathJax, MathJaxContext } from 'better-react-mathjax';
+import { observer } from 'mobx-react';
+import React from 'react';
+import * as Tb from 'react-icons/tb';
import ReactMarkdown from 'react-markdown';
-import { TbCircle0Filled, TbCircle1Filled, TbCircle2Filled, TbCircle3Filled, TbCircle4Filled, TbCircle5Filled, TbCircle6Filled, TbCircle7Filled, TbCircle8Filled, TbCircle9Filled } from 'react-icons/tb';
+import './MessageComponent.scss';
import { AssistantMessage } from './types';
+const TbCircles = [
+ Tb.TbCircleNumber0Filled,
+ Tb.TbCircleNumber1Filled,
+ Tb.TbCircleNumber2Filled,
+ Tb.TbCircleNumber3Filled,
+ Tb.TbCircleNumber4Filled,
+ Tb.TbCircleNumber5Filled,
+ Tb.TbCircleNumber6Filled,
+ Tb.TbCircleNumber7Filled,
+ Tb.TbCircleNumber8Filled,
+ Tb.TbCircleNumber9Filled,
+];
interface MessageComponentProps {
message: AssistantMessage;
toggleToolLogs: (index: number) => void;
@@ -17,89 +31,41 @@ interface MessageComponentProps {
isCurrent?: boolean;
}
-const MessageComponent: React.FC<MessageComponentProps> = function ({ message, toggleToolLogs, expandedLogIndex, goToLinkedDoc, index, showModal, setCurrentFile, isCurrent = false }) {
- // const messageClass = `${message.role} ${isCurrent ? 'current-message' : ''}`;
-
- const LinkRenderer = ({ href, children }: { href: string; children: React.ReactNode }) => {
- // console.log(href + " " + children)
- const regex = /([a-zA-Z0-9_.!-]+)~~~(citation|file_path)/;
- const matches = href.match(regex);
- // console.log(href)
- // console.log(matches)
- const url = matches ? matches[1] : href;
- const linkType = matches ? matches[2] : null;
- if (linkType === 'citation') {
- switch (children) {
- case '0':
- children = <TbCircle0Filled />;
- break;
- case '1':
- children = <TbCircle1Filled />;
- break;
- case '2':
- children = <TbCircle2Filled />;
- break;
- case '3':
- children = <TbCircle3Filled />;
- break;
- case '4':
- children = <TbCircle4Filled />;
- break;
- case '5':
- children = <TbCircle5Filled />;
- break;
- case '6':
- children = <TbCircle6Filled />;
- break;
- case '7':
- children = <TbCircle7Filled />;
- break;
- case '8':
- children = <TbCircle8Filled />;
- break;
- case '9':
- children = <TbCircle9Filled />;
- break;
- default:
- break;
- }
- }
- // console.log(linkType)
- const style = {
- color: 'lightblue',
- verticalAlign: linkType === 'citation' ? 'super' : 'baseline',
- fontSize: linkType === 'citation' ? 'smaller' : 'inherit',
- };
-
- return (
- <a
+const LinkRendererWrapper = (goToLinkedDoc: (url: string) => void, showModal: () => void, setCurrentFile: (file: { url: string }) => void) =>
+ function LinkRenderer({ href, children }: { href?: string; children?: React.ReactNode }) {
+ const Children = TbCircles[Number(children)]; // pascal case variable needed to convert IconType to JSX.Element tag
+ const [, aurl, linkType] = href?.match(/([a-zA-Z0-9_.!-]+)~~~(citation|file_path)/) ?? [undefined, href, null];
+ const renderType = (content: JSX.Element | null, click: (url: string) => void):JSX.Element => (
+ // eslint-disable-next-line jsx-a11y/anchor-is-valid
+ <a className={`MessageComponent-${linkType}`}
href="#"
onClick={e => {
e.preventDefault();
- if (linkType === 'citation') {
- goToLinkedDoc(url);
- } else if (linkType === 'file_path') {
- showModal();
- setCurrentFile({ url });
- }
- }}
- style={style}>
- {children}
- </a>
- );
+ aurl && click(aurl);
+ }}>
+ {content}
+ </a>
+ ); // prettier-ignore
+ switch (linkType) {
+ case 'citation': return renderType(<Children />, (url: string) => goToLinkedDoc(url));
+ case 'file_path': return renderType(null, (url: string) => { showModal(); setCurrentFile({ url }); });
+ default: return null;
+ } // prettier-ignore
};
+const MessageComponent: React.FC<MessageComponentProps> = function ({ message, toggleToolLogs, expandedLogIndex, goToLinkedDoc, index, showModal, setCurrentFile, isCurrent = false }) {
+ // const messageClass = `${message.role} ${isCurrent ? 'current-message' : ''}`;
return (
<div className={`message ${message.role}`}>
<MathJaxContext>
<MathJax dynamic hideUntilTypeset="every">
- <ReactMarkdown components={{ a: LinkRenderer }}>{message.text ? message.text : ''}</ReactMarkdown>
+ <ReactMarkdown components={{ a: LinkRendererWrapper(goToLinkedDoc, showModal, setCurrentFile) }}>{message.text}</ReactMarkdown>
</MathJax>
</MathJaxContext>
{message.image && <img src={message.image} alt="" />}
<div className="message-footer">
{message.tool_logs && (
- <button className="toggle-logs-button" onClick={() => toggleToolLogs(index)}>
+ <button type="button" className="toggle-logs-button" onClick={() => toggleToolLogs(index)}>
{expandedLogIndex === index ? 'Hide Code Interpreter Logs' : 'Show Code Interpreter Logs'}
</button>
)}
diff --git a/src/client/views/nodes/ComparisonBox.scss b/src/client/views/nodes/ComparisonBox.scss
index 093b9c004..08d9e6010 100644
--- a/src/client/views/nodes/ComparisonBox.scss
+++ b/src/client/views/nodes/ComparisonBox.scss
@@ -49,7 +49,6 @@
left: 0;
height: 100%;
overflow: hidden;
- transition: 200ms;
.beforeBox-cont {
height: 100%;
@@ -63,7 +62,6 @@
width: 3px;
display: inline-block;
background: white;
- transition: 200ms;
.slide-handle {
position: absolute;
diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx
index adb380f12..efaf6807a 100644
--- a/src/client/views/nodes/ComparisonBox.tsx
+++ b/src/client/views/nodes/ComparisonBox.tsx
@@ -30,41 +30,23 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
return FieldView.LayoutString(ComparisonBox, fieldKey);
}
private _disposers: (DragManager.DragDropDisposer | undefined)[] = [undefined, undefined];
+ private _closeRef = React.createRef<HTMLDivElement>();
+ @observable _inputValue = '';
+ @observable _outputValue = '';
+ @observable _loading = false;
+ @observable _errorMessage = '';
+ @observable _outputMessage = '';
+ @observable _animating = '';
+
constructor(props: FieldViewProps) {
super(props);
makeObservable(this);
}
- @observable inputValue = '';
- @observable outputValue = '';
- @observable loading = false;
- @observable errorMessage = '';
- @observable outputMessage = '';
-
- @action handleInputChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
- this.inputValue = e.target.value;
- console.log(this.inputValue);
- };
-
- @observable _animating = '';
-
- @computed get clipWidth() {
- return NumCast(this.layoutDoc[this.clipWidthKey], 50);
- }
- get clipWidthKey() {
- return '_' + this._props.fieldKey + '_clipWidth';
- }
-
- @computed get clipHeight() {
- return NumCast(this.layoutDoc[this.clipHeightKey], 200);
- }
- get clipHeightKey() {
- return '_' + this._props.fieldKey + '_clipHeight';
- }
-
componentDidMount() {
this._props.setContentViewBox?.(this);
}
+
protected createDropTarget = (ele: HTMLDivElement | null, fieldKey: string, disposerId: number) => {
this._disposers[disposerId]?.();
if (ele) {
@@ -72,7 +54,19 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
}
};
- private internalDrop = undoable((e: Event, dropEvent: DragManager.DropEvent, fieldKey: string) => {
+ @computed get revealOp() { return this.layoutDoc[`_${this.fieldKey}_revealOp`] as ('flip'|'hover'|undefined); } // prettier-ignore
+ @computed get clipWidth() { return NumCast(this.layoutDoc[`_${this.fieldKey}_clipWidth`], 50); } // prettier-ignore
+ set clipWidth(width: number) { this.layoutDoc[`_${this.fieldKey}_clipWidth`] = width; } // prettier-ignore
+ @computed get useAlternate() { return this.layoutDoc[`_${this.fieldKey}_usePath`] === 'alternate'; } // prettier-ignore
+ set useAlternate(alt: boolean) { this.layoutDoc[`_${this.fieldKey}_usePath`] = alt ? 'alternate' : undefined; } // prettier-ignore
+
+ animateClipWidth = action((clipWidth: number, duration = 200 /* ms */) => {
+ this._animating = `all ${duration}ms`; // turn on clip animation transition, then turn it off at end of animation
+ setTimeout(action(() => { this._animating = ''; }), duration); // prettier-ignore
+ this.clipWidth = clipWidth;
+ });
+
+ internalDrop = undoable((e: Event, dropEvent: DragManager.DropEvent, fieldKey: string) => {
if (dropEvent.complete.docDragData) {
const { droppedDocuments } = dropEvent.complete.docDragData;
const added = dropEvent.complete.docDragData.moveDocument?.(droppedDocuments, this.Document, (doc: Doc | Doc[]) => this.addDoc(toList(doc).lastElement(), fieldKey));
@@ -84,45 +78,34 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
return undefined;
}, 'internal drop');
- private registerSliding = (e: React.PointerEvent<HTMLDivElement>, targetWidth: number) => {
+ registerSliding = (e: React.PointerEvent<HTMLDivElement>, targetWidth: number) => {
if (e.button !== 2) {
setupMoveUpEvents(
this,
e,
this.onPointerMove,
emptyFunction,
- action((moveEv, doubleTap) => {
+ action((clickEv, doubleTap) => {
if (doubleTap) {
this._isAnyChildContentActive = true;
if (!this.dataDoc[this.fieldKey + '_1'] && !this.dataDoc[this.fieldKey]) this.dataDoc[this.fieldKey + '_1'] = DocUtils.copyDragFactory(Doc.UserDoc().emptyNote as Doc);
if (!this.dataDoc[this.fieldKey + '_2'] && !this.dataDoc[this.fieldKey + '_alternate']) this.dataDoc[this.fieldKey + '_2'] = DocUtils.copyDragFactory(Doc.UserDoc().emptyNote as Doc);
+ // DocumentView.addViewRenderedCb(DocCast(this.dataDoc[this.fieldKey + '_1']), dv => {
+ // dv?.select(false);
+ // });
}
}),
- false,
+ true,
undefined,
- action(() => {
- if (this._isAnyChildContentActive) return;
- this._animating = 'all 200ms';
- // on click, animate slider movement to the targetWidth
- this.layoutDoc[this.clipWidthKey] = (targetWidth * 100) / this._props.PanelWidth();
- // this.layoutDoc[this.clipHeightKey] = (targetWidth * 100) / this._props.PanelHeight();
-
- setTimeout(
- action(() => {
- this._animating = '';
- }),
- 200
- );
- })
+ () => !this._isAnyChildContentActive && this.animateClipWidth((targetWidth * 100) / this._props.PanelWidth())
);
}
};
- @action
- private onPointerMove = ({ movementX }: PointerEvent) => {
+ onPointerMove = ({ movementX }: PointerEvent) => {
const width = movementX * this.ScreenToLocalBoxXf().Scale + (this.clipWidth / 100) * this._props.PanelWidth();
- if (width && width > 5 && width < this._props.PanelWidth()) {
- this.layoutDoc[this.clipWidthKey] = (width * 100) / this._props.PanelWidth();
+ if (width > 5 && width < this._props.PanelWidth()) {
+ this.clipWidth = (width * 100) / this._props.PanelWidth();
}
return false;
};
@@ -180,8 +163,10 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
);
};
docStyleProvider = (doc: Opt<Doc>, props: Opt<FieldViewProps>, property: string): any => {
- if (property === StyleProp.PointerEvents) return 'none';
- return this._props.styleProvider?.(doc, props, property);
+ switch (property) {
+ case StyleProp.PointerEvents: return 'none';
+ default: return this._props.styleProvider?.(doc, props, property);
+ } // prettier-ignore
};
moveDoc1 = (docs: Doc | Doc[], targetCol: Doc | undefined, addDoc: any) => toList(docs).reduce((res, doc: Doc) => res && this.moveDoc(doc, addDoc, this.fieldKey + '_1'), true);
moveDoc2 = (docs: Doc | Doc[], targetCol: Doc | undefined, addDoc: any) => toList(docs).reduce((res, doc: Doc) => res && this.moveDoc(doc, addDoc, this.fieldKey + '_2'), true);
@@ -220,36 +205,31 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
return layoutTemplateString;
};
- _closeRef = React.createRef<HTMLDivElement>();
-
/**
* Flips a flashcard to the alternate side for the user to view.
*/
flipFlashcard = () => {
- const usePath = this.layoutDoc[`_${this._props.fieldKey}_usePath`];
- this.layoutDoc[`_${this._props.fieldKey}_usePath`] = usePath === undefined ? 'alternate' : undefined;
+ this.useAlternate = !this.useAlternate;
};
/**
* Changes the view option to hover for a flashcard.
*/
- hoverFlip = (side: string | undefined) => {
- if (this.layoutDoc[`_${this._props.fieldKey}_revealOp`] === 'hover') this.layoutDoc[`_${this._props.fieldKey}_usePath`] = side;
+ hoverFlip = (alternate: boolean) => {
+ if (this.revealOp === 'hover') this.useAlternate = alternate;
};
/**
* Creates the button used to flip the flashcards.
*/
@computed get overlayAlternateIcon() {
- const usepath = this.layoutDoc[`_${this._props.fieldKey}_usePath`];
return (
<Tooltip title={<div className="dash-tooltip">flip</div>}>
<div
className="formattedTextBox-alternateButton"
onPointerDown={e =>
setupMoveUpEvents(e.target, e, returnFalse, emptyFunction, () => {
- console.log(this.layoutDoc[`_${this._props.fieldKey}_revealOp`]);
- if (!this.layoutDoc[`_${this._props.fieldKey}_revealOp`] || this.layoutDoc[`_${this._props.fieldKey}_revealOp`] === 'flip') {
+ if (!this.revealOp || this.revealOp === 'flip') {
this.flipFlashcard();
console.log('Print Front of cards: ' + (RTFCast(DocCast(this.dataDoc[this.fieldKey + '_0']).text)?.Text ?? ''));
console.log('Print Back of cards: ' + (RTFCast(DocCast(this.dataDoc[this.fieldKey + '_1']).text)?.Text ?? ''));
@@ -257,8 +237,8 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
})
}
style={{
- background: usepath === 'alternate' ? 'white' : 'black',
- color: usepath === 'alternate' ? 'black' : 'white',
+ background: this.useAlternate ? 'white' : 'black',
+ color: this.useAlternate ? 'black' : 'white',
}}>
<FontAwesomeIcon icon="turn-up" size="sm" />
</div>
@@ -268,14 +248,14 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
@action handleRenderGPTClick = () => {
// Call the GPT model and get the output
- this.layoutDoc[`_${this._props.fieldKey}_usePath`] = 'alternate';
- this.outputValue = '';
- if (this.inputValue) this.askGPT();
+ this.useAlternate = true;
+ this._outputValue = '';
+ if (this._inputValue) this.askGPT();
};
@action handleRenderClick = () => {
// Call the GPT model and get the output
- this.layoutDoc[`_${this._props.fieldKey}_usePath`] = undefined;
+ this.useAlternate = false;
};
/**
@@ -285,7 +265,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
askGPT = async (): Promise<string | undefined> => {
const questionText = 'Question: ' + StrCast(RTFCast(DocCast(this.dataDoc[this.fieldKey + '_1']).text)?.Text);
const rubricText = ' Rubric: ' + StrCast(RTFCast(DocCast(this.dataDoc[this.fieldKey + '_0']).text)?.Text);
- const queryText = questionText + ' UserAnswer: ' + this.inputValue + '. ' + rubricText;
+ const queryText = questionText + ' UserAnswer: ' + this._inputValue + '. ' + rubricText;
try {
const res = await gptAPICall(queryText, GPTCallType.QUIZ);
@@ -293,8 +273,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
console.error('GPT call failed');
return;
}
- this.outputValue = res;
- console.log(res);
+ this._outputValue = res;
} catch (err) {
console.error('GPT call failed');
}
@@ -355,7 +334,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
);
if (this.Document._layout_isFlashcard) {
- const side = this.layoutDoc[`_${this._props.fieldKey}_usePath`] === 'alternate' ? 1 : 0;
+ const side = this.useAlternate ? 1 : 0;
// add text box to each side when comparison box is first created
if (!(this.dataDoc[this.fieldKey + '_0'] || this.dataDoc[this.fieldKey + '_0'] === 'empty')) {
@@ -383,17 +362,19 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
{/* {StrCast(RTFCast(DocCast(this.dataDoc[this.fieldKey + '_1']).text)?.Text)} */}
<div className="input-box">
<textarea
- value={this.layoutDoc[`_${this._props.fieldKey}_usePath`] === 'alternate' ? this.outputValue : this.inputValue}
- onChange={this.handleInputChange}
- readOnly={this.layoutDoc[`_${this._props.fieldKey}_usePath`] === 'alternate'}
+ value={this.useAlternate ? this._outputValue : this._inputValue}
+ onChange={action(e => {
+ this._inputValue = e.target.value;
+ })}
+ readOnly={this.useAlternate}
/>
</div>
- <div className="submit-button" style={{ display: this.layoutDoc[`_${this._props.fieldKey}_usePath`] === 'alternate' ? 'none' : 'flex' }}>
+ <div className="submit-button" style={{ display: this.useAlternate ? 'none' : 'flex' }}>
<button type="button" onClick={this.handleRenderGPTClick}>
Submit
</button>
</div>
- <div className="submit-button" style={{ display: this.layoutDoc[`_${this._props.fieldKey}_usePath`] === 'alternate' ? 'flex' : 'none' }}>
+ <div className="submit-button" style={{ display: this.useAlternate ? 'flex' : 'none' }}>
<button type="button" onClick={this.handleRenderClick}>
Edit Your Response
</button>
@@ -407,12 +388,8 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
<div
className={`comparisonBox${this._props.isContentActive() ? '-interactive' : ''}`} /* change className to easily disable/enable pointer events in CSS */
style={{ display: 'flex', flexDirection: 'column' }}
- onMouseEnter={() => {
- this.hoverFlip('alternate');
- }}
- onMouseLeave={() => {
- this.hoverFlip(undefined);
- }}>
+ onMouseEnter={() => this.hoverFlip(true)}
+ onMouseLeave={() => this.hoverFlip(false)}>
{displayBox(`${this.fieldKey}_${side === 0 ? 1 : 0}`, side, this._props.PanelWidth() - 3)}
{this.overlayAlternateIcon}
</div>
@@ -430,9 +407,10 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
className="slide-bar"
style={{
left: `calc(${this.clipWidth + '%'} - 0.5px)`,
+ transition: this._animating,
cursor: this.clipWidth < 5 ? 'e-resize' : this.clipWidth / 100 > (this._props.PanelWidth() - 5) / this._props.PanelWidth() ? 'w-resize' : undefined,
}}
- onPointerDown={e => !this._isAnyChildContentActive && this.registerSliding(e, this._props.PanelWidth() / 2)} /* if clicked, return slide-bar to center */
+ onPointerDown={e => this.registerSliding(e, this._props.PanelWidth() / 2)} /* if clicked, return slide-bar to center */
>
<div className="slide-handle" />
</div>
diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
index ffb668b03..8317d62a6 100644
--- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
@@ -192,7 +192,7 @@ export class FontIconBox extends ViewBoxBaseComponent<ButtonProps>() {
} else {
text = script?.script.run({ this: this.Document, value: '', _readOnly_: true }).result;
// text = StrCast((RichTextMenu.Instance?.TextView?.EditorView ? RichTextMenu.Instance : Doc.UserDoc()).fontFamily);
- getStyle = (val: string) => ({ fontFamily: val });
+ // getStyle = (val: string) => ({ fontFamily: val });
}
// Get items to place into the list
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 7a89b143b..8db68ddfe 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -655,9 +655,11 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
);
else {
if (!PDFBox.pdfpromise.get(href)) PDFBox.pdfpromise.set(href, Pdfjs.getDocument(href).promise);
- PDFBox.pdfpromise.get(href)?.then((pdf: any) => {
- PDFBox.pdfcache.set(href, (this._pdf = pdf));
- });
+ PDFBox.pdfpromise.get(href)?.then(
+ action((pdf: any) => {
+ PDFBox.pdfcache.set(href, (this._pdf = pdf));
+ })
+ );
}
}
return pdfView ?? this.renderTitleBox;
diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx
index 2f6824466..96f025ca0 100644
--- a/src/client/views/pdf/AnchorMenu.tsx
+++ b/src/client/views/pdf/AnchorMenu.tsx
@@ -133,7 +133,7 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
_layout_autoHeight: true,
});
- this.addToCollection?.(newCol);
+ this.addToCollection?.(newCol); //this._props.addDocument(newCol)
};
pointerDown = (e: React.PointerEvent) => {
diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.scss b/src/client/views/pdf/GPTPopup/GPTPopup.scss
index 6d8793f82..042b1dea5 100644
--- a/src/client/views/pdf/GPTPopup/GPTPopup.scss
+++ b/src/client/views/pdf/GPTPopup/GPTPopup.scss
@@ -84,6 +84,7 @@ $highlightedText: #82e0ff;
font-size: 9px;
padding: 10px;
color: #ffffff;
+ width: 100%;
background-color: $button;
border-radius: 5px;
}
diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.tsx b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
index cb5aad32d..5a5e80dc4 100644
--- a/src/client/views/pdf/GPTPopup/GPTPopup.tsx
+++ b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
@@ -20,15 +20,19 @@ import './GPTPopup.scss';
import { SettingsManager } from '../../../util/SettingsManager';
import { SnappingManager } from '../../../util/SnappingManager';
+
export enum GPTPopupMode {
SUMMARY,
EDIT,
IMAGE,
FLASHCARD,
DATA,
+ CARD,
SORT,
+ QUIZ
}
+
interface GPTPopupProps {}
@observer
@@ -149,6 +153,28 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> {
this.cardsDoneLoading = done;
}
+ @observable sortRespText: string = ''
+
+ @action setSortRespText(resp: string) {
+ this.sortRespText = resp
+ }
+
+
+ @observable chatSortPrompt: string = ""
+
+ sortPromptChanged = action((e: React.ChangeEvent<HTMLInputElement>) => {
+ this.chatSortPrompt = e.target.value;
+ });
+
+ @observable private regenerateCallback: (() => Promise<void>) | null = null;
+
+ @action public setRegenerateCallback(callback: () => Promise<void>) {
+ this.regenerateCallback = callback;
+ }
+
+
+
+
public addDoc: (doc: Doc | Doc[], sidebarKey?: string | undefined) => boolean = () => false;
public createFilteredDoc: (axes?: any) => boolean = () => false;
public addToCollection: ((doc: Doc | Doc[], annotationKey?: string | undefined) => boolean) | undefined;
@@ -157,24 +183,38 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> {
* Sorts cards in the CollectionCardDeckView
*/
generateSort = async () => {
+ console.log(this.chatSortPrompt + "USER PROMPT")
this.setLoading(true);
this.setSortDone(false);
+ if (this.regenerateCallback) {
+ await this.regenerateCallback();
+ }
+
try {
- const res = await gptAPICall(this.sortDesc, GPTCallType.SORT);
+ const res = await gptAPICall(this.sortDesc, GPTCallType.SORT, this.chatSortPrompt);
// Trigger the callback with the result
if (this.onSortComplete) {
this.onSortComplete(res || 'Something went wrong :(');
+
+ // Extract explanation surrounded by ------ at the top or both at the top and bottom
+ const explanationMatch = res.match(/------\s*([\s\S]*?)\s*(?:------|$)/) || [];
+ const explanation = explanationMatch[1] ? explanationMatch[1].trim() : 'No explanation found';
+
+ // Set the extracted explanation to sortRespText
+ this.setSortRespText(explanation);
+
console.log(res);
}
} catch (err) {
console.error(err);
}
-
+
this.setLoading(false);
this.setSortDone(true);
};
-
+
+
/**
* Generates a Dalle image and uploads it to the server.
*/
@@ -315,58 +355,75 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> {
};
sortBox = () => (
- <>
- <div>
- {this.heading('SORTING')}
- {this.loading ? (
+ <>
+ <div>
+ {this.heading('SORTING')}
+ <>
+ {!this.cardsDoneLoading || this.loading ? (
<div className="content-wrapper">
<div className="loading-spinner">
<ReactLoading type="spin" color={StrCast(Doc.UserDoc().userVariantColor)} height={30} width={30} />
- <span>Loading...</span>
+ {this.loading ? <span>Loading...</span> : <span>Reading Cards...</span>}
</div>
</div>
) : (
- <>
- {!this.cardsDoneLoading ? (
- <div className="content-wrapper">
- <div className="loading-spinner">
- <ReactLoading type="spin" color={StrCast(Doc.UserDoc().userVariantColor)} height={30} width={30} />
- <span>Reading Cards...</span>
- </div>
+ !this.sortDone && (
+ <>
+ <div className="btns-wrapper-gpt">
+ <input
+ className="searchBox-input"
+ defaultValue=""
+ autoComplete="off"
+ onChange={this.sortPromptChanged}
+ onKeyDown={e => {
+ if (e.key === 'Enter') {
+ this.generateSort();
+ }
+ e.stopPropagation();
+ }}
+ type="text"
+ placeholder="How do you want to sort your cards ?"
+ id="search-input"
+ style={{ width: '100%' }}
+ />
</div>
- ) : (
- !this.sortDone && (
- <div className="btns-wrapper-gpt">
- <Button
- tooltip="Have ChatGPT sort your cards for you!"
- text="Sort!"
- onClick={this.generateSort}
- color={StrCast(Doc.UserDoc().userVariantColor)}
- type={Type.TERT}
- style={{
- width: '90%', // Almost as wide as the container
- textAlign: 'center',
- color: '#ffffff', // White text
- fontSize: '16px', // Adjust font size as needed
- }}
- />
- </div>
- )
- )}
-
- {this.sortDone && (
- <div>
- <div className="content-wrapper">
- <p>{this.text === 'Something went wrong :(' ? 'Something went wrong :(' : 'Sorting done! Feel free to move things around / regenerate :) !'}</p>
- <IconButton tooltip="Generate Again" onClick={() => this.setSortDone(false)} icon={<FontAwesomeIcon icon="redo-alt" size="lg" />} color={StrCast(Doc.UserDoc().userVariantColor)} />
- </div>
+ <div className="btns-wrapper-gpt">
+ <Button
+ tooltip="Have ChatGPT sort your cards for you!"
+ text="Sort!"
+ onClick={this.generateSort}
+ color={StrCast(Doc.UserDoc().userVariantColor)}
+ type={Type.TERT}
+ style={{
+ width: '90%',
+ textAlign: 'center',
+ color: '#ffffff',
+ fontSize: '16px',
+ }}
+ />
</div>
- )}
- </>
+ </>
+ )
)}
- </div>
- </>
- );
+
+ {this.sortDone && (
+ <div>
+ <div className="content-wrapper">
+ <p>{this.text === 'Something went wrong :(' ? 'Something went wrong :(' : `${this.sortRespText}`}</p>
+ <IconButton
+ tooltip="Generate Again"
+ onClick={() => this.setSortDone(false)}
+ icon={<FontAwesomeIcon icon="redo-alt" size="lg" />}
+ color={StrCast(Doc.UserDoc().userVariantColor)}
+ />
+ </div>
+ </div>
+ )}
+ </>
+ </div>
+ </>
+);
+
imageBox = () => (
<div style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
{this.heading('GENERATED IMAGE')}
@@ -518,9 +575,12 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> {
render() {
return (
+
<div className="summary-box" style={{ display: this.visible ? 'flex' : 'none' }}>
{this.mode === GPTPopupMode.SUMMARY ? this.summaryBox() : this.mode === GPTPopupMode.DATA ? this.dataAnalysisBox() : this.mode === GPTPopupMode.IMAGE ? this.imageBox() : this.mode === GPTPopupMode.SORT ? this.sortBox() : null}
</div>
+
+
);
}
}
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 6c1617c38..72448e851 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -24,13 +24,11 @@ import { FieldViewProps } from '../nodes/FieldView';
import { FocusViewOptions } from '../nodes/FocusViewOptions';
import { LinkInfo } from '../nodes/LinkDocPreview';
import { PDFBox } from '../nodes/PDFBox';
-import { ComparisonBox } from '../nodes/ComparisonBox';
import { ObservableReactComponent } from '../ObservableReactComponent';
import { StyleProp } from '../StyleProp';
import { AnchorMenu } from './AnchorMenu';
import { Annotation } from './Annotation';
import { GPTPopup } from './GPTPopup/GPTPopup';
-import { Docs } from '../../documents/Documents';
import './PDFViewer.scss';
// pdfjsLib.GlobalWorkerOptions.workerSrc = `/assets/pdf.worker.js`;