From 606088e419f0e146715244d00840349b587c80ba Mon Sep 17 00:00:00 2001
From: bobzel
Date: Sun, 17 Mar 2024 11:50:15 -0400
Subject: use metakey to edit computedfield result instead of expression in
schema cell, set default new field values on data doc. fixed stacking view
from autoresizing when switching to a different collection view. changed
syntax for setting fields in text docs to use ':=' for computed fields.
Added call to Chat in computed functions when (( )) is used. Added caching
of computed function result when a function called by ComputedField uses the
_setCacheResult_ method (currently only gptCallChat).
---
src/client/views/nodes/KeyValueBox.tsx | 65 +++++++++++++++++++++-------------
1 file changed, 41 insertions(+), 24 deletions(-)
(limited to 'src/client/views/nodes/KeyValueBox.tsx')
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index 89a5ac0b8..2257e6455 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -6,7 +6,7 @@ import { Doc, Field, FieldResult } from '../../../fields/Doc';
import { List } from '../../../fields/List';
import { RichTextField } from '../../../fields/RichTextField';
import { ComputedField, ScriptField } from '../../../fields/ScriptField';
-import { DocCast } from '../../../fields/Types';
+import { DocCast, StrCast } from '../../../fields/Types';
import { ImageField } from '../../../fields/URLField';
import { Docs } from '../../documents/Documents';
import { SetupDrag } from '../../util/DragManager';
@@ -71,34 +71,51 @@ export class KeyValueBox extends ObservableReactComponent {
}
}
};
- public static CompileKVPScript(value: string): KVPScript | undefined {
- const eq = value.startsWith('=');
- value = eq ? value.substring(1) : value;
- const dubEq = value.startsWith(':=') ? 'computed' : value.startsWith('$=') ? 'script' : false;
- value = dubEq ? value.substring(2) : value;
- const options: ScriptOptions = { addReturn: true, typecheck: false, params: { this: Doc.name, self: Doc.name, documentView: 'any', _last_: 'any', _readOnly_: 'boolean' }, editable: true };
- if (dubEq) options.typecheck = false;
- const script = CompileScript(value, { ...options, transformer: DocumentIconContainer.getTransformer() });
- return !script.compiled ? undefined : { script, type: dubEq, onDelegate: eq };
+ /**
+ * this compiles a string as a script after parsing off initial characters that determine script parameters
+ * if the script starts with '=', then it will be stored on the delegate of the Doc, otherise on the data doc
+ * if the script then starts with a ':=', then it will be treated as ComputedField,
+ * '$=', then it will just be a Script
+ * @param value
+ * @returns
+ */
+ public static CompileKVPScript(rawvalue: string): KVPScript | undefined {
+ const onDelegate = rawvalue.startsWith('=');
+ rawvalue = onDelegate ? rawvalue.substring(1) : rawvalue;
+ const type: 'computed' | 'script' | false = rawvalue.startsWith(':=') ? 'computed' : rawvalue.startsWith('$=') ? 'script' : false;
+ rawvalue = type ? rawvalue.substring(2) : rawvalue;
+ rawvalue = rawvalue.replace(/.*\(\((.*)\)\)/, 'dashCallChat(_setCacheResult_, this, "$1")');
+ const value = ["'", '"', '`'].includes(rawvalue.length ? rawvalue[0] : '') || !isNaN(rawvalue as any) ? rawvalue : '`' + rawvalue + '`';
+
+ var script = ScriptField.CompileScript(rawvalue, {}, true, undefined, DocumentIconContainer.getTransformer());
+ if (!script.compiled) {
+ script = ScriptField.CompileScript(value, {}, true, undefined, DocumentIconContainer.getTransformer());
+ }
+ return !script.compiled ? undefined : { script, type, onDelegate };
}
- public static ApplyKVPScript(doc: Doc, key: string, kvpScript: KVPScript, forceOnDelegate?: boolean): boolean {
+ public static ApplyKVPScript(doc: Doc, key: string, kvpScript: KVPScript, forceOnDelegate?: boolean, setResult?: (value: FieldResult) => void) {
const { script, type, onDelegate } = kvpScript;
//const target = onDelegate ? Doc.Layout(doc.layout) : Doc.GetProto(doc); // bcz: TODO need to be able to set fields on layout templates
const target = forceOnDelegate || onDelegate || key.startsWith('_') ? doc : DocCast(doc.proto, doc);
- let field: Field;
- if (type === 'computed') {
- field = new ComputedField(script);
- } else if (type === 'script') {
- field = new ScriptField(script);
- } else {
- const res = script.run({ this: Doc.Layout(doc), self: doc }, console.log);
- if (!res.success) {
- target[key] = script.originalScript;
- return true;
+ let field: Field | undefined;
+ switch (type) {
+ case 'computed': field = new ComputedField(script); break; // prettier-ignore
+ case 'script': field = new ScriptField(script); break; // prettier-ignore
+ default: {
+ const _setCacheResult_ = (value: FieldResult) => {
+ field = value as Field;
+ setResult?.(value);
+ };
+ const res = script.run({ this: Doc.Layout(doc), self: doc, _setCacheResult_ }, console.log);
+ if (!res.success) {
+ if (key) target[key] = script.originalScript;
+ return false;
+ }
+ field === undefined && (field = res.result);
}
- field = res.result;
}
+ if (!key) return field;
if (Field.IsField(field, true) && (key !== 'proto' || field !== target)) {
target[key] = field;
return true;
@@ -107,10 +124,10 @@ export class KeyValueBox extends ObservableReactComponent {
}
@undoBatch
- public static SetField(doc: Doc, key: string, value: string, forceOnDelegate?: boolean) {
+ public static SetField(doc: Doc, key: string, value: string, forceOnDelegate?: boolean, setResult?: (value: FieldResult) => void) {
const script = this.CompileKVPScript(value);
if (!script) return false;
- return this.ApplyKVPScript(doc, key, script, forceOnDelegate);
+ return this.ApplyKVPScript(doc, key, script, forceOnDelegate, setResult);
}
onPointerDown = (e: React.PointerEvent): void => {
--
cgit v1.2.3-70-g09d2
From 625c3a67fbcbfc93f1f1b35aed10b9fe7f33dfa9 Mon Sep 17 00:00:00 2001
From: bobzel
Date: Sun, 17 Mar 2024 13:50:45 -0400
Subject: fixed displaying of chat gpt query to use (( )) notation. allow '"'s
in chat gpt calls by escaping with "`" intead.
---
src/client/views/nodes/KeyValueBox.tsx | 6 +++---
src/fields/Doc.ts | 2 +-
2 files changed, 4 insertions(+), 4 deletions(-)
(limited to 'src/client/views/nodes/KeyValueBox.tsx')
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index 2257e6455..2bcad806f 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -6,11 +6,11 @@ import { Doc, Field, FieldResult } from '../../../fields/Doc';
import { List } from '../../../fields/List';
import { RichTextField } from '../../../fields/RichTextField';
import { ComputedField, ScriptField } from '../../../fields/ScriptField';
-import { DocCast, StrCast } from '../../../fields/Types';
+import { DocCast } from '../../../fields/Types';
import { ImageField } from '../../../fields/URLField';
import { Docs } from '../../documents/Documents';
import { SetupDrag } from '../../util/DragManager';
-import { CompileScript, CompiledScript, ScriptOptions } from '../../util/Scripting';
+import { CompiledScript } from '../../util/Scripting';
import { undoBatch } from '../../util/UndoManager';
import { ContextMenu } from '../ContextMenu';
import { ContextMenuProps } from '../ContextMenuItem';
@@ -84,7 +84,7 @@ export class KeyValueBox extends ObservableReactComponent {
rawvalue = onDelegate ? rawvalue.substring(1) : rawvalue;
const type: 'computed' | 'script' | false = rawvalue.startsWith(':=') ? 'computed' : rawvalue.startsWith('$=') ? 'script' : false;
rawvalue = type ? rawvalue.substring(2) : rawvalue;
- rawvalue = rawvalue.replace(/.*\(\((.*)\)\)/, 'dashCallChat(_setCacheResult_, this, "$1")');
+ rawvalue = rawvalue.replace(/.*\(\((.*)\)\)/, 'dashCallChat(_setCacheResult_, this, `$1`)');
const value = ["'", '"', '`'].includes(rawvalue.length ? rawvalue[0] : '') || !isNaN(rawvalue as any) ? rawvalue : '`' + rawvalue + '`';
var script = ScriptField.CompileScript(rawvalue, {}, true, undefined, DocumentIconContainer.getTransformer());
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 30f5f716c..daae32e9f 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -53,7 +53,7 @@ export namespace Field {
(field instanceof ComputedField && showComputedValue
? field._lastComputedResult
: field instanceof ComputedField
- ? `:=${field.script.originalScript}`
+ ? `:=${field.script.originalScript.replace(/dashCallChat\(_setCacheResult_, this, `(.*)`\)/, '(($1))')}`
: field instanceof ScriptField
? `$=${field.script.originalScript}`
: Field.toScriptString(field));
--
cgit v1.2.3-70-g09d2
From a974aa4e6573c8becf93f78610406747fec14c1c Mon Sep 17 00:00:00 2001
From: bobzel
Date: Tue, 19 Mar 2024 17:08:46 -0400
Subject: cleaned up user templates to not get changed on reload. made setting
a template add it to the template tools list and as a tools button. fixed
linking to parts of a template. fixed disappearing templates caused by
stacking view set a field with an empty key. updated field assignment
syntax in trees, dash field views, and key value box to all use :,:=,=,=:=
syntax. added text elide button. added @(title) syntax for hyperlinking.
made using a template both inherit from the template to get default values
and use the template to render. fixed submenu placement of context menu.
updated RTF markdown doc.
---
src/client/util/CurrentUserUtils.ts | 19 +++--
src/client/util/DropConverter.ts | 45 +++++-----
src/client/util/LinkManager.ts | 13 +--
src/client/util/RTFMarkup.tsx | 30 +++----
src/client/views/ContextMenuItem.tsx | 2 +-
src/client/views/TemplateMenu.scss | 1 +
src/client/views/TemplateMenu.tsx | 7 +-
.../collections/CollectionMasonryViewFieldRow.tsx | 10 +--
.../CollectionStackingViewFieldColumn.tsx | 6 +-
src/client/views/collections/TreeView.tsx | 14 +++-
src/client/views/global/globalScripts.ts | 33 +++-----
src/client/views/nodes/ComparisonBox.tsx | 71 +++++++++-------
src/client/views/nodes/DocumentView.tsx | 32 ++++++-
src/client/views/nodes/FieldView.tsx | 1 +
src/client/views/nodes/FontIconBox/FontIconBox.tsx | 22 ++---
src/client/views/nodes/KeyValueBox.tsx | 2 +-
src/client/views/nodes/KeyValuePair.tsx | 2 +-
.../views/nodes/formattedText/DashFieldView.tsx | 57 +++++++++----
.../views/nodes/formattedText/FormattedTextBox.tsx | 34 ++++----
.../views/nodes/formattedText/RichTextMenu.tsx | 13 +++
.../views/nodes/formattedText/RichTextRules.ts | 97 ++++++++++++----------
src/client/views/nodes/formattedText/marks_rts.ts | 5 +-
src/client/views/nodes/formattedText/nodes_rts.ts | 2 +
src/fields/Doc.ts | 42 +++++-----
src/fields/util.ts | 4 +
25 files changed, 328 insertions(+), 236 deletions(-)
(limited to 'src/client/views/nodes/KeyValueBox.tsx')
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 84a33500d..bbee18707 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -73,7 +73,7 @@ export class CurrentUserUtils {
};
const reqdScripts = { dropConverter : "convertToButtons(dragData)" };
const reqdFuncs = { /* hidden: "IsNoviceMode()" */ };
- return DocUtils.AssignScripts(DocUtils.AssignOpts(userDocTemplates, reqdOpts, userTemplates) ?? Docs.Create.MasonryDocument(userTemplates, reqdOpts), reqdScripts, reqdFuncs);
+ return DocUtils.AssignScripts(userDocTemplates ?? Docs.Create.MasonryDocument(userTemplates, reqdOpts), reqdScripts, reqdFuncs);
}
/// Initializes templates for editing click funcs of a document
@@ -133,11 +133,19 @@ export class CurrentUserUtils {
const reqdOpts:DocumentOptions = { title: "Note Layouts", _height: 75, isSystem: true };
return DocUtils.AssignOpts(tempNotes, reqdOpts, reqdNoteList) ?? (doc[field] = Docs.Create.TreeDocument(reqdNoteList, reqdOpts));
}
+ static setupUserTemplates(doc: Doc, field="template_user") {
+ const tempUsers = DocCast(doc[field]);
+ const reqdUserList = DocListCast(tempUsers?.data);
+
+ const reqdOpts:DocumentOptions = { title: "User Layouts", _height: 75, isSystem: true };
+ return DocUtils.AssignOpts(tempUsers, reqdOpts, reqdUserList) ?? (doc[field] = Docs.Create.TreeDocument(reqdUserList, reqdOpts));
+ }
/// Initializes collection of templates for notes and click functions
static setupDocTemplates(doc: Doc, field="myTemplates") {
const templates = [
CurrentUserUtils.setupNoteTemplates(doc),
+ CurrentUserUtils.setupUserTemplates(doc),
CurrentUserUtils.setupClickEditorTemplates(doc)
];
CurrentUserUtils.setupChildClickEditors(doc)
@@ -375,9 +383,9 @@ pie title Minerals in my tap water
{ toolTip: "Tap or drag to create a flashcard", title: "Flashcard", icon: "id-card", dragFactory: doc.emptyFlashcard as Doc, clickFactory: DocCast(doc.emptyFlashcard)},
{ toolTip: "Tap or drag to create an equation", title: "Math", icon: "calculator", dragFactory: doc.emptyEquation as Doc, clickFactory: DocCast(doc.emptyEquation)},
{ toolTip: "Tap or drag to create a mermaid node", title: "Mermaids", icon: "rocket", dragFactory: doc.emptyMermaids as Doc, clickFactory: DocCast(doc.emptyMermaids)},
- { toolTip: "Tap or drag to create a plotly node", title: "Plotly", icon: "rocket", dragFactory: doc.emptyPlotly as Doc, clickFactory: DocCast(doc.emptyMermaids)},
+ { toolTip: "Tap or drag to create a plotly node", title: "Plotly", icon: "rocket", dragFactory: doc.emptyPlotly as Doc, clickFactory: DocCast(doc.emptyMermaids)},
{ toolTip: "Tap or drag to create a physics simulation",title: "Simulation", icon: "rocket",dragFactory: doc.emptySimulation as Doc, clickFactory: DocCast(doc.emptySimulation), funcs: { hidden: "IsNoviceMode()"}},
- { toolTip: "Tap or drag to create a note board", title: "Notes", icon: "book", dragFactory: doc.emptyNoteboard as Doc, clickFactory: DocCast(doc.emptyNoteboard)},
+ { toolTip: "Tap or drag to create a note board", title: "Notes", icon: "book", dragFactory: doc.emptyNoteboard as Doc, clickFactory: DocCast(doc.emptyNoteboard)},
{ toolTip: "Tap or drag to create a collection", title: "Col", icon: "folder", dragFactory: doc.emptyCollection as Doc, clickFactory: DocCast(doc.emptyTab)},
{ toolTip: "Tap or drag to create a webpage", title: "Web", icon: "globe-asia", dragFactory: doc.emptyWebpage as Doc, clickFactory: DocCast(doc.emptyWebpage)},
{ toolTip: "Tap or drag to create a comparison box", title: "Compare", icon: "columns", dragFactory: doc.emptyComparison as Doc, clickFactory: DocCast(doc.emptyComparison)},
@@ -385,10 +393,10 @@ pie title Minerals in my tap water
{ toolTip: "Tap or drag to create a map", title: "Map", icon: "map-marker-alt", dragFactory: doc.emptyMap as Doc, clickFactory: DocCast(doc.emptyMap)},
{ toolTip: "Tap or drag to create a screen grabber", title: "Grab", icon: "photo-video", dragFactory: doc.emptyScreengrab as Doc, clickFactory: DocCast(doc.emptyScreengrab), openFactoryLocation: OpenWhere.overlay, funcs: { hidden: "IsNoviceMode()"}},
{ toolTip: "Tap or drag to create a WebCam recorder", title: "WebCam", icon: "photo-video", dragFactory: doc.emptyWebCam as Doc, clickFactory: DocCast(doc.emptyWebCam), openFactoryLocation: OpenWhere.overlay, funcs: { hidden: "IsNoviceMode()"}},
- { toolTip: "Tap or drag to create a button", title: "Button", icon: "circle", dragFactory: doc.emptyButton as Doc, clickFactory: DocCast(doc.emptyButton)},
+ { toolTip: "Tap or drag to create a button", title: "Button", icon: "circle", dragFactory: doc.emptyButton as Doc, clickFactory: DocCast(doc.emptyButton)},
{ toolTip: "Tap or drag to create a scripting box", title: "Script", icon: "terminal", dragFactory: doc.emptyScript as Doc, clickFactory: DocCast(doc.emptyScript), funcs: { hidden: "IsNoviceMode()"}},
{ toolTip: "Tap or drag to create a data viz node", title: "DataViz", icon: "chart-bar", dragFactory: doc.emptyDataViz as Doc, clickFactory: DocCast(doc.emptyDataViz)},
- { toolTip: "Tap or drag to create a bullet slide", title: "PPT Slide", icon: "person-chalkboard", dragFactory: doc.emptySlide as Doc, clickFactory: DocCast(doc.emptySlide), openFactoryLocation: OpenWhere.overlay, funcs: { hidden: "IsNoviceMode()"}},
+ { toolTip: "Tap or drag to create a bullet slide", title: "PPT Slide", icon: "person-chalkboard", dragFactory: doc.emptySlide as Doc, clickFactory: DocCast(doc.emptySlide), openFactoryLocation: OpenWhere.overlay, funcs: { hidden: "IsNoviceMode()"}},
{ toolTip: "Tap or drag to create a view slide", title: "View Slide", icon: "address-card", dragFactory: doc.emptyViewSlide as Doc,clickFactory: DocCast(doc.emptyViewSlide),openFactoryLocation: OpenWhere.overlay,funcs: { hidden: "IsNoviceMode()"}},
{ toolTip: "Tap or drag to create a data note", title: "DataNote", icon: "window-maximize",dragFactory: doc.emptyHeader as Doc,clickFactory: DocCast(doc.emptyHeader), openFactoryAsDelegate: true, funcs: { hidden: "IsNoviceMode()"} },
{ toolTip: "Toggle a Calculator REPL", title: "replviewer", icon: "calculator", clickFactory: ' ' as any, openFactoryLocation: OpenWhere.overlay}, // hack: clickFactory is not a Doc but will get interpreted as a custom UI by the openDoc() onClick script
@@ -738,6 +746,7 @@ pie title Minerals in my tap water
{ title: "Center", toolTip: "Center align (Cmd-\\)",btnType: ButtonType.ToggleButton, icon: "align-center",toolType:"center",ignoreClick: true, scripts: {onClick: '{ return toggleCharStyle(this.toolType, _readOnly_);}'} },
{ title: "Right", toolTip: "Right align (Cmd-])", btnType: ButtonType.ToggleButton, icon: "align-right", toolType:"right", ignoreClick: true, scripts: {onClick: '{ return toggleCharStyle(this.toolType, _readOnly_);}'} },
]},
+ { title: "Elide", toolTip: "Elide selection", btnType: ButtonType.ToggleButton, icon: "eye", toolType:"elide", ignoreClick: true, scripts: {onClick: '{ return toggleCharStyle(this.toolType, _readOnly_);}'}},
{ title: "Dictate", toolTip: "Dictate", btnType: ButtonType.ToggleButton, icon: "microphone", toolType:"dictation", ignoreClick: true, scripts: {onClick: '{ return toggleCharStyle(this.toolType, _readOnly_);}'}},
{ title: "NoLink", toolTip: "Auto Link", btnType: ButtonType.ToggleButton, icon: "link", toolType:"noAutoLink", expertMode:true, scripts: {onClick: '{ return toggleCharStyle(this.toolType, _readOnly_);}'}, funcs: {hidden: 'IsNoviceMode()'}},
// { title: "Strikethrough", tooltip: "Strikethrough", btnType: ButtonType.ToggleButton, icon: "strikethrough", scripts: {onClick:: 'toggleStrikethrough()'}},
diff --git a/src/client/util/DropConverter.ts b/src/client/util/DropConverter.ts
index 3df3e36c6..ed5749d06 100644
--- a/src/client/util/DropConverter.ts
+++ b/src/client/util/DropConverter.ts
@@ -28,6 +28,7 @@ export function MakeTemplate(doc: Doc) {
}
/**
+ *
* Recursively converts 'doc' into a template that can be used to render other documents.
*
* For recurive Docs in the template, their target fieldKey is defined by their title,
@@ -75,32 +76,36 @@ export function convertDropDataToButtons(data: DragManager.DocumentDragData) {
remProps.map(prop => (dbox[prop] = undefined));
}
} else if (!doc.onDragStart && !doc.isButtonBar) {
- const layoutDoc = doc; // doc.layout instanceof Doc && doc.layout.isTemplateForField ? doc.layout : doc;
- if (layoutDoc.type !== DocumentType.FONTICON) {
- !layoutDoc.isTemplateDoc && makeTemplate(layoutDoc);
- }
- layoutDoc.isTemplateDoc = true;
- dbox = Docs.Create.FontIconDocument({
- _nativeWidth: 100,
- _nativeHeight: 100,
- _width: 100,
- _height: 100,
- backgroundColor: StrCast(doc.backgroundColor),
- title: StrCast(layoutDoc.title),
- btnType: ButtonType.ClickButton,
- icon: 'bolt',
- isSystem: false,
- });
- dbox.title = ComputedField.MakeFunction('this.dragFactory.title');
- dbox.dragFactory = layoutDoc;
- dbox.dropPropertiesToRemove = doc.dropPropertiesToRemove instanceof ObjectField ? ObjectField.MakeCopy(doc.dropPropertiesToRemove) : undefined;
- dbox.onDragStart = ScriptField.MakeFunction('makeDelegate(this.dragFactory)');
+ dbox = makeUserTemplateButton(doc);
} else if (doc.isButtonBar) {
dbox.ignoreClick = true;
}
data.droppedDocuments[i] = dbox;
});
}
+export function makeUserTemplateButton(doc: Doc) {
+ const layoutDoc = doc; // doc.layout instanceof Doc && doc.layout.isTemplateForField ? doc.layout : doc;
+ if (layoutDoc.type !== DocumentType.FONTICON) {
+ !layoutDoc.isTemplateDoc && makeTemplate(layoutDoc);
+ }
+ layoutDoc.isTemplateDoc = true;
+ const dbox = Docs.Create.FontIconDocument({
+ _nativeWidth: 100,
+ _nativeHeight: 100,
+ _width: 100,
+ _height: 100,
+ backgroundColor: StrCast(doc.backgroundColor),
+ title: StrCast(layoutDoc.title),
+ btnType: ButtonType.ClickButton,
+ icon: 'bolt',
+ isSystem: false,
+ });
+ dbox.title = ComputedField.MakeFunction('this.dragFactory.title');
+ dbox.dragFactory = layoutDoc;
+ dbox.dropPropertiesToRemove = doc.dropPropertiesToRemove instanceof ObjectField ? ObjectField.MakeCopy(doc.dropPropertiesToRemove) : undefined;
+ dbox.onDragStart = ScriptField.MakeFunction('getCopy(this.dragFactory)');
+ return dbox;
+}
ScriptingGlobals.add(
function convertToButtons(dragData: any) {
convertDropDataToButtons(dragData as DragManager.DocumentDragData);
diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts
index 0c8d18a7a..cf16c4d6d 100644
--- a/src/client/util/LinkManager.ts
+++ b/src/client/util/LinkManager.ts
@@ -58,8 +58,8 @@ export class LinkManager {
link &&
action(lAnchProtoProtos => {
Doc.AddDocToList(Doc.UserDoc(), 'links', link);
- lAnchs[0] && lAnchs[0][DocData][DirectLinks].add(link);
- lAnchs[1] && lAnchs[1][DocData][DirectLinks].add(link);
+ lAnchs[0]?.[DocData][DirectLinks].add(link);
+ lAnchs[1]?.[DocData][DirectLinks].add(link);
})
)
)
@@ -170,10 +170,11 @@ export class LinkManager {
console.log('WAITING FOR DOC/PROTO IN LINKMANAGER');
return [];
}
- const dirLinks = Doc.GetProto(anchor)[DirectLinks];
- const annos = DocListCast(anchor[Doc.LayoutFieldKey(anchor) + '_annotations']);
- if (!annos) debugger;
- return annos.reduce((list, anno) => [...list, ...LinkManager.Instance.relatedLinker(anno)], Array.from(dirLinks).slice());
+
+ const dirLinks = Array.from(anchor[DocData][DirectLinks]).filter(l => Doc.GetProto(anchor) === anchor[DocData] || ['1', '2'].includes(LinkManager.anchorIndex(l, anchor) as any));
+ const anchorRoot = DocCast(anchor.rootDocument, anchor); // template Doc fields store annotations on the topmost root of a template (not on themselves since the template layout items are only for layout)
+ const annos = DocListCast(anchorRoot[Doc.LayoutFieldKey(anchor) + '_annotations']);
+ return annos.reduce((list, anno) => [...list, ...LinkManager.Instance.relatedLinker(anno)], Array.from(dirLinks));
}, true);
// returns map of group type to anchor's links in that group type
diff --git a/src/client/util/RTFMarkup.tsx b/src/client/util/RTFMarkup.tsx
index f96d8a5df..315daad42 100644
--- a/src/client/util/RTFMarkup.tsx
+++ b/src/client/util/RTFMarkup.tsx
@@ -3,18 +3,12 @@ import { observer } from 'mobx-react';
import * as React from 'react';
import { MainViewModal } from '../views/MainViewModal';
import { SettingsManager } from './SettingsManager';
-import { Doc } from '../../fields/Doc';
-import { StrCast } from '../../fields/Types';
@observer
export class RTFMarkup extends React.Component<{}> {
static Instance: RTFMarkup;
@observable private isOpen = false; // whether the SharingManager modal is open or not
- // private get linkVisible() {
- // return this.targetDoc ? this.targetDoc["acl-" + PublicKey] !== SharingPermissions.None : false;
- // }
-
@action
public open = () => (this.isOpen = true);
@@ -39,6 +33,10 @@ export class RTFMarkup extends React.Component<{}> {
{`wiki:phrase`}
{` display wikipedia page for entered text (terminate with carriage return)`}
+
+ {`(( any text ))`}
+ {` submit text to Chat GPT to have results appended afterward`}
+
{`#tag `}
{` add hashtag metadata to document. e.g, #idea`}
@@ -47,10 +45,6 @@ export class RTFMarkup extends React.Component<{}> {
{`#, ## ... ###### `}
{` set heading style based on number of '#'s between 1 and 6`}
-
- {`#tag `}
- {` add hashtag metadata to document. e.g, #idea`}
-
{`>> `}
{` add a sidebar text document inline`}
@@ -61,7 +55,7 @@ export class RTFMarkup extends React.Component<{}> {
{`cmd-f `}
- {` collapse to an inline footnote)`}
+ {` collapse to an inline footnote`}
{`cmd-e `}
@@ -116,20 +110,20 @@ export class RTFMarkup extends React.Component<{}> {
{` start a block of text that begins with a hanging indent`}
- {`[:doctitle]] `}
+ {`@(doctitle) `}
{` hyperlink to document specified by it’s title`}
- {`[[fieldname]] `}
- {` display value of fieldname`}
+ {`[@(doctitle.)fieldname] `}
+ {` display value of fieldname of text document (unless (doctitle.) is used to indicate another document by it's title)`}
- {`[[fieldname=value]] `}
- {` assign value to fieldname of document and display it`}
+ {`[@fieldname:value] `}
+ {` assign value to fieldname to data document and display it (if '=' is used instead of ':' the value is set on the layout Doc. if value is wrapped in (()) then it will be sent to ChatGPT and the response will replace the value)`}
- {`[[fieldname:doctitle]] `}
- {` show value of fieldname from doc specified by it’s title`}
+ {`[@fieldname:=expression] `}
+ {` assign a computed expression to fieldname to data document and display it (if '=:=' is used instead of ':=' the expression is set on the layout Doc. if value is wrapped in (()) then it will be sent to ChatGPT and the prompt/response will replace the value)`}
);
diff --git a/src/client/views/ContextMenuItem.tsx b/src/client/views/ContextMenuItem.tsx
index b2076e1a5..d15ab749c 100644
--- a/src/client/views/ContextMenuItem.tsx
+++ b/src/client/views/ContextMenuItem.tsx
@@ -113,7 +113,7 @@ export class ContextMenuItem extends ObservableReactComponent 0 ? '90%' : '20%',
+ marginLeft: window.innerWidth - this._overPosX - 50 > 0 ? '90%' : '20%',
marginTop,
background: SettingsManager.userBackgroundColor,
}}>
diff --git a/src/client/views/TemplateMenu.scss b/src/client/views/TemplateMenu.scss
index 4d0f1bf00..36a9ce6d0 100644
--- a/src/client/views/TemplateMenu.scss
+++ b/src/client/views/TemplateMenu.scss
@@ -39,6 +39,7 @@
display: inline-block;
height: 100%;
width: 100%;
+ max-height: 250px;
.templateToggle,
.chromeToggle {
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index eed197b0b..e7269df98 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -1,7 +1,8 @@
-import { action, computed, observable, ObservableSet, runInAction } from 'mobx';
+import { computed, observable, ObservableSet, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { Doc, DocListCast } from '../../fields/Doc';
+import { DocData } from '../../fields/DocSymbols';
import { ScriptField } from '../../fields/ScriptField';
import { Cast, DocCast, StrCast } from '../../fields/Types';
import { TraceMobx } from '../../fields/util';
@@ -9,12 +10,10 @@ import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, retu
import { Docs, DocUtils } from '../documents/Documents';
import { ScriptingGlobals } from '../util/ScriptingGlobals';
import { Transform } from '../util/Transform';
-import { undoBatch } from '../util/UndoManager';
import { CollectionTreeView } from './collections/CollectionTreeView';
import { DocumentView, returnEmptyDocViewList } from './nodes/DocumentView';
import { DefaultStyleProvider } from './StyleProvider';
import './TemplateMenu.scss';
-import { DocData } from '../../fields/DocSymbols';
@observer
class TemplateToggle extends React.Component<{ template: string; checked: boolean; toggle: (event: React.ChangeEvent, template: string) => void }> {
@@ -99,7 +98,7 @@ export class TemplateMenu extends React.Component {
.filter(key => !noteTypes.some(nt => nt.title === key))
.forEach(template => templateMenu.push( this.toggleLayout(e, template)} />));
return (
-
+
{Doc.noviceMode ? null : }
{templateMenu}
Doc.SetInPlace(d, key, castedValue, !onLayoutDoc));
+ key && de.complete.docDragData.droppedDocuments.forEach(d => Doc.SetInPlace(d, key, castedValue, !this.onLayoutDoc(key)));
}
return true;
}
@@ -128,7 +127,7 @@ export class CollectionMasonryViewFieldRow extends ObservableReactComponent Doc.SetInPlace(d, key, castedValue, true));
+ key && this._props.docList.forEach(d => Doc.SetInPlace(d, key, castedValue, true));
this._heading = castedValue.toString();
return true;
}
@@ -155,10 +154,9 @@ export class CollectionMasonryViewFieldRow extends ObservableReactComponent {
this._createEmbeddingSelected = false;
const key = this._props.pivotField;
- this._props.docList.forEach(d => Doc.SetInPlace(d, key, undefined, true));
+ key && this._props.docList.forEach(d => Doc.SetInPlace(d, key, undefined, true));
if (this._props.parent.colHeaderData && this._props.headingObject) {
const index = this._props.parent.colHeaderData.indexOf(this._props.headingObject);
this._props.parent.colHeaderData.splice(index, 1);
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index 6a3cb759e..641e01b81 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -112,7 +112,7 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
if (this._props.colHeaderData?.map(i => i.heading).indexOf(castedValue.toString()) !== -1) {
return false;
}
- this._props.docList.forEach(d => (d[this._props.pivotField] = castedValue));
+ this._props.pivotField && this._props.docList.forEach(d => (d[this._props.pivotField] = castedValue));
if (this._props.headingObject) {
this._props.headingObject.setHeading(castedValue.toString());
this._heading = this._props.headingObject.heading;
@@ -137,7 +137,7 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
if (!value && !forceEmptyNote) return false;
const key = this._props.pivotField;
const newDoc = Docs.Create.TextDocument(value, { _height: 18, _width: 200, _layout_fitWidth: true, title: value, _layout_autoHeight: true });
- newDoc[key] = this.getValue(this._props.heading);
+ key && (newDoc[key] = this.getValue(this._props.heading));
const maxHeading = this._props.docList.reduce((maxHeading, doc) => (NumCast(doc.heading) > maxHeading ? NumCast(doc.heading) : maxHeading), 0);
const heading = maxHeading === 0 || this._props.docList.length === 0 ? 1 : maxHeading === 1 ? 2 : 3;
newDoc.heading = heading;
@@ -148,7 +148,7 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
@action
deleteColumn = () => {
- this._props.docList.forEach(d => (d[this._props.pivotField] = undefined));
+ this._props.pivotField && this._props.docList.forEach(d => (d[this._props.pivotField] = undefined));
if (this._props.colHeaderData && this._props.headingObject) {
const index = this._props.colHeaderData.indexOf(this._props.headingObject);
this._props.colHeaderData.splice(index, 1);
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 08c00d696..1b4349f44 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -574,11 +574,21 @@ export class TreeView extends ObservableReactComponent {
value.indexOf(':') !== -1 && KeyValueBox.SetField(doc, value.substring(0, value.indexOf(':')), value.substring(value.indexOf(':') + 1, value.length), true)}
+ SetValue={input => {
+ const match = input.match(/([a-zA-Z0-9_-]+)(=|=:=)([a-zA-Z,_@\?\+\-\*\/\ 0-9\(\)]+)/);
+ if (match) {
+ const key = match[1];
+ const assign = match[2];
+ const val = match[3];
+ KeyValueBox.SetField(doc, key, assign + val, false);
+ return true;
+ }
+ return false;
+ }}
/>
);
diff --git a/src/client/views/global/globalScripts.ts b/src/client/views/global/globalScripts.ts
index 0579b07c7..3fdc9a488 100644
--- a/src/client/views/global/globalScripts.ts
+++ b/src/client/views/global/globalScripts.ts
@@ -1,29 +1,26 @@
import { Colors } from 'browndash-components';
import { action, runInAction } from 'mobx';
+import { aggregateBounds } from '../../../Utils';
import { Doc, Opt } from '../../../fields/Doc';
import { InkTool } from '../../../fields/InkField';
import { BoolCast, Cast, NumCast, StrCast } from '../../../fields/Types';
import { WebField } from '../../../fields/URLField';
import { GestureUtils } from '../../../pen-gestures/GestureUtils';
-import { aggregateBounds } from '../../../Utils';
import { DocumentType } from '../../documents/DocumentTypes';
import { LinkManager } from '../../util/LinkManager';
import { ScriptingGlobals } from '../../util/ScriptingGlobals';
import { SelectionManager } from '../../util/SelectionManager';
-import { undoable, UndoManager } from '../../util/UndoManager';
-import { CollectionFreeFormView } from '../collections/collectionFreeForm';
+import { UndoManager, undoable } from '../../util/UndoManager';
import { GestureOverlay } from '../GestureOverlay';
import { ActiveFillColor, ActiveInkColor, ActiveInkWidth, ActiveIsInkMask, InkingStroke, SetActiveFillColor, SetActiveInkColor, SetActiveInkWidth, SetActiveIsInkMask } from '../InkingStroke';
+import { CollectionFreeFormView } from '../collections/collectionFreeForm';
// import { InkTranscription } from '../InkTranscription';
+import { DocData } from '../../../fields/DocSymbols';
import { CollectionFreeFormDocumentView } from '../nodes/CollectionFreeFormDocumentView';
import { DocumentView } from '../nodes/DocumentView';
-import { RichTextMenu } from '../nodes/formattedText/RichTextMenu';
-import { WebBox } from '../nodes/WebBox';
import { VideoBox } from '../nodes/VideoBox';
-import { DocData } from '../../../fields/DocSymbols';
-import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
-import { PrefetchProxy } from '../../../fields/Proxy';
-import { MakeTemplate } from '../../util/DropConverter';
+import { WebBox } from '../nodes/WebBox';
+import { RichTextMenu } from '../nodes/formattedText/RichTextMenu';
ScriptingGlobals.add(function IsNoneSelected() {
return SelectionManager.Views.length <= 0;
@@ -76,19 +73,7 @@ ScriptingGlobals.add(function setBackgroundColor(color?: string, checkResult?: b
// toggle: Set overlay status of selected document
ScriptingGlobals.add(function setDefaultTemplate(checkResult?: boolean) {
- if (checkResult) {
- return Doc.UserDoc().defaultTextLayout;
- }
- const view = SelectionManager.Views.length === 1 && SelectionManager.Views[0].ComponentView instanceof FormattedTextBox ? SelectionManager.Views[0] : undefined;
-
- if (view) {
- const tempDoc = view.Document;
- if (!view.layoutDoc.isTemplateDoc) {
- MakeTemplate(tempDoc);
- }
- Doc.UserDoc().defaultTextLayout = new PrefetchProxy(tempDoc);
- tempDoc && Doc.AddDocToList(Cast(Doc.UserDoc().template_notes, Doc, null), 'data', tempDoc);
- } else Doc.UserDoc().defaultTextLayout = undefined;
+ return DocumentView.setDefaultTemplate(checkResult);
});
// toggle: Set overlay status of selected document
ScriptingGlobals.add(function setHeaderColor(color?: string, checkResult?: boolean) {
@@ -197,7 +182,7 @@ ScriptingGlobals.add(function setFontAttr(attr: 'font' | 'fontColor' | 'highligh
map.get(attr)?.setDoc?.();
});
-type attrname = 'noAutoLink' | 'dictation' | 'bold' | 'italics' | 'underline' | 'left' | 'center' | 'right' | 'vcent' | 'bullet' | 'decimal';
+type attrname = 'noAutoLink' | 'dictation' | 'bold' | 'italics' | 'elide' | 'underline' | 'left' | 'center' | 'right' | 'vcent' | 'bullet' | 'decimal';
type attrfuncs = [attrname, { checkResult: () => boolean; toggle: () => any }];
ScriptingGlobals.add(function toggleCharStyle(charStyle: attrname, checkResult?: boolean) {
@@ -221,6 +206,8 @@ ScriptingGlobals.add(function toggleCharStyle(charStyle: attrname, checkResult?:
const attrs:attrfuncs[] = [
['dictation', { checkResult: () => textView?._recordingDictation ? true:false,
toggle: () => textView && runInAction(() => (textView._recordingDictation = !textView._recordingDictation)) }],
+ ['elide', { checkResult: () => false,
+ toggle: () => editorView ? RichTextMenu.Instance?.elideSelection(): 0}],
['noAutoLink',{ checkResult: () => (editorView ? RichTextMenu.Instance.noAutoLink : false),
toggle: () => editorView && RichTextMenu.Instance?.toggleNoAutoLinkAnchor()}],
['bold', { checkResult: () => (editorView ? RichTextMenu.Instance.bold : (Doc.UserDoc().fontWeight === 'bold') ? true:false),
diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx
index 715b23fb6..62f630c6c 100644
--- a/src/client/views/nodes/ComparisonBox.tsx
+++ b/src/client/views/nodes/ComparisonBox.tsx
@@ -159,6 +159,37 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
moveDoc2 = (doc: Doc | Doc[], targetCol: Doc | undefined, addDoc: any) => (doc instanceof Doc ? [doc] : doc).reduce((res, doc: Doc) => res && this.moveDoc(doc, addDoc, this.fieldKey + '_2'), true);
remDoc1 = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((res, doc) => res && this.remDoc(doc, this.fieldKey + '_1'), true);
remDoc2 = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((res, doc) => res && this.remDoc(doc, this.fieldKey + '_2'), true);
+
+ /**
+ * Tests for whether a comparison box slot (ie, before or after) has renderable text content
+ * @param whichSlot field key for start or end slot
+ * @returns a JSX layout string if a text field is found, othwerise undefined
+ */
+ testForTextFields = (whichSlot: string) => {
+ const slotHasText = Doc.Get(this.dataDoc, whichSlot, true) instanceof RichTextField || typeof Doc.Get(this.dataDoc, whichSlot, true) === 'string';
+ const subjectText = RTFCast(this.Document[this.fieldKey])?.Text.trim();
+ const altText = RTFCast(this.Document[this.fieldKey + '_alternate'])?.Text.trim();
+ const layoutTemplateString =
+ slotHasText ? FormattedTextBox.LayoutString(whichSlot):
+ whichSlot.endsWith('1') ? (subjectText !== undefined ? FormattedTextBox.LayoutString(this.fieldKey) : undefined) :
+ altText !== undefined ? FormattedTextBox.LayoutString(this.fieldKey + '_alternate'): undefined; // prettier-ignore
+
+ // A bit hacky to try out the concept of using GPT to fill in flashcards
+ // If the second slot doesn't have anything in it, but the fieldKey slot has text (e.g., this.text is a string)
+ // and the fieldKey + "_alternate" has text that includes a GPT query (indicated by (( && )) ) that is parameterized (optionally) by the fieldKey text (this) or other metadata (this.).
+ // eg., this.text_alternate is
+ // "((Provide a one sentence definition for (this) that doesn't use any word in (this.excludeWords) ))"
+ // where (this) is replaced by the text in the fieldKey slot abd this.excludeWords is repalced by the conetnts of the excludeWords field
+ // The GPT call will put the "answer" in the second slot of the comparison (eg., text_2)
+ if (whichSlot.endsWith('2') && !layoutTemplateString?.includes(whichSlot)) {
+ var queryText = altText.replace('(this)', subjectText); // TODO: this should be done in KeyValueBox.setField but it doesn't know about the fieldKey ...
+ if (queryText && queryText.match(/\(\(.*\)\)/)) {
+ KeyValueBox.SetField(this.Document, whichSlot, ':=' + queryText, false); // make the second slot be a computed field on the data doc that calls ChatGpt
+ }
+ }
+ return layoutTemplateString;
+ };
+
_closeRef = React.createRef();
render() {
const clearButton = (which: string) => {
@@ -176,48 +207,24 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
/**
* Display the Docs in the before/after fields of the comparison. This also supports a GPT flash card use case
* where if there are no Docs in the slots, but the main fieldKey contains text, then
- * @param which
+ * @param whichSlot
* @returns
*/
- const displayDoc = (which: string) => {
- const whichDoc = DocCast(this.dataDoc[which]);
+ const displayDoc = (whichSlot: string) => {
+ const whichDoc = DocCast(this.dataDoc[whichSlot]);
const targetDoc = DocCast(whichDoc?.annotationOn, whichDoc);
- const subjectText = RTFCast(this.Document[this.fieldKey])?.Text.trim();
- // if there is no Doc in the first comparison slot, but the comparison box's fieldKey slot has a RichTextField, then render a text box to show the contents of the document's field key slot
- // of if there is no Doc in the second comparison slot, but the second slot has a RichTextField, then render a text box to show the contents of the document's field key slot
- const layoutTemplateString = !targetDoc
- ? which.endsWith('1') && subjectText !== undefined
- ? FormattedTextBox.LayoutString(this.fieldKey)
- : which.endsWith('2') && (this.Document[which] instanceof RichTextField || typeof this.Document[which] === 'string')
- ? FormattedTextBox.LayoutString(which)
- : undefined
- : undefined;
-
- // A bit hacky to try out the concept of using GPT to fill in flashcards -- this whole process should probably be packaged into a script to be more generic.
- // If the second slot doesn't have anything in it, but the fieldKey slot has text (e.g., this.text is a string)
- // and the fieldKey + "_alternate" has text, then treat the _alternate's text as a GPT query (indicated by (( && )) ) that is parameterized (optionally)
- // by the field references in the text (eg., this.text_alternate is
- // "((Provide a one sentence definition for (this) that doesn't use any word in (this.excludeWords) ))"
- // where (this) is replaced by the text in the fieldKey slot abd this.excludeWords is repalced by the conetnts of the excludeWords field
- // A GPT call will put the "answer" in the second slot of the comparison (eg., text_2)
- if (which.endsWith('2') && !layoutTemplateString && !targetDoc) {
- var queryText = RTFCast(this.Document[this.fieldKey + '_alternate'])
- ?.Text.replace('(this)', subjectText) // TODO: this should be done in KeyValueBox.setField but it doesn't know about the fieldKey ...
- .trim();
- if (subjectText && queryText.match(/\(\(.*\)\)/)) {
- KeyValueBox.SetField(this.Document, which, ':=' + queryText, false); // make the second slot be a computed field on the data doc that calls ChatGpt
- }
- }
+ const layoutTemplateString = targetDoc ? '' : this.testForTextFields(whichSlot);
return targetDoc || layoutTemplateString ? (
<>
()
hideLinkButton={true}
pointerEvents={this._isAnyChildContentActive ? undefined : returnNone}
/>
- {layoutTemplateString ? null : clearButton(which)}
+ {layoutTemplateString ? null : clearButton(whichSlot)}
> // placeholder image if doc is missing
) : (
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 9848f18e0..e9ce98583 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -10,6 +10,7 @@ import { AclPrivate, Animation, AudioPlay, DocData, DocViews } from '../../../fi
import { Id } from '../../../fields/FieldSymbols';
import { InkTool } from '../../../fields/InkField';
import { List } from '../../../fields/List';
+import { PrefetchProxy } from '../../../fields/Proxy';
import { listSpec } from '../../../fields/Schema';
import { ScriptField } from '../../../fields/ScriptField';
import { BoolCast, Cast, DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
@@ -19,10 +20,11 @@ import { DocServer } from '../../DocServer';
import { Networking } from '../../Network';
import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils';
import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes';
-import { DocOptions, DocUtils, Docs } from '../../documents/Documents';
+import { DocUtils, Docs } from '../../documents/Documents';
import { DictationManager } from '../../util/DictationManager';
import { DocumentManager } from '../../util/DocumentManager';
import { DragManager, dropActionType } from '../../util/DragManager';
+import { MakeTemplate, makeUserTemplateButton } from '../../util/DropConverter';
import { FollowLinkScript } from '../../util/LinkFollower';
import { LinkManager } from '../../util/LinkManager';
import { ScriptingGlobals } from '../../util/ScriptingGlobals';
@@ -36,6 +38,7 @@ import { ContextMenu } from '../ContextMenu';
import { ContextMenuProps } from '../ContextMenuItem';
import { DocComponent, ViewBoxInterface } from '../DocComponent';
import { EditableView } from '../EditableView';
+import { FieldsDropdown } from '../FieldsDropdown';
import { GestureOverlay } from '../GestureOverlay';
import { LightboxView } from '../LightboxView';
import { AudioAnnoState, StyleProp } from '../StyleProvider';
@@ -47,7 +50,6 @@ import { KeyValueBox } from './KeyValueBox';
import { LinkAnchorBox } from './LinkAnchorBox';
import { FormattedTextBox } from './formattedText/FormattedTextBox';
import { PresEffect, PresEffectDirection } from './trails';
-import { FieldsDropdown } from '../FieldsDropdown';
interface Window {
MediaRecorder: MediaRecorder;
}
@@ -1271,6 +1273,32 @@ export class DocumentView extends DocComponent() {
custom && DocUtils.makeCustomViewClicked(this.Document, Docs.Create.StackingDocument, layout, undefined);
}, 'set custom view');
+ public static setDefaultTemplate(checkResult?: boolean) {
+ if (checkResult) {
+ return Doc.UserDoc().defaultTextLayout;
+ }
+ const view = SelectionManager.Views[0]?._props.renderDepth > 0 ? SelectionManager.Views[0] : undefined;
+ undoable(() => {
+ var tempDoc: Opt;
+ if (view) {
+ if (!view.layoutDoc.isTemplateDoc) {
+ tempDoc = view.Document;
+ MakeTemplate(tempDoc);
+ Doc.AddDocToList(Doc.UserDoc(), 'template_user', tempDoc);
+ Doc.AddDocToList(DocListCast(Doc.MyTools.data)[1], 'data', makeUserTemplateButton(tempDoc));
+ tempDoc && Doc.AddDocToList(Cast(Doc.UserDoc().template_user, Doc, null), 'data', tempDoc);
+ } else {
+ tempDoc = DocCast(view.Document[StrCast(view.Document.layout_fieldKey)]);
+ if (!tempDoc) {
+ tempDoc = view.Document;
+ while (tempDoc && !Doc.isTemplateDoc(tempDoc)) tempDoc = DocCast(tempDoc.proto);
+ }
+ }
+ }
+ Doc.UserDoc().defaultTextLayout = tempDoc ? new PrefetchProxy(tempDoc) : undefined;
+ }, 'set default template')();
+ }
+
/**
* This switches between the current view of a Doc and a specified alternate layout view.
* The current view of the Doc is stored in the layout_default field so that it can be restored.
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 4ecaaa283..5b47dd91d 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -55,6 +55,7 @@ export interface FieldViewSharedProps {
ignoreAutoHeight?: boolean;
disableBrushing?: boolean; // should highlighting for this view be disabled when same document in another view is hovered over.
hideClickBehaviors?: boolean; // whether to suppress menu item options for changing click behaviors
+ ignoreUsePath?: boolean; // ignore the usePath field for selecting the fieldKey (eg., on text docs)
CollectionFreeFormDocumentView?: () => CollectionFreeFormDocumentView;
containerViewPath?: () => DocumentView[];
fitContentsToBox?: () => boolean; // used by freeformview to fit its contents to its panel. corresponds to _freeform_fitContentsToBox property on a Document
diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
index f02ad7300..57ae92359 100644
--- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
@@ -5,8 +5,7 @@ import { action, computed, makeObservable, observable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { Doc, DocListCast, StrListCast } from '../../../../fields/Doc';
-import { ScriptField } from '../../../../fields/ScriptField';
-import { BoolCast, Cast, DocCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
+import { BoolCast, DocCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
import { emptyFunction, returnTrue, setupMoveUpEvents, Utils } from '../../../../Utils';
import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes';
import { SelectionManager } from '../../../util/SelectionManager';
@@ -61,23 +60,12 @@ export class FontIconBox extends ViewBoxBaseComponent() {
}
@observable noTooltip = false;
- showTemplate = (): void => {
- const dragFactory = Cast(this.layoutDoc.dragFactory, Doc, null);
- dragFactory && this._props.addDocTab(dragFactory, OpenWhere.addRight);
- };
- dragAsTemplate = (): void => {
- this.layoutDoc.onDragStart = ScriptField.MakeFunction('getCopy(this.dragFactory, true)');
- };
- useAsPrototype = (): void => {
- this.layoutDoc.onDragStart = ScriptField.MakeFunction('makeDelegate(this.dragFactory, true)');
- };
+ showTemplate = (dragFactory: Doc) => this._props.addDocTab(dragFactory, OpenWhere.addRight);
specificContextMenu = (): void => {
- if (!Doc.noviceMode && Cast(this.layoutDoc.dragFactory, Doc, null)) {
- const cm = ContextMenu.Instance;
- cm.addItem({ description: 'Show Template', event: this.showTemplate, icon: 'tag' });
- cm.addItem({ description: 'Use as Render Template', event: this.dragAsTemplate, icon: 'tag' });
- cm.addItem({ description: 'Use as Prototype', event: this.useAsPrototype, icon: 'tag' });
+ const dragFactory = DocCast(this.layoutDoc.dragFactory);
+ if (!Doc.noviceMode && dragFactory) {
+ ContextMenu.Instance.addItem({ description: 'Show Template', event: () => this.showTemplate(dragFactory), icon: 'tag' });
}
};
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index 2bcad806f..d85432631 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -115,7 +115,7 @@ export class KeyValueBox extends ObservableReactComponent {
field === undefined && (field = res.result);
}
}
- if (!key) return field;
+ if (!key) return false;
if (Field.IsField(field, true) && (key !== 'proto' || field !== target)) {
target[key] = field;
return true;
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index d59489a78..f9e8ce4f3 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -125,7 +125,7 @@ export class KeyValuePair extends ObservableReactComponent {
pinToPres: returnZero,
}}
GetValue={() => Field.toKeyValueString(this._props.doc, this._props.keyName)}
- SetValue={(value: string) => (KeyValueBox.SetField(this._props.doc, this._props.keyName, value) ? true : false)}
+ SetValue={(value: string) => KeyValueBox.SetField(this._props.doc, this._props.keyName, value)}
/>
diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx
index 62cb460c2..6b66d829c 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.tsx
+++ b/src/client/views/nodes/formattedText/DashFieldView.tsx
@@ -1,6 +1,6 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Tooltip } from '@mui/material';
-import { action, computed, IReactionDisposer, makeObservable, observable, reaction } from 'mobx';
+import { action, computed, IReactionDisposer, makeObservable, observable, reaction, trace } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import * as ReactDOM from 'react-dom/client';
@@ -8,12 +8,12 @@ import { Doc, DocListCast, Field } from '../../../../fields/Doc';
import { List } from '../../../../fields/List';
import { listSpec } from '../../../../fields/Schema';
import { SchemaHeaderField } from '../../../../fields/SchemaHeaderField';
-import { Cast } from '../../../../fields/Types';
+import { Cast, DocCast } from '../../../../fields/Types';
import { emptyFunction, returnFalse, returnZero, setupMoveUpEvents } from '../../../../Utils';
import { DocServer } from '../../../DocServer';
import { CollectionViewType } from '../../../documents/DocumentTypes';
import { Transform } from '../../../util/Transform';
-import { undoBatch } from '../../../util/UndoManager';
+import { undoable, undoBatch } from '../../../util/UndoManager';
import { AntimodeMenu, AntimodeMenuProps } from '../../AntimodeMenu';
import { SchemaTableCell } from '../../collections/collectionSchema/SchemaTableCell';
import { FilterPanel } from '../../FilterPanel';
@@ -21,6 +21,7 @@ import { ObservableReactComponent } from '../../ObservableReactComponent';
import { OpenWhere } from '../DocumentView';
import './DashFieldView.scss';
import { FormattedTextBox } from './FormattedTextBox';
+import { DocData } from '../../../../fields/DocSymbols';
export class DashFieldView {
dom: HTMLDivElement; // container for label and value
@@ -62,6 +63,8 @@ export class DashFieldView {
height={node.attrs.height}
hideKey={node.attrs.hideKey}
editable={node.attrs.editable}
+ expanded={node.attrs.expanded}
+ dataDoc={node.attrs.dataDoc}
tbox={tbox}
/>
);
@@ -89,6 +92,8 @@ interface IDashFieldViewInternal {
width: number;
height: number;
editable: boolean;
+ expanded: boolean;
+ dataDoc: boolean;
node: any;
getPos: any;
unclickable: () => boolean;
@@ -101,18 +106,19 @@ export class DashFieldViewInternal extends ObservableReactComponent();
@observable _dashDoc: Doc | undefined = undefined;
- @observable _expanded = false;
+ @observable _expanded = this._props.expanded;
constructor(props: IDashFieldViewInternal) {
super(props);
makeObservable(this);
this._fieldKey = this._props.fieldKey;
- this._textBoxDoc = this._fieldKey.startsWith('_') ? this._props.tbox.Document : this._props.tbox.dataDoc;
+ this._textBoxDoc = this._props.tbox.Document;
+ const setDoc = (doc: Doc) => (this._dashDoc = this._props.dataDoc ? doc[DocData] : doc);
if (this._props.docId) {
- DocServer.GetRefField(this._props.docId).then(action(dashDoc => dashDoc instanceof Doc && (this._dashDoc = dashDoc)));
+ DocServer.GetRefField(this._props.docId).then(dashDoc => dashDoc instanceof Doc && setDoc(dashDoc));
} else {
- this._dashDoc = this._fieldKey.startsWith('_') ? this._props.tbox.Document : this._props.tbox.dataDoc;
+ setDoc(this._props.tbox.Document);
}
}
@@ -126,7 +132,9 @@ export class DashFieldViewInternal extends ObservableReactComponent 100;
+ isRowActive = () => this._expanded && this._props.editable;
+ finishEdit = action(() => (this._expanded = false));
+ selectedCell = (): [Doc, number] => [this._dashDoc!, 0];
// set the display of the field's value (checkbox for booleans, span of text for strings)
@computed get fieldValueContent() {
@@ -137,18 +145,18 @@ export class DashFieldViewInternal extends ObservableReactComponent this._props.tbox._props.PanelWidth() - 20 : returnZero}
- selectedCell={() => [this._dashDoc!, 0]}
+ maxWidth={this._props.hideKey || this._hideKey ? undefined : this._props.tbox._props.PanelWidth}
+ columnWidth={returnZero}
+ selectedCell={this.selectedCell}
fieldKey={this._fieldKey}
rowHeight={returnZero}
- isRowActive={() => this._expanded && this._props.editable}
+ isRowActive={this.isRowActive}
padding={0}
getFinfo={emptyFunction}
setColumnValues={returnFalse}
allowCRs={true}
oneLine={!this._expanded}
- finishEdit={action(() => (this._expanded = false))}
+ finishEdit={this.finishEdit}
transform={Transform.Identity}
menuTarget={null}
/>
@@ -173,11 +181,21 @@ export class DashFieldViewInternal extends ObservableReactComponent this._dashDoc && (this._dashDoc[this._fieldKey + '_hideKey'] = !this._dashDoc[this._fieldKey + '_hideKey'])),
+ 'hideKey'
+ );
+
+ @computed get _hideKey() {
+ return this._dashDoc && this._dashDoc[this._fieldKey + '_hideKey'];
+ }
+
// clicking on the label creates a pivot view collection of all documents
// in the same collection. The pivot field is the fieldKey of this label
onPointerDownLabelSpan = (e: any) => {
setupMoveUpEvents(this, e, returnFalse, returnFalse, e => {
DashFieldViewMenu.createFieldView = this.createPivotForField;
+ DashFieldViewMenu.toggleFieldHide = this.toggleFieldHide;
DashFieldViewMenu.Instance.show(e.clientX, e.clientY + 16, this._fieldKey);
});
};
@@ -188,6 +206,7 @@ export class DashFieldViewInternal extends ObservableReactComponent ({ value: facet, label: facet }));
@@ -203,9 +222,9 @@ export class DashFieldViewInternal extends ObservableReactComponent
- {this._props.hideKey ? null : (
+ {this._props.hideKey || this._hideKey ? null : (
- {(this._textBoxDoc === this._dashDoc ? '' : this._dashDoc?.title + ':') + this._fieldKey}
+ {(Doc.AreProtosEqual(DocCast(this._textBoxDoc.rootDocument) ?? this._textBoxDoc, DocCast(this._dashDoc?.rootDocument) ?? this._dashDoc) ? '' : this._dashDoc?.title + ':') + this._fieldKey}
)}
{this._props.fieldKey.startsWith('#') ? null : this.fieldValueContent}
@@ -224,6 +243,7 @@ export class DashFieldViewInternal extends ObservableReactComponent {
static Instance: DashFieldViewMenu;
static createFieldView: (e: React.MouseEvent) => void = emptyFunction;
+ static toggleFieldHide: () => void = emptyFunction;
constructor(props: any) {
super(props);
DashFieldViewMenu.Instance = this;
@@ -233,6 +253,10 @@ export class DashFieldViewMenu extends AntimodeMenu {
DashFieldViewMenu.createFieldView(e);
DashFieldViewMenu.Instance.fadeOut(true);
};
+ toggleFieldHide = (e: React.MouseEvent) => {
+ DashFieldViewMenu.toggleFieldHide();
+ DashFieldViewMenu.Instance.fadeOut(true);
+ };
@observable _fieldKey = '';
@@ -252,6 +276,9 @@ export class DashFieldViewMenu extends AntimodeMenu {
+
+
+
);
}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index fb709818c..2b48494f2 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -451,7 +451,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent (tr = this.hyperlinkTerm(tr, term, newAutoLinks)));
+ Doc.MyPublishedDocs.filter(term => term.title).forEach(term => (tr = this.hyperlinkTerm(tr, term, newAutoLinks)));
tr = tr.setSelection(isNodeSel && false ? new NodeSelection(tr.doc.resolve(f)) : new TextSelection(tr.doc.resolve(f), tr.doc.resolve(t)));
this._editorView?.dispatch(tr);
}
@@ -958,8 +958,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent (this.layoutDoc._layout_autoHeight = !this.layoutDoc._layout_autoHeight),
icon: this.Document._layout_autoHeight ? 'lock' : 'unlock',
});
- optionItems.push({ description: `show markdown options`, event: RTFMarkup.Instance.open, icon: });
!options && cm.addItem({ description: 'Options...', subitems: optionItems, icon: 'eye' });
+ const help = cm.findByDescription('Help...');
+ const helpItems = help && 'subitems' in help ? help.subitems : [];
+ helpItems.push({ description: `show markdown options`, event: RTFMarkup.Instance.open, icon: });
+ !help && cm.addItem({ description: 'Help...', subitems: helpItems, icon: 'eye' });
this._downX = this._downY = Number.NaN;
};
@@ -1239,8 +1242,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent this._editorView?.dispatch(tx.insertText(incomingValue.str)));
+ } else {
+ selectAll(this._editorView.state, tx => this._editorView?.dispatch(tx.insertText(incomingValue?.str ?? '')));
}
}
}
@@ -1339,15 +1342,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent {
if (pdfAnchor instanceof Doc) {
const dashField = view.state.schema.nodes.paragraph.create({}, [
- view.state.schema.nodes.dashField.create({ fieldKey: 'text', docId: pdfAnchor[Id], hideKey: true, editable: false }, undefined, [
+ view.state.schema.nodes.dashField.create({ fieldKey: 'text', docId: pdfAnchor[Id], hideKey: true, editable: false, expanded: true }, undefined, [
view.state.schema.marks.linkAnchor.create({
allAnchors: [{ href: `/doc/${this.Document[Id]}`, title: this.Document.title, anchorId: `${this.Document[Id]}` }],
- title: `from: ${DocCast(pdfAnchor.embedContainer).title}`,
+ title: StrCast(pdfAnchor.title),
noPreview: true,
- docref: false,
+ docref: true,
+ fontSize: '8px',
}),
- view.state.schema.marks.pFontSize.create({ fontSize: '8px' }),
- view.state.schema.marks.em.create({}),
]),
]);
@@ -1926,11 +1928,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent
);
}
- cycleAlternateText = () => {
- if (this.layoutDoc._layout_enableAltContentUI) {
- const usePath = this.layoutDoc[`_${this._props.fieldKey}_usePath`];
- this.layoutDoc[`_${this._props.fieldKey}_usePath`] = usePath === undefined ? 'alternate' : usePath === 'alternate' ? 'alternate:hover' : undefined;
- }
+ cycleAlternateText = (skipHover?: boolean) => {
+ this.layoutDoc._layout_enableAltContentUI = true;
+ const usePath = this.layoutDoc[`_${this._props.fieldKey}_usePath`];
+ this.layoutDoc[`_${this._props.fieldKey}_usePath`] = usePath === undefined ? 'alternate' : usePath === 'alternate' && !skipHover ? 'alternate:hover' : undefined;
};
@computed get overlayAlternateIcon() {
const usePath = this.layoutDoc[`_${this._props.fieldKey}_usePath`];
@@ -1965,7 +1966,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent {
});
}
+ elideSelection = () => {
+ const state = this.view?.state;
+ if (!state) return;
+ if (state.selection.empty) return false;
+ const mark = state.schema.marks.summarize.create();
+ const tr = state.tr;
+ tr.addMark(state.selection.from, state.selection.to, mark);
+ const content = tr.selection.content();
+ const newNode = state.schema.nodes.summary.create({ visibility: false, text: content, textslice: content.toJSON() });
+ this.view?.dispatch?.(tr.replaceSelectionWith(newNode).removeMark(tr.selection.from - 1, tr.selection.from, mark));
+ return true;
+ };
+
toggleNoAutoLinkAnchor = () => {
if (this.view) {
const mark = this.view.state.schema.mark(this.view.state.schema.marks.noAutoLinkAnchor);
diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts
index c798ae4b3..b97141e92 100644
--- a/src/client/views/nodes/formattedText/RichTextRules.ts
+++ b/src/client/views/nodes/formattedText/RichTextRules.ts
@@ -267,7 +267,7 @@ export class RichTextRules {
// toggle alternate text UI %/
new InputRule(new RegExp(/%\//), (state, match, start, end) => {
- setTimeout(this.TextBox.cycleAlternateText);
+ setTimeout(() => this.TextBox.cycleAlternateText(true));
return state.tr.deleteRange(start, end);
}),
@@ -283,40 +283,26 @@ export class RichTextRules {
: tr;
}),
- new InputRule(new RegExp(/(^|[^=])(\(\(.*\)\))/), (state, match, start, end) => {
- var count = 0; // ignore first return value which will be the notation that chat is pending a result
- KeyValueBox.SetField(this.Document, '', match[2], false, (gptval: FieldResult) => {
- count && this.TextBox.EditorView?.dispatch(this.TextBox.EditorView!.state.tr.insertText(' ' + (gptval as string)));
- count++;
- });
- return null;
- }),
-
- // create a text display of a metadata field on this or another document, or create a hyperlink portal to another document
- // [[ : ]]
- // [[:docTitle]] => hyperlink
- // [[fieldKey]] => show field
- // [[fieldKey{:,=:}=value]] => show field and also set its value
- // [[fieldKey:docTitle]] => show field of doc
- new InputRule(
- new RegExp(/\[\[([a-zA-Z_\? \-0-9]*)((=:|:)?=)([a-z,A-Z_@\?+\-*/\ 0-9\(\)]*)?(:[a-zA-Z_@:\.\? \-0-9]+)?\]\]$/),
- (state, match, start, end) => {
- const fieldKey = match[1];
- const assign = match[2] === '=' ? '' : match[2];
- const value = match[4];
- const docTitle = match[5]?.replace(':', '');
+ // create a hyperlink to a titled document
+ // @()
+ new InputRule(new RegExp(/(^|\s)@\(([a-zA-Z_@:\.\? \-0-9]+)\)/), (state, match, start, end) => {
+ const docTitle = match[2];
+ const prefixLength = '@('.length;
+ if (docTitle) {
const linkToDoc = (target: Doc) => {
- const rstate = this.TextBox.EditorView?.state;
- const selection = rstate?.selection.$from.pos;
- if (rstate) {
- this.TextBox.EditorView?.dispatch(rstate.tr.setSelection(new TextSelection(rstate.doc.resolve(start), rstate.doc.resolve(end - 3))));
+ const editor = this.TextBox.EditorView;
+ const selection = editor?.state?.selection.$from.pos;
+ if (editor) {
+ const estate = editor.state;
+ editor.dispatch(estate.tr.setSelection(new TextSelection(estate.doc.resolve(start), estate.doc.resolve(end - prefixLength))));
}
DocUtils.MakeLink(this.TextBox.getAnchor(true), target, { link_relationship: 'portal to:portal from' });
- const fstate = this.TextBox.EditorView?.state;
- if (fstate && selection) {
- this.TextBox.EditorView?.dispatch(fstate.tr.setSelection(new TextSelection(fstate.doc.resolve(selection))));
+ const teditor = this.TextBox.EditorView;
+ if (teditor && selection) {
+ const tstate = teditor.state;
+ teditor.dispatch(tstate.tr.setSelection(new TextSelection(tstate.doc.resolve(selection))));
}
};
const getTitledDoc = (docTitle: string) => {
@@ -326,32 +312,57 @@ export class RichTextRules {
const titledDoc = DocServer.FindDocByTitle(docTitle);
return titledDoc ? Doc.BestEmbedding(titledDoc) : titledDoc;
};
- if (!fieldKey) {
- if (docTitle) {
- const target = getTitledDoc(docTitle);
- if (target) {
- setTimeout(() => linkToDoc(target));
- return state.tr.deleteRange(end - 1, end).deleteRange(start, start + 3);
- }
- }
- return state.tr;
+ const target = getTitledDoc(docTitle);
+ if (target) {
+ setTimeout(() => linkToDoc(target));
+ return state.tr.insertText(' ').deleteRange(start, start + prefixLength);
}
+ }
+ return state.tr;
+ }),
+
+ // create a text display of a metadata field on this or another document, or create a hyperlink portal to another document
+ // [@{this,doctitle,}.fieldKey{:,=,:=,=:=}value]
+ // [@{this,doctitle,}.fieldKey]
+ new InputRule(
+ new RegExp(/\[(@|@this\.|@[a-zA-Z_\? \-0-9]+\.)([a-zA-Z_\?\-0-9]+)((:|=|:=|=:=)([a-zA-Z,_@\?\+\-\*\/\ 0-9\(\)]*))?\]/),
+ (state, match, start, end) => {
+ const docTitle = match[1].substring(1).replace(/\.$/, '');
+ const fieldKey = match[2];
+ const assign = match[4] === ':' ? (match[4] = '') : match[4];
+ const value = match[5];
+ const dataDoc = value === undefined ? !fieldKey.startsWith('_') : !assign?.startsWith('=');
+ const getTitledDoc = (docTitle: string) => {
+ if (!DocServer.FindDocByTitle(docTitle)) {
+ Doc.AddToMyPublished(Docs.Create.TextDocument('', { title: docTitle, _width: 400, _layout_autoHeight: true }));
+ }
+ return DocServer.FindDocByTitle(docTitle);
+ };
// if the value has commas assume its an array (unless it's part of a chat gpt call indicated by '((' )
if (value?.includes(',') && !value.startsWith('((')) {
const values = value.split(',');
const strs = values.some(v => !v.match(/^[-]?[0-9.]$/));
this.Document[DocData][fieldKey] = strs ? new List(values) : new List(values.map(v => Number(v)));
} else if (value) {
- KeyValueBox.SetField(this.Document, fieldKey, assign + value, Doc.IsDataProto(this.Document) ? true : undefined, assign ? undefined:
- (gptval: FieldResult) => this.Document[DocData][fieldKey] = gptval as string ); // prettier-ignore
+ KeyValueBox.SetField(this.Document, fieldKey, assign + value, Doc.IsDataProto(this.Document) ? true : undefined, assign.includes(":=") ? undefined:
+ (gptval: FieldResult) => (dataDoc ? this.Document[DocData]:this.Document)[fieldKey] = gptval as string ); // prettier-ignore
}
- const target = getTitledDoc(docTitle);
- const fieldView = state.schema.nodes.dashField.create({ fieldKey, docId: target?.[Id], hideKey: false });
+ const target = docTitle ? getTitledDoc(docTitle) : undefined;
+ const fieldView = state.schema.nodes.dashField.create({ fieldKey, docId: target?.[Id], hideKey: false, dataDoc });
return state.tr.setSelection(new TextSelection(state.doc.resolve(start), state.doc.resolve(end))).replaceSelectionWith(fieldView, true);
},
{ inCode: true }
),
+ new InputRule(new RegExp(/(^|[^=])(\(\(.*\)\))/), (state, match, start, end) => {
+ var count = 0; // ignore first return value which will be the notation that chat is pending a result
+ KeyValueBox.SetField(this.Document, '', match[2], false, (gptval: FieldResult) => {
+ count && this.TextBox.EditorView?.dispatch(this.TextBox.EditorView!.state.tr.insertText(' ' + (gptval as string)));
+ count++;
+ });
+ return null;
+ }),
+
// create a text display of a metadata field on this or another document, or create a hyperlink portal to another document
// wiki:title
new InputRule(new RegExp(/wiki:([a-zA-Z_@:\.\?\-0-9]+ )$/), (state, match, start, end) => {
diff --git a/src/client/views/nodes/formattedText/marks_rts.ts b/src/client/views/nodes/formattedText/marks_rts.ts
index a141ef041..b68acc8f8 100644
--- a/src/client/views/nodes/formattedText/marks_rts.ts
+++ b/src/client/views/nodes/formattedText/marks_rts.ts
@@ -74,6 +74,7 @@ export const marks: { [index: string]: MarkSpec } = {
allAnchors: { default: [] as { href: string; title: string; anchorId: string }[] },
title: { default: null },
noPreview: { default: false },
+ fontSize: { default: null },
docref: { default: false }, // flags whether the linked text comes from a document within Dash. If so, an attribution label is appended after the text
},
inclusive: false,
@@ -93,14 +94,16 @@ export const marks: { [index: string]: MarkSpec } = {
const anchorids = node.attrs.allAnchors.reduce((p: string, item: { href: string; title: string; anchorId: string }) => (p ? p + ' ' + item.anchorId : item.anchorId), '');
return node.attrs.docref && node.attrs.title
? [
- 'div',
+ 'a',
['span', 0],
[
'span',
{
...node.attrs,
class: 'prosemirror-attribution',
+ 'data-targethrefs': targethrefs,
href: node.attrs.allAnchors[0].href,
+ style: `font-size: ${node.attrs.fontSize}`,
},
node.attrs.title,
],
diff --git a/src/client/views/nodes/formattedText/nodes_rts.ts b/src/client/views/nodes/formattedText/nodes_rts.ts
index c9115be90..905146ee2 100644
--- a/src/client/views/nodes/formattedText/nodes_rts.ts
+++ b/src/client/views/nodes/formattedText/nodes_rts.ts
@@ -265,6 +265,8 @@ export const nodes: { [index: string]: NodeSpec } = {
docId: { default: '' },
hideKey: { default: false },
editable: { default: true },
+ expanded: { default: null },
+ dataDoc: { default: false },
},
leafText: node => Field.toString((DocServer.GetCachedRefField(node.attrs.docId as string) as Doc)?.[node.attrs.fieldKey as string] as Field),
group: 'inline',
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index daae32e9f..4b40d11b9 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -445,6 +445,12 @@ export namespace Doc {
export function GetT(doc: Doc, key: string, ctor: ToConstructor, ignoreProto: boolean = false): FieldResult {
return Cast(Get(doc, key, ignoreProto), ctor) as FieldResult;
}
+ export function isTemplateDoc(doc: Doc) {
+ return GetT(doc, 'isTemplateDoc', 'boolean', true);
+ }
+ export function isTemplateForField(doc: Doc) {
+ return GetT(doc, 'isTemplateForField', 'string', true);
+ }
export function IsDataProto(doc: Doc) {
return GetT(doc, 'isDataDoc', 'boolean', true);
}
@@ -642,7 +648,7 @@ export namespace Doc {
cloneLinks: boolean,
cloneTemplates: boolean
): Promise {
- if (Doc.IsBaseProto(doc) || ((Doc.Get(doc, 'isTemplateDoc', true) || Doc.Get(doc, 'isTemplateForField', true)) && !cloneTemplates)) {
+ if (Doc.IsBaseProto(doc) || ((Doc.isTemplateDoc(doc) || Doc.isTemplateForField(doc)) && !cloneTemplates)) {
return doc;
}
if (cloneMap.get(doc[Id])) return cloneMap.get(doc[Id])!;
@@ -735,7 +741,7 @@ export namespace Doc {
const docAtKey = DocCast(clone[key]);
if (docAtKey && !Doc.IsSystem(docAtKey)) {
if (!Array.from(cloneMap.values()).includes(docAtKey)) {
- clone[key] = !cloneTemplates && (Doc.Get(docAtKey, 'isTemplateDoc', true) || Doc.Get(docAtKey, 'isTemplateForField', true)) ? docAtKey : cloneMap.get(docAtKey[Id]);
+ clone[key] = !cloneTemplates && (Doc.isTemplateDoc(docAtKey) || Doc.isTemplateForField(docAtKey)) ? docAtKey : cloneMap.get(docAtKey[Id]);
} else {
repairClone(docAtKey, cloneMap, cloneTemplates, visited);
}
@@ -857,7 +863,7 @@ export namespace Doc {
// of the original layout while allowing for individual layout properties to be overridden in the expanded layout.
export function expandTemplateLayout(templateLayoutDoc: Doc, targetDoc?: Doc) {
// nothing to do if the layout isn't a template or we don't have a target that's different than the template
- if (!targetDoc || templateLayoutDoc === targetDoc || (!templateLayoutDoc.isTemplateForField && !templateLayoutDoc.isTemplateDoc)) {
+ if (!targetDoc || templateLayoutDoc === targetDoc || (!Doc.isTemplateForField(templateLayoutDoc) && !Doc.isTemplateDoc(templateLayoutDoc))) {
return templateLayoutDoc;
}
@@ -874,7 +880,7 @@ export namespace Doc {
expandedTemplateLayout = undefined;
_pendingMap.add(targetDoc[Id] + expandedLayoutFieldKey);
} else if (expandedTemplateLayout === undefined && !_pendingMap.has(targetDoc[Id] + expandedLayoutFieldKey)) {
- if (templateLayoutDoc.resolvedDataDoc === (targetDoc.rootDocument ?? Doc.GetProto(targetDoc))) {
+ if (templateLayoutDoc.resolvedDataDoc === targetDoc[DocData]) {
expandedTemplateLayout = templateLayoutDoc; // reuse an existing template layout if its for the same document with the same params
} else {
templateLayoutDoc.resolvedDataDoc && (templateLayoutDoc = DocCast(templateLayoutDoc.proto, templateLayoutDoc)); // if the template has already been applied (ie, a nested template), then use the template's prototype
@@ -910,8 +916,9 @@ export namespace Doc {
console.log('Warning: GetLayoutDataDocPair childDoc not defined');
return { layout: childDoc, data: childDoc };
}
- const resolvedDataDoc = Doc.AreProtosEqual(containerDataDoc, containerDoc) || (!childDoc.isTemplateDoc && !childDoc.isTemplateForField) ? undefined : containerDataDoc;
- return { layout: Doc.expandTemplateLayout(childDoc, resolvedDataDoc), data: resolvedDataDoc };
+ const resolvedDataDoc = Doc.AreProtosEqual(containerDataDoc, containerDoc) || (!Doc.isTemplateDoc(childDoc) && !Doc.isTemplateForField(childDoc)) ? undefined : containerDataDoc;
+ const templateRoot = DocCast(containerDoc?.rootDocument);
+ return { layout: Doc.expandTemplateLayout(childDoc, templateRoot), data: resolvedDataDoc };
}
export function FindReferences(infield: Doc | List, references: Set, system: boolean | undefined) {
@@ -1035,20 +1042,13 @@ export namespace Doc {
// (ie, the 'data' doc), and then creates another delegate of that (ie, the 'layout' doc).
// This is appropriate if you're trying to create a document that behaves like all
// regularly created documents (e.g, text docs, pdfs, etc which all have data/layout docs)
- export function MakeDelegateWithProto(doc: Doc, id?: string, title?: string): Doc {
- const delegateProto = new Doc();
- delegateProto[Initializing] = true;
- delegateProto.proto = doc;
- delegateProto.author = Doc.CurrentUserEmail;
- delegateProto.isDataDoc = true;
- title && (delegateProto.title = title);
- const delegate = new Doc(id, true);
- delegate[Initializing] = true;
- delegate.proto = delegateProto;
- delegate.author = Doc.CurrentUserEmail;
- delegate[Initializing] = false;
- delegateProto[Initializing] = false;
- return delegate;
+ export function MakeDelegateWithProto(doc: Doc, id?: string, title?: string) {
+ const ndoc = Doc.ApplyTemplate(doc);
+ if (ndoc) {
+ Doc.GetProto(ndoc).isDataDoc = true;
+ ndoc && (Doc.GetProto(ndoc).proto = doc);
+ }
+ return ndoc;
}
let _applyCount: number = 0;
@@ -1671,7 +1671,7 @@ ScriptingGlobals.add(function getEmbedding(doc: any) {
return Doc.MakeEmbedding(doc);
});
ScriptingGlobals.add(function getCopy(doc: any, copyProto: any) {
- return doc.isTemplateDoc ? Doc.ApplyTemplate(doc) : Doc.MakeCopy(doc, copyProto);
+ return doc.isTemplateDoc ? Doc.MakeDelegateWithProto(doc) : Doc.MakeCopy(doc, copyProto);
});
ScriptingGlobals.add(function copyField(field: any) {
return Field.Copy(field);
diff --git a/src/fields/util.ts b/src/fields/util.ts
index b73520999..c2ec3f13a 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -286,6 +286,10 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc
// target should be either a Doc or ListImpl. receiver should be a Proxy Or List.
//
export function setter(target: any, in_prop: string | symbol | number, value: any, receiver: any): boolean {
+ if (!in_prop) {
+ console.log('WARNING: trying to set an empty property. This should be fixed. ');
+ return false;
+ }
let prop = in_prop;
const effectiveAcl = in_prop === 'constructor' || typeof in_prop === 'symbol' ? AclAdmin : GetPropAcl(target, prop);
if (effectiveAcl !== AclEdit && effectiveAcl !== AclAugment && effectiveAcl !== AclAdmin) return true;
--
cgit v1.2.3-70-g09d2
From 4ea0ea97359cf2f3e97367dafbfaf9157ecc987b Mon Sep 17 00:00:00 2001
From: bobzel
Date: Sat, 23 Mar 2024 15:30:49 -0400
Subject: made backspace deleting a schema row undoable. fixed calling chat on
string fields
---
src/client/views/collections/collectionSchema/CollectionSchemaView.tsx | 2 +-
src/client/views/nodes/KeyValueBox.tsx | 3 ++-
2 files changed, 3 insertions(+), 2 deletions(-)
(limited to 'src/client/views/nodes/KeyValueBox.tsx')
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index 4a0ca8fe5..5f4948808 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -238,7 +238,7 @@ export class CollectionSchemaView extends CollectionSubView() {
}
break;
case 'Backspace': {
- this.removeDocument(this._selectedDocs);
+ undoable(() => this.removeDocument(this._selectedDocs), 'delete schema row');
break;
}
case 'Escape': {
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index d85432631..5394c7dbb 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -105,7 +105,8 @@ export class KeyValueBox extends ObservableReactComponent {
default: {
const _setCacheResult_ = (value: FieldResult) => {
field = value as Field;
- setResult?.(value);
+ if (setResult) setResult?.(value);
+ else target[key] = field;
};
const res = script.run({ this: Doc.Layout(doc), self: doc, _setCacheResult_ }, console.log);
if (!res.success) {
--
cgit v1.2.3-70-g09d2
From b420caf2c7ecd386cae2cc550904522474b541aa Mon Sep 17 00:00:00 2001
From: bobzel
Date: Tue, 26 Mar 2024 22:34:10 -0400
Subject: added empty image tool and click on empty image to select from
filesystem. fixed following links in lightbox and showing links to
stackedTimelines. fixed embedding docs into text. fixed not resizing text
boxes that also show up in pivot view. prevent context menu from going off
top of screen. fixed freeform clustering colors and click to type. fixed
links to stackedTimeline marks, and titles for marks. made title editing
from doc deco and header use same syntax as keyValue. fixed marquee
selection on webBoxes. turn off transitions in freeformdocview after
timeout. enabled iconifying templates to propagate to "offspring". fixes
images in templates. don't show headr on schema views.
---
src/client/documents/Documents.ts | 52 ++++++++--------
src/client/util/DocumentManager.ts | 4 +-
src/client/views/ContextMenu.tsx | 6 +-
src/client/views/DocumentDecorations.tsx | 71 ++++++++--------------
src/client/views/FieldsDropdown.tsx | 1 -
src/client/views/LightboxView.tsx | 2 +
src/client/views/MarqueeAnnotator.tsx | 19 ++++--
src/client/views/ObservableReactComponent.tsx | 3 +-
src/client/views/PropertiesView.tsx | 2 +-
src/client/views/StyleProvider.tsx | 6 +-
.../collections/CollectionStackedTimeline.tsx | 13 ++--
.../CollectionFreeFormLayoutEngines.tsx | 2 +-
.../collectionFreeForm/CollectionFreeFormView.tsx | 11 ++--
.../collections/collectionFreeForm/MarqueeView.tsx | 4 +-
.../collectionSchema/SchemaTableCell.tsx | 2 +-
src/client/views/global/globalScripts.ts | 4 +-
.../views/nodes/CollectionFreeFormDocumentView.tsx | 24 ++++++--
src/client/views/nodes/DocumentContentsView.tsx | 7 ++-
src/client/views/nodes/DocumentView.tsx | 35 +++++++----
src/client/views/nodes/FieldView.tsx | 1 +
src/client/views/nodes/ImageBox.tsx | 55 +++++++++++++----
src/client/views/nodes/KeyValueBox.tsx | 2 +-
src/client/views/nodes/LabelBox.tsx | 2 +-
src/client/views/nodes/LinkBox.tsx | 47 ++++++++++----
src/client/views/nodes/VideoBox.tsx | 5 +-
src/client/views/nodes/WebBox.tsx | 65 ++++++++++++++------
.../views/nodes/formattedText/FormattedTextBox.tsx | 17 ++++--
src/client/views/nodes/trails/PresBox.tsx | 2 +-
src/fields/Doc.ts | 29 +++++----
src/fields/PresField.ts | 6 --
src/fields/RichTextField.ts | 7 ---
31 files changed, 311 insertions(+), 195 deletions(-)
delete mode 100644 src/fields/PresField.ts
(limited to 'src/client/views/nodes/KeyValueBox.tsx')
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 17cb6fef8..b63c5e429 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -1017,8 +1017,8 @@ export namespace Docs {
}
export function ImageDocument(url: string | ImageField, options: DocumentOptions = {}, overwriteDoc?: Doc) {
- const imgField = url instanceof ImageField ? url : new ImageField(url);
- return InstanceFromProto(Prototypes.get(DocumentType.IMG), imgField, { title: basename(imgField.url.href), ...options }, undefined, undefined, undefined, overwriteDoc);
+ const imgField = url instanceof ImageField ? url : url ? new ImageField(url) : undefined;
+ return InstanceFromProto(Prototypes.get(DocumentType.IMG), imgField, { title: basename(imgField?.url.href ?? '-no image-'), ...options }, undefined, undefined, undefined, overwriteDoc);
}
export function PresDocument(options: DocumentOptions = {}) {
@@ -1950,6 +1950,31 @@ export namespace DocUtils {
return dd;
}
+ export function assignImageInfo(result: Upload.FileInformation, proto: Doc) {
+ if (Upload.isImageInformation(result)) {
+ const maxNativeDim = Math.min(Math.max(result.nativeHeight, result.nativeWidth), defaultNativeImageDim);
+ const exifRotation = StrCast((result.exifData?.data as any)?.Orientation).toLowerCase();
+ proto['data-nativeOrientation'] = result.exifData?.data?.image?.Orientation ?? (exifRotation.includes('rotate 90') || exifRotation.includes('rotate 270') ? 5 : undefined);
+ proto['data_nativeWidth'] = result.nativeWidth < result.nativeHeight ? (maxNativeDim * result.nativeWidth) / result.nativeHeight : maxNativeDim;
+ proto['data_nativeHeight'] = result.nativeWidth < result.nativeHeight ? maxNativeDim : maxNativeDim / (result.nativeWidth / result.nativeHeight);
+ if (NumCast(proto['data-nativeOrientation']) >= 5) {
+ proto['data_nativeHeight'] = result.nativeWidth < result.nativeHeight ? (maxNativeDim * result.nativeWidth) / result.nativeHeight : maxNativeDim;
+ proto['data_nativeWidth'] = result.nativeWidth < result.nativeHeight ? maxNativeDim : maxNativeDim / (result.nativeWidth / result.nativeHeight);
+ }
+ proto.data_exif = JSON.stringify(result.exifData?.data);
+ proto.data_contentSize = result.contentSize;
+ // exif gps data coordinates are stored in DMS (Degrees Minutes Seconds), the following operation converts that to decimal coordinates
+ const latitude = result.exifData?.data?.GPSLatitude;
+ const latitudeDirection = result.exifData?.data?.GPSLatitudeRef;
+ const longitude = result.exifData?.data?.GPSLongitude;
+ const longitudeDirection = result.exifData?.data?.GPSLongitudeRef;
+ if (latitude !== undefined && longitude !== undefined && latitudeDirection !== undefined && longitudeDirection !== undefined) {
+ proto.latitude = ConvertDMSToDD(latitude[0], latitude[1], latitude[2], latitudeDirection);
+ proto.longitude = ConvertDMSToDD(longitude[0], longitude[1], longitude[2], longitudeDirection);
+ }
+ }
+ }
+
async function processFileupload(generatedDocuments: Doc[], name: string, type: string, result: Error | Upload.FileInformation, options: DocumentOptions, overwriteDoc?: Doc) {
if (result instanceof Error) {
alert(`Upload failed: ${result.message}`);
@@ -1961,28 +1986,7 @@ export namespace DocUtils {
if (doc) {
const proto = Doc.GetProto(doc);
proto.text = result.rawText;
- if (Upload.isImageInformation(result)) {
- const maxNativeDim = Math.min(Math.max(result.nativeHeight, result.nativeWidth), defaultNativeImageDim);
- const exifRotation = StrCast((result.exifData?.data as any)?.Orientation).toLowerCase();
- proto['data-nativeOrientation'] = result.exifData?.data?.image?.Orientation ?? (exifRotation.includes('rotate 90') || exifRotation.includes('rotate 270') ? 5 : undefined);
- proto['data_nativeWidth'] = result.nativeWidth < result.nativeHeight ? (maxNativeDim * result.nativeWidth) / result.nativeHeight : maxNativeDim;
- proto['data_nativeHeight'] = result.nativeWidth < result.nativeHeight ? maxNativeDim : maxNativeDim / (result.nativeWidth / result.nativeHeight);
- if (NumCast(proto['data-nativeOrientation']) >= 5) {
- proto['data_nativeHeight'] = result.nativeWidth < result.nativeHeight ? (maxNativeDim * result.nativeWidth) / result.nativeHeight : maxNativeDim;
- proto['data_nativeWidth'] = result.nativeWidth < result.nativeHeight ? maxNativeDim : maxNativeDim / (result.nativeWidth / result.nativeHeight);
- }
- proto.data_exif = JSON.stringify(result.exifData?.data);
- proto.data_contentSize = result.contentSize;
- // exif gps data coordinates are stored in DMS (Degrees Minutes Seconds), the following operation converts that to decimal coordinates
- const latitude = result.exifData?.data?.GPSLatitude;
- const latitudeDirection = result.exifData?.data?.GPSLatitudeRef;
- const longitude = result.exifData?.data?.GPSLongitude;
- const longitudeDirection = result.exifData?.data?.GPSLongitudeRef;
- if (latitude !== undefined && longitude !== undefined && latitudeDirection !== undefined && longitudeDirection !== undefined) {
- proto.latitude = ConvertDMSToDD(latitude[0], latitude[1], latitude[2], latitudeDirection);
- proto.longitude = ConvertDMSToDD(longitude[0], longitude[1], longitude[2], longitudeDirection);
- }
- }
+ !(result instanceof Error) && DocUtils.assignImageInfo(result, proto);
if (Upload.isVideoInformation(result)) {
proto.data_duration = result.duration;
}
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index a38a330da..40d28c690 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -59,7 +59,7 @@ export class DocumentManager {
private _viewRenderedCbs: { doc: Doc; func: (dv: DocumentView) => any }[] = [];
public AddViewRenderedCb = (doc: Opt, func: (dv: DocumentView) => any) => {
if (doc) {
- const dv = this.getDocumentView(doc);
+ const dv = LightboxView.LightboxDoc ? this.getLightboxDocumentView(doc) : this.getDocumentView(doc);
this._viewRenderedCbs.push({ doc, func });
if (dv) {
this.callAddViewFuncs(dv);
@@ -262,7 +262,7 @@ export class DocumentManager {
return res(this.getDocumentView(docContextPath[0])!);
}
options.didMove = true;
- docContextPath.some(doc => TabDocView.Activate(doc)) || DocumentViewInternal.addDocTabFunc(docContextPath[0], options.openLocation ?? OpenWhere.addRight);
+ (!LightboxView.LightboxDoc && docContextPath.some(doc => TabDocView.Activate(doc))) || DocumentViewInternal.addDocTabFunc(docContextPath[0], options.openLocation ?? OpenWhere.addRight);
this.AddViewRenderedCb(docContextPath[0], dv => res(dv));
}));
if (options.openLocation === OpenWhere.lightbox) {
diff --git a/src/client/views/ContextMenu.tsx b/src/client/views/ContextMenu.tsx
index 8f4e43978..ca877b93e 100644
--- a/src/client/views/ContextMenu.tsx
+++ b/src/client/views/ContextMenu.tsx
@@ -218,11 +218,12 @@ export class ContextMenu extends ObservableReactComponent<{}> {
this._width = DivWidth(r);
this._height = DivHeight(r);
}
+ this._searchRef.current?.focus();
})}
style={{
display: this._display ? '' : 'none',
left: this.pageX,
- ...(this._yRelativeToTop ? { top: this.pageY } : { bottom: this.pageY }),
+ ...(this._yRelativeToTop ? { top: Math.max(0, this.pageY) } : { bottom: this.pageY }),
background: SettingsManager.userBackgroundColor,
color: SettingsManager.userColor,
}}>
@@ -265,7 +266,8 @@ export class ContextMenu extends ObservableReactComponent<{}> {
const item = this.flatItems[this._selectedIndex];
if (item) {
item.event({ x: this.pageX, y: this.pageY });
- } else if (this._searchString.startsWith(this._defaultPrefix)) {
+ } else {
+ //if (this._searchString.startsWith(this._defaultPrefix)) {
this._defaultItem?.(this._searchString.substring(this._defaultPrefix.length));
}
this.closeMenu();
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 2fb9f0fc1..4d9b93896 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -34,6 +34,7 @@ import { Colors } from './global/globalEnums';
import { DocumentView, OpenWhereMod } from './nodes/DocumentView';
import { ImageBox } from './nodes/ImageBox';
import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
+import { KeyValueBox } from './nodes/KeyValueBox';
interface DocumentDecorationsProps {
PanelWidth: number;
@@ -57,7 +58,7 @@ export class DocumentDecorations extends ObservableReactComponent {
- if (this._accumulatedTitle.startsWith('#') || this._accumulatedTitle.startsWith('=')) {
+ if (this._accumulatedTitle.startsWith('$')) {
this._titleControlString = this._accumulatedTitle;
- } else if (this._titleControlString.startsWith('#')) {
+ } else if (this._titleControlString.startsWith('$')) {
if (this._accumulatedTitle.startsWith('-->#')) {
SelectionManager.Docs.forEach(doc => (doc[DocData].onViewMounted = ScriptField.MakeScript(`updateTagsCollection(this)`)));
}
@@ -131,26 +132,7 @@ export class DocumentDecorations extends ObservableReactComponent')) {
- const title = titleField.toString().replace(/\.?/, '');
- const curKey = Doc.LayoutFieldKey(d.Document);
- if (curKey !== title) {
- if (title) {
- if (d.dataDoc[title] === undefined || d.dataDoc[title] instanceof RichTextField || typeof d.dataDoc[title] === 'string') {
- d.Document.layout_fieldKey = `layout_${title}`;
- d.Document[`layout_${title}`] = FormattedTextBox.LayoutString(title);
- d.Document[`${title}_nativeWidth`] = d.Document[`${title}_nativeHeight`] = 0;
- }
- } else {
- d.Document.layout_fieldKey = undefined;
- }
- }
- } else {
- Doc.SetInPlace(d.Document, titleFieldKey, titleField, true);
- }
+ KeyValueBox.SetField(d.Document, titleFieldKey, this._accumulatedTitle);
}),
'edit title'
);
@@ -181,7 +163,8 @@ export class DocumentDecorations extends ObservableReactComponent this.onBackgroundMove(true, e),
emptyFunction,
action(e => {
- !this._editingTitle && (this._accumulatedTitle = this._titleControlString.startsWith('#') ? this.selectionTitle : this._titleControlString);
+ const selected = SelectionManager.Views.length === 1 ? SelectionManager.Docs[0] : undefined;
+ !this._editingTitle && (this._accumulatedTitle = this._titleControlString.startsWith('$') ? (selected && Field.toKeyValueString(selected, this._titleControlString.substring(1))) || '-unset-' : this._titleControlString);
this._editingTitle = true;
this._keyinput.current && setTimeout(this._keyinput.current.focus);
})
@@ -622,11 +605,8 @@ export class DocumentDecorations extends ObservableReactComponent {
- this._editingTitle = false;
- !hideTitle && this.titleBlur();
- })}
- onChange={action(e => !hideTitle && (this._accumulatedTitle = e.target.value))}
- onKeyDown={hideTitle ? emptyFunction : this.titleEntered}
- onPointerDown={e => e.stopPropagation()}
- />
+ <>
+ {r - x < 150 ? null : {this._titleControlString + ':'} }
+ {
+ this._editingTitle = false;
+ !hideTitle && this.titleBlur();
+ })}
+ onChange={action(e => !hideTitle && (this._accumulatedTitle = e.target.value))}
+ onKeyDown={hideTitle ? emptyFunction : this.titleEntered}
+ onPointerDown={e => e.stopPropagation()}
+ />
+ >
) : (
{hideTitle ? null : (
diff --git a/src/client/views/FieldsDropdown.tsx b/src/client/views/FieldsDropdown.tsx
index 5638d34c6..6a5c2cb4c 100644
--- a/src/client/views/FieldsDropdown.tsx
+++ b/src/client/views/FieldsDropdown.tsx
@@ -61,7 +61,6 @@ export class FieldsDropdown extends ObservableReactComponent
filteredOptions.push(pair[0]));
const options = filteredOptions.sort().map(facet => ({ value: facet, label: facet }));
- console.log(options);
return (
{
}}>
(this._docView = r !== null ? r : undefined))}
Document={this._doc}
PanelWidth={this.lightboxWidth}
diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx
index bd6be2519..c29474fcd 100644
--- a/src/client/views/MarqueeAnnotator.tsx
+++ b/src/client/views/MarqueeAnnotator.tsx
@@ -34,7 +34,7 @@ export interface MarqueeAnnotatorProps {
annotationLayer: HTMLDivElement;
addDocument: (doc: Doc) => boolean;
getPageFromScroll?: (top: number) => number;
- finishMarquee: (x?: number, y?: number, PointerEvent?: PointerEvent) => void;
+ finishMarquee: (x?: number, y?: number) => void;
anchorMenuClick?: () => undefined | ((anchor: Doc) => void);
anchorMenuCrop?: (anchor: Doc | undefined, addCrop: boolean) => Doc | undefined;
highlightDragSrcColor?: string;
@@ -168,7 +168,6 @@ export class MarqueeAnnotator extends ObservableReactComponent {
+ const movLoc = this.getTransformedScreenPt(pt);
+ this._width = movLoc.x - this._start.x;
+ this._height = movLoc.y - this._start.y;
+ };
+
@action
onSelectMove = (e: PointerEvent) => {
const movLoc = this.getTransformedScreenPt([e.clientX, e.clientY]);
@@ -248,9 +254,12 @@ export class MarqueeAnnotator extends ObservableReactComponent {
e.stopPropagation();
+ this.onEnd(e.clientX, e.clientY);
+ };
+ @action
+ onEnd = (x: number, y: number) => {
const marquees = this.props.marqueeContainer.getElementsByClassName('marqueeAnnotator-dragBox');
const marqueeStyle = (Array.from(marquees).lastElement() as HTMLDivElement)?.style;
if (!this.isEmpty && marqueeStyle) {
@@ -266,9 +275,9 @@ export class MarqueeAnnotator extends ObservableReactComponent extends React.Component
makeObservable(this);
}
componentDidUpdate(prevProps: Readonly): void {
- Object.keys(prevProps).forEach(action(pkey =>
- (prevProps as any)[pkey] !== (this.props as any)[pkey] &&
+ Object.keys(prevProps).filter(pkey => (prevProps as any)[pkey] !== (this.props as any)[pkey]).forEach(action(pkey =>
((this._props as any)[pkey] = (this.props as any)[pkey]))); // prettier-ignore
}
}
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index cbd3ff358..195b1a04c 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -1399,7 +1399,7 @@ export class PropertiesView extends ObservableReactComponent
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index 1adb0d9e5..7a15e67e4 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -25,6 +25,7 @@ import { FieldViewProps } from './nodes/FieldView';
import { KeyValueBox } from './nodes/KeyValueBox';
import { PropertiesView } from './PropertiesView';
import './StyleProvider.scss';
+import { CollectionSchemaView } from './collections/collectionSchema/CollectionSchemaView';
export enum StyleProp {
TreeViewIcon = 'treeView_Icon',
@@ -137,14 +138,15 @@ export function DefaultStyleProvider(doc: Opt, props: Opt boolean | undefined;
ScreenToLocalTransform: () => Transform;
+ containerViewPath?: () => DocumentView[];
_timeline: HTMLDivElement | null;
focus: FocusFuncType;
currentTimecode: () => number;
@@ -796,12 +798,15 @@ class StackedTimelineAnchor extends ObservableReactComponent {
+ this._props.mark[DocData].title = ComputedField.MakeFunction(
+ `["${this._props.endTag}"] ? "#" + formatToTime(this["${this._props.startTag}"]) + "-" + formatToTime(this["${this._props.endTag}"]) : "#" + formatToTime(this["${this._props.startTag}"]`
+ );
+ };
// context menu
contextMenuItems = () => {
const resetTitle = {
- script: ScriptField.MakeFunction(
- `this.title = this["${this._props.endTag}"] ? "#" + formatToTime(this["${this._props.startTag}"]) + "-" + formatToTime(this["${this._props.endTag}"]) : "#" + formatToTime(this["${this._props.startTag}"])`
- )!,
+ method: this.resetTitle,
icon: 'folder-plus',
label: 'Reset Title',
};
@@ -826,7 +831,7 @@ class StackedTimelineAnchor extends ObservableReactComponent (anchor.view = r))}
Document={mark}
TemplateDataDocument={undefined}
- containerViewPath={returnEmptyDoclist}
+ containerViewPath={this._props.containerViewPath}
pointerEvents={this.noEvents ? returnNone : undefined}
styleProvider={this._props.styleProvider}
renderDepth={this._props.renderDepth + 1}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index b8c0967c1..6484658e0 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -388,7 +388,7 @@ function normalizeResults(
minWidth: number,
extras: ViewDefBounds[]
): ViewDefResult[] {
- const grpEles = groupNames.map(gn => ({ x: gn.x, y: gn.y, width: gn.width, height: gn.height } as ViewDefBounds));
+ const grpEles = groupNames.map(gn => ({ x: gn.x, y: gn.y, width: gn.width, height: gn.height }) as ViewDefBounds);
const docEles = Array.from(docMap.entries()).map(ele => ele[1]);
const aggBounds = aggregateBounds(
extras.concat(grpEles.concat(docEles.map(de => ({ ...de, type: 'doc', payload: '' })))).filter(e => e.zIndex !== -99),
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 1fd453e96..5495735ea 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -565,7 +565,7 @@ export class CollectionFreeFormView extends CollectionSubView, props: Opt, property: string) => {
let styleProp = this._props.styleProvider?.(doc, props, property); // bcz: check 'props' used to be renderDepth + 1
if (doc && this.childDocList?.includes(doc))
- switch (property) {
+ switch (property.split(':')[0]) {
case StyleProp.BackgroundColor:
const cluster = NumCast(doc?.layout_cluster);
if (this.Document._freeform_useClusters && doc?.type !== DocumentType.IMG) {
@@ -590,7 +590,7 @@ export class CollectionFreeFormView extends CollectionSubView {
- if (this._hitCluster !== -1) {
+ if (addToSel && this._hitCluster !== -1) {
!addToSel && SelectionManager.DeselectAll();
const eles = this.childLayoutPairs.map(pair => pair.layout).filter(cd => (this.Document._freeform_useClusters ? NumCast(cd.layout_cluster) : NumCast(cd.group, -1)) === this._hitCluster);
this.selectDocuments(eles);
@@ -1288,15 +1288,16 @@ export class CollectionFreeFormView extends CollectionSubView this._props.addDocTab(Docs.Create.WebDocument(`https://bing.com/search?q=${str}`, { _width: 400, x, y, _height: 512, _nativeWidth: 850, title: 'bing', data_useCors: true }), OpenWhere.addRight));
+ cm.setDefaultItem('?', (str: string) =>
+ this._props.addDocTab(Docs.Create.WebDocument(`https://wikipedia.org/wiki/${str}`, { _width: 400, x, y, _height: 512, _nativeWidth: 850, title: `wiki:${str}`, data_useCors: true }), OpenWhere.addRight)
+ );
cm.displayMenu(this._downX, this._downY, undefined, true);
e.stopPropagation();
} else if (e.key === 'u' && this._props.ungroup) {
diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
index 67dc508d5..bda8afa41 100644
--- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
@@ -213,7 +213,7 @@ export class SchemaImageCell extends ObservableReactComponent Cast(doc[Doc.LayoutFieldKey(doc)], ImageField, null)?.url)
.filter(url => url)
diff --git a/src/client/views/global/globalScripts.ts b/src/client/views/global/globalScripts.ts
index 3fdc9a488..d12639fc7 100644
--- a/src/client/views/global/globalScripts.ts
+++ b/src/client/views/global/globalScripts.ts
@@ -87,8 +87,8 @@ ScriptingGlobals.add(function setHeaderColor(color?: string, checkResult?: boole
});
} else {
Doc.SharingDoc().headingColor = undefined;
- Doc.GetProto(Doc.SharingDoc()).headingColor = color;
- Doc.UserDoc().layout_showTitle = color === 'transparent' ? undefined : StrCast(Doc.UserDoc().layout_showTitle, 'author_date');
+ Doc.GetProto(Doc.SharingDoc()).headingColor = color === 'transparent' ? undefined : color;
+ Doc.UserDoc().layout_showTitle = color === 'transparent' ? undefined : StrCast(Doc.UserDoc().layout_showTitle, 'title');
}
});
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 2800ea200..0d0a7c623 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -1,4 +1,4 @@
-import { action, makeObservable, observable } from 'mobx';
+import { action, makeObservable, observable, trace } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { OmitKeys, numberRange } from '../../../Utils';
@@ -17,6 +17,7 @@ import { CollectionFreeFormView } from '../collections/collectionFreeForm/Collec
import './CollectionFreeFormDocumentView.scss';
import { DocumentView, DocumentViewProps, OpenWhere } from './DocumentView';
import { FieldViewProps } from './FieldView';
+import { TransitionTimer } from '../../../fields/DocSymbols';
/// Ugh, typescript has no run-time way of iterating through the keys of an interface. so we need
/// manaully keep this list of keys in synch wih the fields of the freeFormProps interface
@@ -91,6 +92,16 @@ export class CollectionFreeFormDocumentView extends DocComponent (this.Document[TransitionTimer] = this.Transition = undefined)),
+ num
+ );
+ }
+ }
+
componentDidUpdate(prevProps: Readonly>) {
super.componentDidUpdate(prevProps);
this.WrapperKeys.forEach(action(keys => ((this as any)[keys.upper] = (this.props as any)[keys.lower])));
@@ -98,14 +109,14 @@ export class CollectionFreeFormDocumentView extends DocComponent this._props.transition; // prettier-ignore
+ DataTransition = () => this.Transition || StrCast(this.Document.dataTransition); // prettier-ignore
RenderCutoffProvider = this.props.RenderCutoffProvider; // needed for type checking
PanelWidth = () => this._props.autoDim ? this._props.PanelWidth?.() : this.Width; // prettier-ignore
PanelHeight = () => this._props.autoDim ? this._props.PanelHeight?.() : this.Height; // prettier-ignore
styleProvider = (doc: Doc | undefined, props: Opt, property: string) => {
if (doc === this.layoutDoc) {
- switch (property) {
+ switch (property.split(':')[0]) {
case StyleProp.Opacity: return this.Opacity; // only change the opacity for this specific document, not its children
case StyleProp.BackgroundColor: return this.BackgroundColor;
case StyleProp.Color: return this.Color;
@@ -224,7 +235,8 @@ export class CollectionFreeFormDocumentView extends DocComponent {
if (this.CollectionFreeFormView.isAnyChildContentActive()) return undefined;
- const isGroup = this.dataDoc.isGroup && (!this.layoutDoc.backgroundColor || this.layoutDoc.backgroundColor === 'transparent');
+ const backColor = this.BackgroundColor;
+ const isGroup = this.dataDoc.isGroup && (!backColor || backColor === 'transparent');
return isGroup ? (this._props.isDocumentActive?.() ? 'group' : this._props.isGroupActive?.() ? 'child' : 'inactive') : this._props.isGroupActive?.() ? 'child' : undefined;
};
render() {
@@ -237,7 +249,7 @@ export class CollectionFreeFormDocumentView extends DocComponent
@@ -251,6 +263,8 @@ export class CollectionFreeFormDocumentView extends DocComponent
)}
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 5a326ecb0..e729e2fa2 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -7,7 +7,7 @@ import { OmitKeys, Without, emptyPath } from '../../../Utils';
import { Doc, Opt } from '../../../fields/Doc';
import { AclPrivate, DocData } from '../../../fields/DocSymbols';
import { ScriptField } from '../../../fields/ScriptField';
-import { Cast, StrCast } from '../../../fields/Types';
+import { Cast, DocCast, StrCast } from '../../../fields/Types';
import { GetEffectiveAcl, TraceMobx } from '../../../fields/util';
import { InkingStroke } from '../InkingStroke';
import { ObservableReactComponent } from '../ObservableReactComponent';
@@ -128,7 +128,9 @@ export class DocumentContentsView extends ObservableReactComponentawaiting layout';
if (this._props.layoutFieldKey === 'layout_keyValue') return StrCast(this._props.Document.layout_keyValue, KeyValueBox.LayoutString());
- const layout = Cast(this.layoutDoc[this.layoutDoc === this._props.Document && this._props.layoutFieldKey ? this._props.layoutFieldKey : StrCast(this.layoutDoc.layout_fieldKey, 'layout')], 'string');
+ const tempLayout = DocCast(this.layoutDoc[this.layoutDoc === this._props.Document && this._props.layoutFieldKey ? this._props.layoutFieldKey : StrCast(this.layoutDoc.layout_fieldKey, 'layout')]);
+ const layoutDoc = tempLayout ?? this.layoutDoc;
+ const layout = Cast(layoutDoc[layoutDoc === this._props.Document && this._props.layoutFieldKey ? this._props.layoutFieldKey : StrCast(layoutDoc.layout_fieldKey, 'layout')], 'string');
if (layout === undefined) return this._props.Document.data ? " " : KeyValueBox.LayoutString();
if (typeof layout === 'string') return layout;
return 'Loading layout
';
@@ -154,6 +156,7 @@ export class DocumentContentsView extends ObservableReactComponent string | undefined;
NativeWidth?: () => number;
NativeHeight?: () => number;
- contextMenuItems?: () => { script: ScriptField; filter?: ScriptField; label: string; icon: string }[];
+ contextMenuItems?: () => { script?: ScriptField; method?: () => void; filter?: ScriptField; label: string; icon: string }[];
dragConfig?: (data: DragManager.DocumentDragData) => void;
dragStarting?: () => void;
dragEnding?: () => void;
@@ -560,7 +559,13 @@ export class DocumentViewInternal extends DocComponent
cm.addItem({ description: label, event: () => customScripts[i]?.script.run({ documentView: this, this: this.Document, scriptContext: this._props.scriptContext }), icon: 'sticky-note' })
);
- this._props.contextMenuItems?.().forEach(item => item.label && cm.addItem({ description: item.label, event: () => item.script.script.run({ this: this.Document, scriptContext: this._props.scriptContext }), icon: item.icon as IconProp }));
+ this._props
+ .contextMenuItems?.()
+ .forEach(
+ item =>
+ item.label &&
+ cm.addItem({ description: item.label, event: () => (item.method ? item.method() : item.script?.script.run({ this: this.Document, documentView: this, scriptContext: this._props.scriptContext })), icon: item.icon as IconProp })
+ );
if (!this.Document.isFolder) {
const templateDoc = Cast(this.Document[StrCast(this.Document.layout_fieldKey)], Doc, null);
@@ -597,7 +602,7 @@ export class DocumentViewInternal extends DocComponent this.toggleFollowLink(false, false), icon: 'link' });
!Doc.noviceMode && onClicks.push({ description: 'Edit onClick Script', event: () => UndoManager.RunInBatch(() => DocUtils.makeCustomViewClicked(this.Document, undefined, 'onClick'), 'edit onClick'), icon: 'terminal' });
- cm.addItem({ description: 'OnClick...', noexpand: true, subitems: onClicks, icon: 'mouse-pointer' });
+ !existingOnClick && cm.addItem({ description: 'OnClick...', noexpand: true, subitems: onClicks, icon: 'mouse-pointer' });
} else if (LinkManager.Links(this.Document).length) {
onClicks.push({ description: 'Restore On Click default', event: () => this.noOnClick(), icon: 'link' });
onClicks.push({ description: 'Follow Link on Click', event: () => this.followLinkOnClick(), icon: 'link' });
@@ -810,8 +815,7 @@ export class DocumentViewInternal extends DocComponent Field.toString(targetDoc[field.trim()] as Field))
+ .map(field => Field.toJavascriptString(this.Document[field] as Field))
.join(' \\ ') || '-unset-'
}
display="block"
oneLine={true}
fontSize={(this.titleHeight / 15) * 10}
- GetValue={() => (showTitle.split(';').length !== 1 ? '#' + showTitle : Field.toKeyValueString(this.Document, showTitle.split(';')[0]))}
+ GetValue={() =>
+ showTitle
+ .split(';')
+ .map(field => Field.toKeyValueString(this.Document, field))
+ .join('\\')
+ }
SetValue={undoBatch((input: string) => {
- if (input?.startsWith('#')) {
+ if (input?.startsWith('$')) {
if (this.layoutDoc.layout_showTitle) {
- this.layoutDoc._layout_showTitle = input?.substring(1);
+ this.layoutDoc._layout_showTitle = input?.substring(1) ? input.substring(1) : undefined;
} else if (!this._props.layout_showTitle) {
- Doc.UserDoc().layout_showTitle = input?.substring(1) ?? 'author_date';
+ Doc.UserDoc().layout_showTitle = input?.substring(1) ? input.substring(1) : 'title';
}
- } else if (showTitle && !showTitle.includes('Date') && showTitle !== 'author') {
+ } else if (showTitle && !showTitle.includes(';') && !showTitle.includes('Date') && showTitle !== 'author') {
KeyValueBox.SetField(targetDoc, showTitle, input);
}
return true;
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 5b47dd91d..771856788 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -92,6 +92,7 @@ export interface FieldViewSharedProps {
waitForDoubleClickToClick?: () => 'never' | 'always' | undefined;
defaultDoubleClick?: () => 'default' | 'ignore' | undefined;
pointerEvents?: () => Opt;
+ suppressSetHeight?: boolean;
}
/**
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 251235b93..86e8ed60a 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -9,7 +9,7 @@ import { DocData } from '../../../fields/DocSymbols';
import { Id } from '../../../fields/FieldSymbols';
import { InkTool } from '../../../fields/InkField';
import { ObjectField } from '../../../fields/ObjectField';
-import { Cast, NumCast, StrCast } from '../../../fields/Types';
+import { Cast, ImageCast, NumCast, StrCast } from '../../../fields/Types';
import { ImageField } from '../../../fields/URLField';
import { TraceMobx } from '../../../fields/util';
import { DashColor, emptyFunction, returnEmptyString, returnFalse, returnOne, returnZero, setupMoveUpEvents, Utils } from '../../../Utils';
@@ -29,6 +29,12 @@ import { OpenWhere } from './DocumentView';
import { FocusViewOptions, FieldView, FieldViewProps } from './FieldView';
import './ImageBox.scss';
import { PinProps, PresBox } from './trails';
+import { Colors } from 'browndash-components';
+import { listSpec } from '../../../fields/Schema';
+import { List } from '../../../fields/List';
+import { url } from 'inspector';
+import { OverlayView } from '../OverlayView';
+import { Networking } from '../../Network';
@observer
export class ImageBox extends ViewBoxAnnotatableComponent() implements ViewBoxInterface {
@@ -134,7 +140,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent() impl
if (de.metaKey || targetIsBullseye(e.target as HTMLElement)) {
added = de.complete.docDragData.droppedDocuments.reduce((last: boolean, drop: Doc) => {
this.layoutDoc[this.fieldKey + '_usePath'] = 'alternate:hover';
- return last && Doc.AddDocToList(this.dataDoc, this.fieldKey + '-alternates', drop);
+ return last && Doc.AddDocToList(this.dataDoc, this.fieldKey + '_alternates', drop);
}, true);
} else if (de.altKey || !this.dataDoc[this.fieldKey]) {
const layoutDoc = de.complete.docDragData?.draggedDocuments[0];
@@ -259,7 +265,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent() impl
const lower = url.href.toLowerCase();
if (url.protocol === 'data') return url.href;
if (url.href.indexOf(window.location.origin) === -1 && url.href.indexOf('dashblobstore') === -1) return Utils.CorsProxy(url.href);
- if (!/\.(png|jpg|jpeg|gif|webp)$/.test(lower)) return `/assets/unknown-file-icon-hi.png`;
+ if (!/\.(png|jpg|jpeg|gif|webp)$/.test(lower) || lower.endsWith('/assets/unknown-file-icon-hi.png')) return `/assets/unknown-file-icon-hi.png`;
const ext = extname(url.href);
return url.href.replace(ext, (this._error ? '_o' : this._curSuffix) + ext);
@@ -297,7 +303,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent() impl
ref={this._overlayIconRef}
onPointerDown={e => setupMoveUpEvents(e.target, e, returnFalse, emptyFunction, e => (this.layoutDoc[`_${this.fieldKey}_usePath`] = usePath === undefined ? 'alternate' : usePath === 'alternate' ? 'alternate:hover' : undefined))}
style={{
- display: (this._props.isContentActive() !== false && DragManager.DocDragData?.canEmbed) || DocListCast(this.dataDoc[this.fieldKey + '-alternates']).length ? 'block' : 'none',
+ display: (this._props.isContentActive() !== false && DragManager.DocDragData?.canEmbed) || this.dataDoc[this.fieldKey + '_alternates'] ? 'block' : 'none',
width: 'min(10%, 25px)',
height: 'min(10%, 25px)',
background: usePath === undefined ? 'white' : usePath === 'alternate' ? 'black' : 'gray',
@@ -311,13 +317,15 @@ export class ImageBox extends ViewBoxAnnotatableComponent() impl
@computed get paths() {
const field = Cast(this.dataDoc[this.fieldKey], ImageField, null); // retrieve the primary image URL that is being rendered from the data doc
- const alts = DocListCast(this.dataDoc[this.fieldKey + '-alternates']); // retrieve alternate documents that may be rendered as alternate images
- const altpaths = alts
- .map(doc => Cast(doc[Doc.LayoutFieldKey(doc)], ImageField, null)?.url)
- .filter(url => url)
- .map(url => this.choosePath(url)); // access the primary layout data of the alternate documents
+ const alts = this.dataDoc[this.fieldKey + '_alternates'] as any as List; // retrieve alternate documents that may be rendered as alternate images
+ const defaultUrl = new URL(Utils.prepend('/assets/unknown-file-icon-hi.png'));
+ const altpaths =
+ alts
+ ?.map(doc => (doc instanceof Doc ? ImageCast(doc[Doc.LayoutFieldKey(doc)])?.url ?? defaultUrl : defaultUrl))
+ .filter(url => url)
+ .map(url => this.choosePath(url)) ?? []; // acc ess the primary layout data of the alternate documents
const paths = field ? [this.choosePath(field.url), ...altpaths] : altpaths;
- return paths.length ? paths : [Utils.CorsProxy('https://cs.brown.edu/~bcz/noImage.png')];
+ return paths.length ? paths : [defaultUrl.href];
}
@observable _error = '';
@@ -326,7 +334,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent() impl
@computed get content() {
TraceMobx();
- const backColor = DashColor(this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BackgroundColor));
+ const backColor = DashColor(this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BackgroundColor) ?? Colors.WHITE);
const backAlpha = backColor.red() === 0 && backColor.green() === 0 && backColor.blue() === 0 ? backColor.alpha() : 1;
const srcpath = this.layoutDoc.hideImage ? '' : this.paths[0];
const fadepath = this.layoutDoc.hideImage ? '' : this.paths.lastElement();
@@ -370,6 +378,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent() impl
}
screenToLocalTransform = () => this.ScreenToLocalBoxXf().translate(0, NumCast(this.layoutDoc._layout_scrollTop) * this.ScreenToLocalBoxXf().Scale);
marqueeDown = (e: React.PointerEvent) => {
+ if (!this.dataDoc[this.fieldKey]) return this.chooseImage();
if (!e.altKey && e.button === 0 && NumCast(this.layoutDoc._freeform_scale, 1) <= NumCast(this.dataDoc.freeform_scaleMin, 1) && this._props.isContentActive() && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) {
setupMoveUpEvents(
this,
@@ -468,4 +477,28 @@ export class ImageBox extends ViewBoxAnnotatableComponent() impl
);
}
+
+ public chooseImage = () => {
+ const input = document.createElement('input');
+ input.type = 'file';
+ input.multiple = true;
+ input.accept = 'image/*';
+ input.onchange = async _e => {
+ const file = input.files?.[0];
+ if (file) {
+ const disposer = OverlayView.ShowSpinner();
+ const [{ result }] = await Networking.UploadFilesToServer({ file });
+ if (result instanceof Error) {
+ alert('Error uploading files - possibly due to unsupported file types');
+ } else {
+ this.dataDoc[this.fieldKey] = new ImageField(result.accessPaths.agnostic.client);
+ !(result instanceof Error) && DocUtils.assignImageInfo(result, this.dataDoc);
+ }
+ disposer();
+ } else {
+ console.log('No file selected');
+ }
+ };
+ input.click();
+ };
}
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index 5394c7dbb..78e4435ce 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -113,7 +113,7 @@ export class KeyValueBox extends ObservableReactComponent {
if (key) target[key] = script.originalScript;
return false;
}
- field === undefined && (field = res.result);
+ field === undefined && (field = res.result instanceof Array ? new List(res.result) : res.result);
}
}
if (!key) return false;
diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx
index be20b5934..74e78c671 100644
--- a/src/client/views/nodes/LabelBox.tsx
+++ b/src/client/views/nodes/LabelBox.tsx
@@ -41,7 +41,7 @@ export class LabelBox extends ViewBoxBaseComponent() {
}
@computed get Title() {
- return Field.toString(this.dataDoc[this.fieldKey] as Field);
+ return Field.toString(this.dataDoc[this.fieldKey] as Field) || StrCast(this.Document.title);
}
protected createDropTarget = (ele: HTMLDivElement) => {
diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx
index 6e4d0e92a..0809e2ad6 100644
--- a/src/client/views/nodes/LinkBox.tsx
+++ b/src/client/views/nodes/LinkBox.tsx
@@ -2,12 +2,13 @@ import { action, computed, IReactionDisposer, makeObservable, observable, reacti
import { observer } from 'mobx-react';
import * as React from 'react';
import Xarrow from 'react-xarrows';
+import { FieldResult } from '../../../fields/Doc';
import { DocCss, DocData } from '../../../fields/DocSymbols';
import { Id } from '../../../fields/FieldSymbols';
import { DocCast, NumCast, StrCast } from '../../../fields/Types';
+import { TraceMobx } from '../../../fields/util';
import { DashColor, emptyFunction, lightOrDark, returnFalse } from '../../../Utils';
import { DocumentManager } from '../../util/DocumentManager';
-import { LinkManager } from '../../util/LinkManager';
import { SnappingManager } from '../../util/SnappingManager';
import { ViewBoxBaseComponent } from '../DocComponent';
import { EditableView } from '../EditableView';
@@ -22,8 +23,8 @@ export class LinkBox extends ViewBoxBaseComponent() {
public static LayoutString(fieldKey: string = 'link') {
return FieldView.LayoutString(LinkBox, fieldKey);
}
- disposer: IReactionDisposer | undefined;
- @observable _forceAnimate = 0; // forces xArrow to animate when a transition animation is detected on something that affects an anchor
+ _disposer: IReactionDisposer | undefined;
+ @observable _forceAnimate: number = 0; // forces xArrow to animate when a transition animation is detected on something that affects an anchor
@observable _hide = false; // don't render if anchor is not visible since that breaks xAnchor
constructor(props: FieldViewProps) {
@@ -39,11 +40,11 @@ export class LinkBox extends ViewBoxBaseComponent() {
return DocumentManager.Instance.getDocumentView(anchor, this.DocumentView?.().containerViewPath?.().lastElement());
};
componentWillUnmount() {
- this.disposer?.();
+ this._disposer?.();
}
componentDidMount() {
this._props.setContentViewBox?.(this);
- this.disposer = reaction(
+ this._disposer = reaction(
() => ({ drag: SnappingManager.IsDragging }),
({ drag }) => {
!LightboxView.Contains(this.DocumentView?.()) &&
@@ -64,12 +65,13 @@ export class LinkBox extends ViewBoxBaseComponent() {
}
})
);
- },
- { fireImmediately: true }
+ }
);
}
render() {
+ TraceMobx();
+
if (this._hide) return null;
const a = this.anchor1;
const b = this.anchor2;
@@ -92,18 +94,34 @@ export class LinkBox extends ViewBoxBaseComponent() {
const at = a.getBounds?.transition; // these force re-render when a or b change size and at the end of an animated transition
const bt = b.getBounds?.transition; // inquring getBounds() also causes text anchors to update whether or not they reflow (any size change triggers an invalidation)
+ var foundParent = false;
+ const getAnchor = (field: FieldResult): Element[] => {
+ const doc = DocCast(field);
+ const ele = document.getElementById(doc[Id]);
+ if (ele?.getBoundingClientRect().width) return [ele];
+ const eles = Array.from(document.getElementsByClassName(doc[Id])).filter(ele => ele?.getBoundingClientRect().width);
+ const annoOn = DocCast(doc.annotationOn);
+ if (eles.length || !annoOn) return eles;
+ const pareles = getAnchor(annoOn);
+ foundParent = pareles.length ? true : false;
+ return pareles;
+ };
// if there's an element in the DOM with a classname containing a link anchor's id (eg a hypertext ),
// then that DOM element is a hyperlink source for the current anchor and we want to place our link box at it's top right
// otherwise, we just use the computed nearest point on the document boundary to the target Document
- const targetAhyperlink = Array.from(document.getElementsByClassName(DocCast(this.dataDoc.link_anchor_1)[Id])).lastElement();
- const targetBhyperlink = Array.from(document.getElementsByClassName(DocCast(this.dataDoc.link_anchor_2)[Id])).lastElement();
+ const targetAhyperlinks = getAnchor(this.dataDoc.link_anchor_1);
+ const targetBhyperlinks = getAnchor(this.dataDoc.link_anchor_2);
- const aid = targetAhyperlink?.id || a.Document[Id];
- const bid = targetBhyperlink?.id || b.Document[Id];
- if (!document.getElementById(aid) || !document.getElementById(bid)) {
+ const container = this.DocumentView?.().containerViewPath?.().lastElement().ContentDiv;
+ const aid = targetAhyperlinks?.find(alink => container?.contains(alink))?.id ?? targetAhyperlinks?.lastElement()?.id;
+ const bid = targetBhyperlinks?.find(blink => container?.contains(blink))?.id ?? targetBhyperlinks?.lastElement()?.id;
+ if (!aid || !bid) {
setTimeout(action(() => (this._forceAnimate = this._forceAnimate + 0.01)));
return null;
}
+ if (foundParent) {
+ setTimeout(action(() => (this._forceAnimate = this._forceAnimate + 1)));
+ }
if (at || bt) setTimeout(action(() => (this._forceAnimate = this._forceAnimate + 0.01))); // this forces an update during a transition animation
const highlight = this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Highlighting);
@@ -192,6 +210,11 @@ export class LinkBox extends ViewBoxBaseComponent() {
>
);
}
+
+ setTimeout(
+ action(() => (this._forceAnimate = this._forceAnimate + 1)),
+ 2
+ );
return (
() impl
Doc.SetNativeHeight(imageSnapshot[DocData], Doc.NativeHeight(this.layoutDoc));
this._props.addDocument?.(imageSnapshot);
const link = DocUtils.MakeLink(imageSnapshot, this.getAnchor(true), { link_relationship: 'video snapshot' });
- link && (DocCast(link.link_anchor_2)[DocData].timecodeToHide = NumCast(DocCast(link.link_anchor_2).timecodeToShow) + 3);
+ // link && (DocCast(link.link_anchor_2)[DocData].timecodeToHide = NumCast(DocCast(link.link_anchor_2).timecodeToShow) + 3); // do we need to set an end time? should default to +0.1
setTimeout(() => downX !== undefined && downY !== undefined && DocumentManager.Instance.getFirstDocumentView(imageSnapshot)?.startDragging(downX, downY, dropActionType.move, true));
};
@@ -373,7 +373,8 @@ export class VideoBox extends ViewBoxAnnotatableComponent() impl
getView = (doc: Doc, options: FocusViewOptions) => {
if (this._stackedTimeline?.makeDocUnfiltered(doc)) {
if (this.heightPercent === 100) {
- this.layoutDoc._layout_timelineHeightPercent = VideoBox.heightPercent;
+ // do we want to always open up the timeline when followin a link? kind of clunky visually
+ //this.layoutDoc._layout_timelineHeightPercent = VideoBox.heightPercent;
options.didMove = true;
}
return this._stackedTimeline.getView(doc, options);
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index c9340edc0..f4d5eef05 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -1,6 +1,6 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { htmlToText } from 'html-to-text';
-import { action, computed, IReactionDisposer, makeObservable, observable, ObservableMap, reaction, runInAction } from 'mobx';
+import { action, computed, IReactionDisposer, makeObservable, observable, ObservableMap, reaction, runInAction, trace } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import * as WebRequest from 'web-request';
@@ -340,8 +340,18 @@ export class WebBox extends ViewBoxAnnotatableComponent() implem
_textAnnotationCreator: (() => ObservableMap) | undefined;
savedAnnotationsCreator: () => ObservableMap = () => this._textAnnotationCreator?.() || this._savedAnnotations;
+ @action
+ iframeMove = (e: PointerEvent) => {
+ const theclick = this.props
+ .ScreenToLocalTransform()
+ .inverse()
+ .transformPoint(e.clientX, e.clientY - NumCast(this.layoutDoc.layout_scrollTop));
+ this._marqueeref.current?.onMove(theclick);
+ };
@action
iframeUp = (e: PointerEvent) => {
+ this._iframe?.contentDocument?.removeEventListener('pointermove', this.iframeMove);
+ this.marqueeing = undefined;
this._getAnchor = AnchorMenu.Instance?.GetAnchor; // need to save AnchorMenu's getAnchor since a subsequent selection on another doc will overwrite this value
this._textAnnotationCreator = undefined;
this.DocumentView?.()?.cleanupPointerEvents(); // pointerup events aren't generated on containing document view, so we have to invoke it here.
@@ -358,6 +368,29 @@ export class WebBox extends ViewBoxAnnotatableComponent() implem
GPTPopup.Instance.setSidebarId(`${this._props.fieldKey}_${this._urlHash ? this._urlHash + '_' : ''}sidebar`);
GPTPopup.Instance.addDoc = this.sidebarAddDocument;
}
+ } else {
+ const theclick = this.props
+ .ScreenToLocalTransform()
+ .inverse()
+ .transformPoint(e.clientX, e.clientY - NumCast(this.layoutDoc.layout_scrollTop));
+ if (!this._marqueeref.current?.isEmpty) this._marqueeref.current?.onEnd(theclick[0], theclick[1]);
+ else {
+ if (!(e.target as any)?.tagName?.includes('INPUT')) this.finishMarquee(theclick[0], theclick[1]);
+ this._getAnchor = AnchorMenu.Instance?.GetAnchor;
+ this.marqueeing = undefined;
+ }
+
+ ContextMenu.Instance.closeMenu();
+ ContextMenu.Instance.setIgnoreEvents(false);
+ if (e?.button === 2 || e?.altKey) {
+ e?.preventDefault();
+ e?.stopPropagation();
+ setTimeout(() => {
+ // if menu comes up right away, the down event can still be active causing a menu item to be selected
+ this.specificContextMenu(undefined as any);
+ this.DocumentView?.().onContextMenu(undefined, theclick[0], theclick[1]);
+ });
+ }
}
};
@action
@@ -400,6 +433,12 @@ export class WebBox extends ViewBoxAnnotatableComponent() implem
};
@action
iframeDown = (e: PointerEvent) => {
+ this._textAnnotationCreator = undefined;
+ const sel = this._url ? this._iframe?.contentDocument?.getSelection() : window.document.getSelection();
+ if (sel?.empty)
+ sel.empty(); // Chrome
+ else if (sel?.removeAllRanges) sel.removeAllRanges(); // Firefox
+
this._props.select(false);
const theclick = this.props
.ScreenToLocalTransform()
@@ -409,6 +448,8 @@ export class WebBox extends ViewBoxAnnotatableComponent() implem
const word = getWordAtPoint(e.target, e.clientX, e.clientY);
if (!word && !(e.target as any)?.className?.includes('rangeslider') && !(e.target as any)?.onclick && !(e.target as any)?.parentNode?.onclick) {
this.marqueeing = theclick;
+ this._marqueeref.current?.onInitiateSelection(this.marqueeing);
+ this._iframe?.contentDocument?.addEventListener('pointermove', this.iframeMove);
e.preventDefault();
}
};
@@ -739,28 +780,10 @@ export class WebBox extends ViewBoxAnnotatableComponent() implem
this.marqueeing = undefined;
}
};
- @action finishMarquee = (x?: number, y?: number, e?: PointerEvent) => {
+ @action finishMarquee = (x?: number, y?: number) => {
this._getAnchor = AnchorMenu.Instance?.GetAnchor;
this.marqueeing = undefined;
- this._textAnnotationCreator = undefined;
- const sel = this._url ? this._iframe?.contentDocument?.getSelection() : window.document.getSelection();
- if (sel?.empty)
- sel.empty(); // Chrome
- else if (sel?.removeAllRanges) sel.removeAllRanges(); // Firefox
this._setPreviewCursor?.(x ?? 0, y ?? 0, false, !this._marqueeref.current?.isEmpty, this.Document);
- if (x !== undefined && y !== undefined) {
- ContextMenu.Instance.closeMenu();
- ContextMenu.Instance.setIgnoreEvents(false);
- if (e?.button === 2 || e?.altKey) {
- e?.preventDefault();
- e?.stopPropagation();
- setTimeout(() => {
- // if menu comes up right away, the down event can still be active causing a menu item to be selected
- this.specificContextMenu(undefined as any);
- this.DocumentView?.().onContextMenu(undefined, x, y);
- });
- }
- }
};
@observable lighttext = false;
@@ -992,6 +1015,7 @@ export class WebBox extends ViewBoxAnnotatableComponent() implem
}
childPointerEvents = () => (this._props.isContentActive() ? 'all' : undefined);
@computed get webpage() {
+ TraceMobx();
const previewScale = this._previewNativeWidth ? 1 - this.sidebarWidth() / this._previewNativeWidth : 1;
const pointerEvents = this.layoutDoc._lockedPosition ? 'none' : (this._props.pointerEvents?.() as any);
const scale = previewScale * (this._props.NativeDimScaling?.() || 1);
@@ -1071,6 +1095,7 @@ export class WebBox extends ViewBoxAnnotatableComponent() implem
: 'none';
annotationPointerEvents = () => (this._props.isContentActive() && (SnappingManager.IsDragging || Doc.ActiveTool !== InkTool.None) ? 'all' : 'none');
render() {
+ TraceMobx();
const previewScale = this._previewNativeWidth ? 1 - this.sidebarWidth() / this._previewNativeWidth : 1;
const pointerEvents = this.layoutDoc._lockedPosition ? 'none' : (this._props.pointerEvents?.() as any);
const scale = previewScale * (this._props.NativeDimScaling?.() || 1);
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 793595694..eb7293054 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -450,16 +450,23 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent {
const newAutoLinks = new Set();
- const oldAutoLinks = LinkManager.Links(this.Document).filter(link => link.link_relationship === LinkManager.AutoKeywords);
+ const oldAutoLinks = LinkManager.Links(this.Document).filter(
+ link =>
+ ((!Doc.isTemplateForField(this.Document) &&
+ (!Doc.isTemplateForField(DocCast(link.link_anchor_1)) || !Doc.AreProtosEqual(DocCast(link.link_anchor_1), this.Document)) &&
+ (!Doc.isTemplateForField(DocCast(link.link_anchor_2)) || !Doc.AreProtosEqual(DocCast(link.link_anchor_2), this.Document))) ||
+ (Doc.isTemplateForField(this.Document) && (link.link_anchor_1 === this.Document || link.link_anchor_2 === this.Document))) &&
+ link.link_relationship === LinkManager.AutoKeywords
+ ); // prettier-ignore
if (this._editorView?.state.doc.textContent) {
- const isNodeSel = this._editorView.state.selection instanceof NodeSelection;
const f = this._editorView.state.selection.from;
+
const t = this._editorView.state.selection.to;
var tr = this._editorView.state.tr as any;
const autoAnch = this._editorView.state.schema.marks.autoLinkAnchor;
tr = tr.removeMark(0, tr.doc.content.size, autoAnch);
Doc.MyPublishedDocs.filter(term => term.title).forEach(term => (tr = this.hyperlinkTerm(tr, term, newAutoLinks)));
- tr = tr.setSelection(isNodeSel && false ? new NodeSelection(tr.doc.resolve(f)) : new TextSelection(tr.doc.resolve(f), tr.doc.resolve(t)));
+ tr = tr.setSelection(new TextSelection(tr.doc.resolve(f), tr.doc.resolve(t)));
this._editorView?.dispatch(tr);
}
oldAutoLinks.filter(oldLink => !newAutoLinks.has(oldLink) && oldLink.link_anchor_2 !== this.Document).forEach(LinkManager.Instance.deleteLink);
@@ -624,7 +631,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent p + toHgt(child), margins);
const scrollHeight = this.ProseRef && proseHeight;
- if (this._props.setHeight && scrollHeight && !this._props.dontRegisterView) {
+ if (this._props.setHeight && !this._props.suppressSetHeight && scrollHeight && !this._props.dontRegisterView) {
// if top === 0, then the text box is growing upward (as the overlay caption) which doesn't contribute to the height computation
const setScrollHeight = () => (this.dataDoc[this.fieldKey + '_scrollHeight'] = scrollHeight);
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index e34144fae..ae6da8fb0 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -703,7 +703,7 @@ export class PresBox extends ViewBoxBaseComponent() {
if (pinProps.pinData.temporal) {
pinDoc.config_clipStart = targetDoc._layout_currentTimecode;
const duration = NumCast(pinDoc[`${Doc.LayoutFieldKey(pinDoc)}_duration`], NumCast(targetDoc.config_clipStart) + 0.1);
- pinDoc.config_clipEnd = NumCast(targetDoc.clipEnd, duration);
+ pinDoc.config_clipEnd = NumCast(pinDoc.config_clipStart) + NumCast(targetDoc.clipEnd, duration);
}
}
if (pinProps?.pinViewport) {
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 246828709..1bd49cf3f 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -45,18 +45,23 @@ export namespace Field {
export function toKeyValueString(doc: Doc, key: string, showComputedValue?: boolean): string {
const onDelegate = !Doc.IsDataProto(doc) && Object.keys(doc).includes(key.replace(/^_/, ''));
const field = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
- return !Field.IsField(field)
- ? key.startsWith('_')
- ? '='
- : ''
- : (onDelegate ? '=' : '') +
- (field instanceof ComputedField && showComputedValue
- ? field._lastComputedResult
- : field instanceof ComputedField
- ? `:=${field.script.originalScript.replace(/dashCallChat\(_setCacheResult_, this, `(.*)`\)/, '(($1))')}`
- : field instanceof ScriptField
- ? `$=${field.script.originalScript}`
- : Field.toScriptString(field));
+ const valFunc = (field: Field): string => {
+ const res =
+ field instanceof ComputedField && showComputedValue
+ ? field._lastComputedResult
+ : field instanceof ComputedField
+ ? `:=${field.script.originalScript.replace(/dashCallChat\(_setCacheResult_, this, `(.*)`\)/, '(($1))')}`
+ : field instanceof ScriptField
+ ? `$=${field.script.originalScript}`
+ : Field.toScriptString(field);
+ const resStr = (res + '').replace(/^`(.*)`$/, '$1');
+ return typeof field === 'string' && (+resStr).toString() !== resStr && !Array.from('+-*/.').some(k => Array.from(resStr).includes(k))
+ ? resStr
+ : (res + '') // adjust the key value string to be easier to enter: represent any initial list as an array with []
+ .trim()
+ .replace(/^new List\((.*)\)$/, '$1');
+ };
+ return !Field.IsField(field) ? (key.startsWith('_') ? '=' : '') : (onDelegate ? '=' : '') + valFunc(field);
}
export function toScriptString(field: Field) {
switch (typeof field) {
diff --git a/src/fields/PresField.ts b/src/fields/PresField.ts
deleted file mode 100644
index f236a04fd..000000000
--- a/src/fields/PresField.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-//insert code here
-import { ObjectField } from "./ObjectField";
-
-export abstract class PresField extends ObjectField {
-
-}
\ No newline at end of file
diff --git a/src/fields/RichTextField.ts b/src/fields/RichTextField.ts
index f5801de73..d0a149506 100644
--- a/src/fields/RichTextField.ts
+++ b/src/fields/RichTextField.ts
@@ -36,11 +36,4 @@ export class RichTextField extends ObjectField {
[ToString]() {
return this.Text;
}
-
- public static DashField(fieldKey: string) {
- return new RichTextField(
- `{"doc":{"type":"doc","content":[{"type":"paragraph","attrs":{"align":null,"color":null,"id":null,"indent":null,"inset":null,"lineSpacing":null,"paddingBottom":null,"paddingTop":null},"content":[{"type":"dashField","attrs":{"fieldKey":"${fieldKey}","docId":""}}]}]},"selection":{"type":"text","anchor":2,"head":2},"storedMarks":[]}`,
- ''
- );
- }
}
--
cgit v1.2.3-70-g09d2
From 1b592f74a7df8f6dd7b2881032725f26aedff403 Mon Sep 17 00:00:00 2001
From: bobzel
Date: Thu, 28 Mar 2024 11:38:56 -0400
Subject: fixed keyvaluebox to show props document, never the doc in the
fieldKey slot. changed computedFIelds to do mobx caching. changed text
boxes to do updating from templates based on a fieldKey_autoUpdate flag
combined with modification timestamps. enabled comparison box to work with
text fields in addition to docs.
---
src/client/util/Scripting.ts | 9 +--
src/client/views/InkControlPtHandles.tsx | 85 ++++++++++++----------
src/client/views/PropertiesButtons.tsx | 4 +-
src/client/views/nodes/ComparisonBox.tsx | 6 +-
src/client/views/nodes/KeyValueBox.tsx | 2 +-
.../views/nodes/formattedText/FormattedTextBox.tsx | 31 +++++---
src/fields/Doc.ts | 2 +-
src/fields/ScriptField.ts | 22 ++++--
8 files changed, 90 insertions(+), 71 deletions(-)
(limited to 'src/client/views/nodes/KeyValueBox.tsx')
diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts
index f5e162d16..31222aa50 100644
--- a/src/client/util/Scripting.ts
+++ b/src/client/util/Scripting.ts
@@ -88,15 +88,10 @@ function Run(script: string | undefined, customParams: string[], diagnostics: an
}
const result = compiledFunction.apply(thisParam, params).apply(thisParam, argsArray);
- if (batch) {
- batch.end();
- }
-
+ batch?.end();
return { success: true, result };
} catch (error) {
- if (batch) {
- batch.end();
- }
+ batch?.end();
onError?.(script + ' ' + error);
return { success: false, error, result: errorVal };
}
diff --git a/src/client/views/InkControlPtHandles.tsx b/src/client/views/InkControlPtHandles.tsx
index 31b13d2c8..01d52135a 100644
--- a/src/client/views/InkControlPtHandles.tsx
+++ b/src/client/views/InkControlPtHandles.tsx
@@ -1,5 +1,5 @@
import * as React from 'react';
-import { action, observable } from 'mobx';
+import { action, makeObservable, observable } from 'mobx';
import { observer } from 'mobx-react';
import { Doc } from '../../fields/Doc';
import { ControlPoint, InkData, PointData } from '../../fields/InkField';
@@ -13,6 +13,7 @@ import { Colors } from './global/globalEnums';
import { InkingStroke } from './InkingStroke';
import { InkStrokeProperties } from './InkStrokeProperties';
import { SnappingManager } from '../util/SnappingManager';
+import { ObservableReactComponent } from './ObservableReactComponent';
export interface InkControlProps {
inkDoc: Doc;
@@ -24,10 +25,15 @@ export interface InkControlProps {
}
@observer
-export class InkControlPtHandles extends React.Component {
+export class InkControlPtHandles extends ObservableReactComponent {
@observable private _overControl = -1;
get docView() {
- return this.props.inkView.DocumentView?.();
+ return this._props.inkView.DocumentView?.();
+ }
+
+ constructor(props: InkControlProps) {
+ super(props);
+ makeObservable(this);
}
componentDidMount() {
@@ -42,51 +48,51 @@ export class InkControlPtHandles extends React.Component {
*/
@action
onControlDown = (e: React.PointerEvent, controlIndex: number): void => {
- const ptFromScreen = this.props.inkView.ptFromScreen;
+ const ptFromScreen = this._props.inkView.ptFromScreen;
if (ptFromScreen) {
const order = controlIndex % 4;
- const handleIndexA = ((order === 3 ? controlIndex - 1 : controlIndex - 2) + this.props.inkCtrlPoints.length) % this.props.inkCtrlPoints.length;
- const handleIndexB = (order === 3 ? controlIndex + 2 : controlIndex + 1) % this.props.inkCtrlPoints.length;
- const brokenIndices = Cast(this.props.inkDoc.brokenInkIndices, listSpec('number'));
+ const handleIndexA = ((order === 3 ? controlIndex - 1 : controlIndex - 2) + this._props.inkCtrlPoints.length) % this._props.inkCtrlPoints.length;
+ const handleIndexB = (order === 3 ? controlIndex + 2 : controlIndex + 1) % this._props.inkCtrlPoints.length;
+ const brokenIndices = Cast(this._props.inkDoc.brokenInkIndices, listSpec('number'));
const wasSelected = InkStrokeProperties.Instance._currentPoint === controlIndex;
if (!wasSelected) InkStrokeProperties.Instance._currentPoint = -1;
- const origInk = this.props.inkCtrlPoints.slice();
+ const origInk = this._props.inkCtrlPoints.slice();
setupMoveUpEvents(
this,
e,
action((e: PointerEvent, down: number[], delta: number[]) => {
- if (!this.props.inkView.controlUndo) this.props.inkView.controlUndo = UndoManager.StartBatch('drag ink ctrl pt');
+ if (!this._props.inkView.controlUndo) this._props.inkView.controlUndo = UndoManager.StartBatch('drag ink ctrl pt');
const inkMoveEnd = ptFromScreen({ X: delta[0], Y: delta[1] });
const inkMoveStart = ptFromScreen({ X: 0, Y: 0 });
this.docView && InkStrokeProperties.Instance.moveControlPtHandle(this.docView, inkMoveEnd.X - inkMoveStart.X, inkMoveEnd.Y - inkMoveStart.Y, controlIndex, origInk);
return false;
}),
action(() => {
- if (this.props.inkView.controlUndo && this.docView) {
+ if (this._props.inkView.controlUndo && this.docView) {
InkStrokeProperties.Instance.snapControl(this.docView, controlIndex);
}
- this.props.inkView.controlUndo?.end();
- this.props.inkView.controlUndo = undefined;
+ this._props.inkView.controlUndo?.end();
+ this._props.inkView.controlUndo = undefined;
UndoManager.FilterBatches(['data', 'x', 'y', 'width', 'height']);
}),
action((e: PointerEvent, doubleTap: boolean | undefined) => {
- const equivIndex = controlIndex === 0 ? this.props.inkCtrlPoints.length - 1 : controlIndex === this.props.inkCtrlPoints.length - 1 ? 0 : controlIndex;
+ const equivIndex = controlIndex === 0 ? this._props.inkCtrlPoints.length - 1 : controlIndex === this._props.inkCtrlPoints.length - 1 ? 0 : controlIndex;
if (doubleTap || e.button === 2) {
if (!brokenIndices?.includes(equivIndex) && !brokenIndices?.includes(controlIndex)) {
if (brokenIndices) brokenIndices.push(controlIndex);
- else this.props.inkDoc.brokenInkIndices = new List([controlIndex]);
+ else this._props.inkDoc.brokenInkIndices = new List([controlIndex]);
} else {
if (brokenIndices?.includes(equivIndex)) {
- if (!this.props.inkView.controlUndo) this.props.inkView.controlUndo = UndoManager.StartBatch('make smooth');
+ if (!this._props.inkView.controlUndo) this._props.inkView.controlUndo = UndoManager.StartBatch('make smooth');
this.docView && InkStrokeProperties.Instance.snapHandleTangent(this.docView, equivIndex, handleIndexA, handleIndexB);
}
if (equivIndex !== controlIndex && brokenIndices?.includes(controlIndex)) {
- if (!this.props.inkView.controlUndo) this.props.inkView.controlUndo = UndoManager.StartBatch('make smooth');
+ if (!this._props.inkView.controlUndo) this._props.inkView.controlUndo = UndoManager.StartBatch('make smooth');
this.docView && InkStrokeProperties.Instance.snapHandleTangent(this.docView, controlIndex, handleIndexA, handleIndexB);
}
}
- this.props.inkView.controlUndo?.end();
- this.props.inkView.controlUndo = undefined;
+ this._props.inkView.controlUndo?.end();
+ this._props.inkView.controlUndo = undefined;
}
this.changeCurrPoint(controlIndex);
}),
@@ -126,14 +132,14 @@ export class InkControlPtHandles extends React.Component {
render() {
// Accessing the current ink's data and extracting all control points.
- const scrData = this.props.screenCtrlPoints;
+ const scrData = this._props.screenCtrlPoints;
const sreenCtrlPoints: ControlPoint[] = [];
for (let i = 0; i <= scrData.length - 4; i += 4) {
sreenCtrlPoints.push({ ...scrData[i], I: i });
sreenCtrlPoints.push({ ...scrData[i + 3], I: i + 3 });
}
- const inkData = this.props.inkCtrlPoints;
+ const inkData = this._props.inkCtrlPoints;
const inkCtrlPts: ControlPoint[] = [];
for (let i = 0; i <= inkData.length - 4; i += 4) {
inkCtrlPts.push({ ...inkData[i], I: i });
@@ -141,23 +147,23 @@ export class InkControlPtHandles extends React.Component {
}
const closed = InkingStroke.IsClosed(inkData);
- const nearestScreenPt = this.props.nearestScreenPt();
+ const nearestScreenPt = this._props.nearestScreenPt();
const TagType = (broken?: boolean) => (broken ? 'rect' : 'circle');
const hdl = (control: { X: number; Y: number; I: number }, scale: number, color: string) => {
- const broken = Cast(this.props.inkDoc.brokenInkIndices, listSpec('number'))?.includes(control.I);
+ const broken = Cast(this._props.inkDoc.brokenInkIndices, listSpec('number'))?.includes(control.I);
const Tag = TagType((control.I === 0 || control.I === inkData.length - 1) && !closed) as keyof JSX.IntrinsicElements;
return (
this.onControlDown(e, control.I)}
@@ -170,7 +176,7 @@ export class InkControlPtHandles extends React.Component {
};
return (
- {!nearestScreenPt ? null : }
+ {!nearestScreenPt ? null : }
{sreenCtrlPoints.map(control => hdl(control, this._overControl !== control.I ? 1 : 3 / 2, Colors.WHITE))}
);
@@ -185,10 +191,15 @@ export interface InkEndProps {
endPt: () => PointData;
}
@observer
-export class InkEndPtHandles extends React.Component {
+export class InkEndPtHandles extends ObservableReactComponent {
@observable _overStart: boolean = false;
@observable _overEnd: boolean = false;
+ constructor(props: InkEndProps) {
+ super(props);
+ makeObservable(this);
+ }
+
_throttle = 0; // need to throttle dragging since the position may change when the control points change. this allows the stroke to settle so that we don't get increasingly bad jitter
@action
dragRotate = (e: React.PointerEvent, pt1: () => { X: number; Y: number }, pt2: () => { X: number; Y: number }) => {
@@ -198,7 +209,7 @@ export class InkEndPtHandles extends React.Component {
e,
action(e => {
if (this._throttle++ % 2 !== 0) return false;
- if (!this.props.inkView.controlUndo) this.props.inkView.controlUndo = UndoManager.StartBatch('stretch ink');
+ if (!this._props.inkView.controlUndo) this._props.inkView.controlUndo = UndoManager.StartBatch('stretch ink');
// compute stretch factor by finding scaling along axis between start and end points
const p1 = pt1();
const p2 = pt2();
@@ -216,8 +227,8 @@ export class InkEndPtHandles extends React.Component {
}),
action(() => {
SnappingManager.SetIsDragging(false);
- this.props.inkView.controlUndo?.end();
- this.props.inkView.controlUndo = undefined;
+ this._props.inkView.controlUndo?.end();
+ this._props.inkView.controlUndo = undefined;
UndoManager.FilterBatches(['stroke', 'x', 'y', 'width', 'height']);
}),
returnFalse
@@ -230,7 +241,7 @@ export class InkEndPtHandles extends React.Component {
key={key}
cx={pt.X}
cy={pt.Y}
- r={this.props.screenSpaceLineWidth * 2}
+ r={this._props.screenSpaceLineWidth * 2}
fill={this._overStart ? '#aaaaaa' : '#99999977'}
stroke={'#00007777'}
strokeWidth={0}
@@ -242,8 +253,8 @@ export class InkEndPtHandles extends React.Component {
);
return (
- {hdl('start', this.props.startPt(), e => this.dragRotate(e, this.props.startPt, this.props.endPt))}
- {hdl('end', this.props.endPt(), e => this.dragRotate(e, this.props.endPt, this.props.startPt))}
+ {hdl('start', this._props.startPt(), e => this.dragRotate(e, this._props.startPt, this._props.endPt))}
+ {hdl('end', this._props.endPt(), e => this.dragRotate(e, this._props.endPt, this._props.startPt))}
);
}
diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index 02f288a68..517a80d63 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -87,7 +87,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
containerDoc._isLightbox = !containerDoc._isLightbox;
//containerDoc._xPadding = containerDoc._yPadding = containerDoc._isLightbox ? 10 : undefined;
const containerContents = DocListCast(dv.dataDoc[Doc.LayoutFieldKey(containerDoc)]);
- //dv.Docuemnt.onClick = ScriptField.MakeScript('{self.data = undefined; documentView.select(false)}', { documentView: 'any' });
+ //dv.Docuemnt.onClick = ScriptField.MakeScript('{this.data = undefined; documentView.select(false)}', { documentView: 'any' });
containerContents.forEach(doc => LinkManager.Links(doc).forEach(link => (link.link_displayLine = false)));
});
}
@@ -219,7 +219,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
// containerDoc._isLightbox = !containerDoc._isLightbox;
// //containerDoc._xPadding = containerDoc._yPadding = containerDoc._isLightbox ? 10 : undefined;
// const containerContents = DocListCast(dv.dataDoc[dv.props.fieldKey ?? Doc.LayoutFieldKey(containerDoc)]);
- // //dv.Document.onClick = ScriptField.MakeScript('{self.data = undefined; documentView.select(false)}', { documentView: 'any' });
+ // //dv.Document.onClick = ScriptField.MakeScript('{this.data = undefined; documentView.select(false)}', { documentView: 'any' });
// containerContents.forEach(doc => LinkManager.Links(doc).forEach(link => (link.layout_linkDisplay = false)));
// });
// }
diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx
index 59d99b371..9ffdc350d 100644
--- a/src/client/views/nodes/ComparisonBox.tsx
+++ b/src/client/views/nodes/ComparisonBox.tsx
@@ -69,8 +69,8 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
action((e, doubleTap) => {
if (doubleTap) {
this._isAnyChildContentActive = true;
- if (!this.dataDoc[this.fieldKey + '_1']) this.dataDoc[this.fieldKey + '_1'] = DocUtils.copyDragFactory(Doc.UserDoc().emptyNote as Doc);
- if (!this.dataDoc[this.fieldKey + '_2']) this.dataDoc[this.fieldKey + '_2'] = DocUtils.copyDragFactory(Doc.UserDoc().emptyNote as Doc);
+ 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);
}
}),
false,
@@ -131,8 +131,6 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent()
return false;
};
- whenChildContentsActiveChanged = action((isActive: boolean) => (this._isAnyChildContentActive = isActive));
-
closeDown = (e: React.PointerEvent, which: string) => {
setupMoveUpEvents(
this,
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index 78e4435ce..31a2367fc 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -55,7 +55,7 @@ export class KeyValueBox extends ObservableReactComponent {
@observable _splitPercentage = 50;
get fieldDocToLayout() {
- return this._props.fieldKey ? DocCast(this._props.Document[this._props.fieldKey], DocCast(this._props.Document)) : this._props.Document;
+ return DocCast(this._props.Document);
}
@action
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index d25101844..f06e5fad0 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -21,7 +21,7 @@ import { List } from '../../../../fields/List';
import { PrefetchProxy } from '../../../../fields/Proxy';
import { RichTextField } from '../../../../fields/RichTextField';
import { ComputedField } from '../../../../fields/ScriptField';
-import { BoolCast, Cast, DocCast, FieldValue, NumCast, RTFCast, ScriptCast, StrCast } from '../../../../fields/Types';
+import { BoolCast, Cast, DateCast, DocCast, FieldValue, NumCast, RTFCast, ScriptCast, StrCast } from '../../../../fields/Types';
import { GetEffectiveAcl, TraceMobx } from '../../../../fields/util';
import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, DivWidth, emptyFunction, numberRange, returnFalse, returnZero, setupMoveUpEvents, smoothScroll, unimplementedFunction, Utils } from '../../../../Utils';
import { gptAPICall, GPTCallType } from '../../../apis/gpt/GPT';
@@ -349,6 +349,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent json?.replace(/"selection":.*/, '');
@@ -369,22 +370,20 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent (this.dataDoc[this.fieldKey + '_autoUpdate'] = !this.dataDoc[this.fieldKey + '_autoUpdate']), icon: 'star' });
optionItems.push({ description: `Generate Dall-E Image`, event: () => this.generateImage(), icon: 'star' });
optionItems.push({ description: `Ask GPT-3`, event: () => this.askGPT(), icon: 'lightbulb' });
this._props.renderDepth &&
@@ -1244,9 +1244,15 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent {
- const dataDoc = Doc.IsDelegateField(DocCast(this.layoutDoc?.proto), this.fieldKey) ? DocCast(this.layoutDoc?.proto) : this?.dataDoc;
- const whichDoc = !this.dataDoc || !this.layoutDoc ? undefined : dataDoc?.[this.fieldKey + '_noTemplate'] || !this.layoutDoc[this.fieldKey] ? dataDoc : this.layoutDoc;
- return !whichDoc ? undefined : { data: Cast(whichDoc[this.fieldKey], RichTextField, null), str: Field.toString(DocCast(whichDoc[this.fieldKey]) ?? StrCast(whichDoc[this.fieldKey])) };
+ const protoData = DocCast(this.dataDoc.proto)?.[this.fieldKey];
+ const dataData = this.dataDoc[this.fieldKey];
+ const layoutData = Doc.AreProtosEqual(this.layoutDoc, this.dataDoc) ? undefined : this.layoutDoc[this.fieldKey];
+ const dataTime = dataData ? DateCast(this.dataDoc[this.fieldKey + '_modificationDate'])?.date.getTime() ?? 0 : 0;
+ const layoutTime = layoutData && this.dataDoc[this.fieldKey + '_autoUpdate'] ? DateCast(DocCast(this.layoutDoc)[this.fieldKey + '_modificationDate'])?.date.getTime() ?? 0 : 0;
+ const protoTime = protoData && this.dataDoc[this.fieldKey + '_autoUpdate'] ? DateCast(DocCast(this.dataDoc.proto)[this.fieldKey + '_modificationDate'])?.date.getTime() ?? 0 : 0;
+ const recentData = dataTime >= layoutTime ? (protoTime >= dataTime ? protoData : dataData) : layoutTime >= protoTime ? layoutData : protoData;
+ const whichData = recentData ?? (this.layoutDoc.isTemplateDoc ? layoutData : protoData) ?? protoData;
+ return !whichData ? undefined : { data: RTFCast(whichData), str: Field.toString(DocCast(whichData) ?? StrCast(whichData)) };
},
incomingValue => {
if (this._editorView && this._applyingChange !== this.fieldKey) {
@@ -1256,11 +1262,12 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent this._editorView?.dispatch(tx.insertText(incomingValue?.str ?? '')));
}
}
- }
+ },
+ { fireImmediately: true }
);
this._disposers.search = reaction(
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index bdf600475..200896e25 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -48,7 +48,7 @@ export namespace Field {
const valFunc = (field: Field): string => {
const res =
field instanceof ComputedField && showComputedValue
- ? field._lastComputedResult
+ ? field.value(doc)
: field instanceof ComputedField
? `:=${field.script.originalScript.replace(/dashCallChat\(_setCacheResult_, this, `(.*)`\)/, '(($1))')}`
: field instanceof ScriptField
diff --git a/src/fields/ScriptField.ts b/src/fields/ScriptField.ts
index 9021c8896..8b51088b2 100644
--- a/src/fields/ScriptField.ts
+++ b/src/fields/ScriptField.ts
@@ -198,14 +198,22 @@ export class ComputedField extends ScriptField {
}
_lastComputedResult: FieldResult;
- value = computedFn((doc: Doc) => this._valueOutsideReaction(doc));
- _valueOutsideReaction = (doc: Doc) => {
- this._lastComputedResult =
- this._cachedResult ?? (this.script.compiled && this.script.run({ this: doc, self: doc, value: '', _setCacheResult_: this.setCacheResult, _last_: this._lastComputedResult, _readOnly_: true }, console.log).result);
- return this._lastComputedResult;
- };
+ value = (doc:Doc) => (this._lastComputedResult = this._cachedResult ??
+ computedFn((doc: Doc) =>
+ this.script.compiled &&
+ this.script.run( {
+ this: doc,
+ //value: '',
+ _setCacheResult_: this.setCacheResult,
+ _last_: this._lastComputedResult,
+ _readOnly_: true,
+ },
+ console.log
+ ).result
+ )(doc)
+ ); // prettier-ignore
- [ToValue](doc: Doc) { if (ComputedField.useComputed) return { value: this._valueOutsideReaction(doc) }; } // prettier-ignore
+ [ToValue](doc: Doc) { if (ComputedField.useComputed) return { value: this.value(doc) }; } // prettier-ignore
[Copy](): ObjectField { return new ComputedField(this.script, this.setterscript, this.rawscript); } // prettier-ignore
public static MakeFunction(script: string, params: object = {}, capturedVariables?: { [name: string]: Doc | string | number | boolean }, setterscript?: string) {
--
cgit v1.2.3-70-g09d2
From 2a313f28fcb8675223708b0657de7517a3281095 Mon Sep 17 00:00:00 2001
From: bobzel
Date: Wed, 17 Apr 2024 12:27:21 -0400
Subject: restoring eslint - updates not complete yet
---
.eslintrc.json | 69 +-
package-lock.json | 838 +++++++++++++--
package.json | 17 +-
src/ServerUtils.ts | 27 +
src/Utils.ts | 865 ++--------------
src/client/DocServer.ts | 228 ++---
src/client/Network.ts | 11 +-
.../apis/google_docs/GooglePhotosClientUtils.ts | 10 +-
src/client/apis/gpt/GPT.ts | 5 +
src/client/apis/youtube/YoutubeBox.scss | 126 ---
src/client/apis/youtube/YoutubeBox.tsx | 369 -------
src/client/cognitive_services/CognitiveServices.ts | 24 +-
src/client/documents/DocumentTypes.ts | 6 +-
src/client/documents/Documents.ts | 317 ++++--
src/client/util/CaptureManager.tsx | 2 +-
src/client/util/CurrentUserUtils.ts | 250 ++---
src/client/util/DictationManager.ts | 6 +-
src/client/util/DocumentManager.ts | 80 +-
src/client/util/DragManager.ts | 175 ++--
src/client/util/DropActionTypes.ts | 9 +
src/client/util/GroupManager.tsx | 20 +-
src/client/util/History.ts | 4 +-
src/client/util/HypothesisUtils.ts | 4 +-
src/client/util/Import & Export/ImageUtils.ts | 10 +-
src/client/util/InteractionUtils.tsx | 317 ++----
src/client/util/LinkFollower.ts | 4 +-
src/client/util/LinkManager.ts | 49 +-
src/client/util/Scripting.ts | 52 +-
src/client/util/ScriptingGlobals.ts | 47 +-
src/client/util/SearchUtil.ts | 6 +-
src/client/util/SerializationHelper.ts | 29 +-
src/client/util/SettingsManager.tsx | 86 +-
src/client/util/SharingManager.tsx | 27 +-
src/client/util/SnappingManager.ts | 31 +-
src/client/util/UndoManager.ts | 13 +-
src/client/util/bezierFit.ts | 395 ++++----
src/client/util/reportManager/ReportManager.tsx | 31 +-
src/client/views/ContextMenu.tsx | 30 +-
src/client/views/ContextMenuItem.tsx | 27 +-
src/client/views/DashboardView.tsx | 62 +-
src/client/views/DocComponent.tsx | 51 +-
src/client/views/DocumentButtonBar.tsx | 18 +-
src/client/views/DocumentDecorations.tsx | 98 +-
src/client/views/FilterPanel.tsx | 162 +--
src/client/views/GestureOverlay.tsx | 214 ++--
src/client/views/GlobalKeyHandler.ts | 73 +-
src/client/views/InkControlPtHandles.tsx | 2 +-
src/client/views/InkTangentHandles.tsx | 3 +-
src/client/views/InkingStroke.tsx | 15 +-
src/client/views/LightboxView.tsx | 9 +-
src/client/views/MainView.tsx | 132 ++-
src/client/views/MainViewModal.tsx | 4 +-
src/client/views/MarqueeAnnotator.tsx | 2 +-
src/client/views/MetadataEntryMenu.tsx | 9 +-
src/client/views/ObservableReactComponent.tsx | 7 +-
src/client/views/OverlayView.tsx | 37 +-
src/client/views/PreviewCursor.tsx | 2 +-
src/client/views/PropertiesButtons.tsx | 8 +-
src/client/views/PropertiesView.tsx | 1062 +++++++++++---------
src/client/views/ScriptBox.tsx | 2 +-
src/client/views/SidebarAnnos.tsx | 13 +-
src/client/views/StyleProvider.tsx | 23 +-
src/client/views/TemplateMenu.tsx | 70 +-
src/client/views/Touchable.tsx | 213 ----
src/client/views/animationtimeline/Timeline.tsx | 3 +-
.../views/animationtimeline/TimelineMenu.tsx | 25 +-
.../views/collections/CollectionCalendarView.tsx | 3 +-
.../views/collections/CollectionCarousel3DView.tsx | 97 +-
.../views/collections/CollectionCarouselView.tsx | 5 +-
.../views/collections/CollectionDockingView.tsx | 156 +--
.../collections/CollectionMasonryViewFieldRow.tsx | 3 +-
src/client/views/collections/CollectionMenu.tsx | 38 +-
.../views/collections/CollectionNoteTakingView.tsx | 15 +-
.../collections/CollectionNoteTakingViewColumn.tsx | 2 +-
.../CollectionNoteTakingViewDivider.tsx | 3 +-
.../views/collections/CollectionPileView.tsx | 5 +-
.../collections/CollectionStackedTimeline.tsx | 3 +-
.../views/collections/CollectionStackingView.tsx | 79 +-
.../CollectionStackingViewFieldColumn.tsx | 80 +-
src/client/views/collections/CollectionSubView.tsx | 173 ++--
.../views/collections/CollectionTimeView.tsx | 5 +-
.../views/collections/CollectionTreeView.tsx | 6 +-
src/client/views/collections/CollectionView.tsx | 75 +-
src/client/views/collections/TabDocView.tsx | 69 +-
src/client/views/collections/TreeView.tsx | 307 +++---
.../CollectionFreeFormInfoUI.tsx | 31 +-
.../CollectionFreeFormLayoutEngines.tsx | 18 +-
.../CollectionFreeFormPannableContents.tsx | 18 +-
.../CollectionFreeFormRemoteCursors.tsx | 6 +-
.../collectionFreeForm/CollectionFreeFormView.tsx | 122 ++-
.../collections/collectionFreeForm/MarqueeView.tsx | 50 +-
.../collectionGrid/CollectionGridView.tsx | 3 +-
.../collectionLinear/CollectionLinearView.tsx | 66 +-
.../collectionSchema/CollectionSchemaView.tsx | 24 +-
.../collectionSchema/SchemaColumnHeader.tsx | 3 +-
.../collections/collectionSchema/SchemaRowBox.tsx | 3 +-
.../collectionSchema/SchemaTableCell.tsx | 12 +-
src/client/views/global/globalScripts.ts | 137 +--
src/client/views/linking/LinkMenuItem.tsx | 6 +-
src/client/views/linking/LinkPopup.tsx | 3 +-
src/client/views/newlightbox/NewLightboxView.tsx | 3 +-
src/client/views/nodes/AudioBox.tsx | 106 +-
.../views/nodes/CollectionFreeFormDocumentView.tsx | 9 +-
src/client/views/nodes/ComparisonBox.tsx | 54 +-
src/client/views/nodes/DataVizBox/DataVizBox.tsx | 10 +-
.../views/nodes/DataVizBox/SchemaCSVPopUp.tsx | 5 +-
.../nodes/DataVizBox/components/Histogram.tsx | 26 +-
.../nodes/DataVizBox/components/LineChart.tsx | 80 +-
.../views/nodes/DataVizBox/components/PieChart.tsx | 24 +-
.../views/nodes/DataVizBox/components/TableBox.tsx | 5 +-
src/client/views/nodes/DocumentContentsView.tsx | 5 +-
src/client/views/nodes/DocumentIcon.tsx | 63 +-
src/client/views/nodes/DocumentLinksButton.tsx | 9 +-
src/client/views/nodes/DocumentView.tsx | 282 +++---
src/client/views/nodes/EquationBox.tsx | 2 +-
src/client/views/nodes/FieldView.tsx | 10 +-
src/client/views/nodes/FontIconBox/FontIconBox.tsx | 5 +-
src/client/views/nodes/FunctionPlotBox.tsx | 10 +-
src/client/views/nodes/ImageBox.tsx | 117 ++-
src/client/views/nodes/KeyValueBox.tsx | 10 +-
src/client/views/nodes/KeyValuePair.tsx | 3 +-
src/client/views/nodes/LabelBox.tsx | 10 +-
src/client/views/nodes/LinkAnchorBox.tsx | 15 +-
src/client/views/nodes/LinkBox.tsx | 3 +-
src/client/views/nodes/LinkDocPreview.tsx | 3 +-
.../views/nodes/MapBox/DirectionsAnchorMenu.tsx | 3 +-
src/client/views/nodes/MapBox/MapAnchorMenu.tsx | 3 +-
src/client/views/nodes/MapBox/MapBox.tsx | 11 +-
.../views/nodes/MapboxMapBox/MapboxContainer.tsx | 76 +-
src/client/views/nodes/PDFBox.tsx | 31 +-
.../views/nodes/RecordingBox/RecordingBox.tsx | 15 +-
.../views/nodes/RecordingBox/RecordingView.tsx | 2 +-
src/client/views/nodes/ScreenshotBox.tsx | 17 +-
src/client/views/nodes/ScriptingBox.tsx | 2 +-
src/client/views/nodes/VideoBox.tsx | 21 +-
src/client/views/nodes/WebBox.tsx | 38 +-
src/client/views/nodes/calendarBox/CalendarBox.tsx | 2 +-
.../views/nodes/formattedText/DashDocView.tsx | 3 +-
.../views/nodes/formattedText/DashFieldView.tsx | 8 +-
.../views/nodes/formattedText/FormattedTextBox.tsx | 137 ++-
.../formattedText/FormattedTextBoxComment.tsx | 7 +-
.../formattedText/ProsemirrorExampleTransfer.ts | 60 +-
.../views/nodes/formattedText/RichTextMenu.tsx | 2 +-
.../views/nodes/formattedText/RichTextRules.ts | 121 ++-
src/client/views/nodes/formattedText/marks_rts.ts | 7 +-
src/client/views/nodes/formattedText/nodes_rts.ts | 4 +-
.../views/nodes/generativeFill/GenerativeFill.tsx | 91 +-
.../views/nodes/importBox/ImportElementBox.tsx | 2 +-
src/client/views/nodes/trails/PresBox.tsx | 176 ++--
src/client/views/nodes/trails/PresElementBox.tsx | 3 +-
src/client/views/pdf/AnchorMenu.tsx | 22 +-
src/client/views/pdf/GPTPopup/GPTPopup.tsx | 100 +-
src/client/views/pdf/PDFViewer.tsx | 21 +-
src/client/views/search/SearchBox.tsx | 4 +-
src/client/views/topbar/TopBar.tsx | 5 +-
src/debug/Viewer.tsx | 6 +-
src/fields/DateField.ts | 3 +-
src/fields/Doc.ts | 182 +---
src/fields/InkField.ts | 11 +-
src/fields/List.ts | 61 +-
src/fields/ObjectField.ts | 25 +-
src/fields/RefField.ts | 4 +-
src/fields/RichTextUtils.ts | 17 +-
src/fields/Schema.ts | 6 +-
src/fields/SchemaHeaderField.ts | 25 +-
src/fields/ScriptField.ts | 96 +-
src/fields/Types.ts | 73 +-
src/fields/URLField.ts | 32 +-
src/fields/util.ts | 204 ++--
src/mobile/ImageUpload.tsx | 32 +-
src/mobile/MobileInkOverlay.tsx | 6 +-
src/mobile/MobileInterface.tsx | 2 +-
src/pen-gestures/GestureTypes.ts | 16 +
src/pen-gestures/GestureUtils.ts | 26 +-
src/pen-gestures/ndollar.ts | 10 +-
src/server/ActionUtilities.ts | 71 +-
src/server/ApiManagers/DataVizManager.ts | 15 +-
src/server/ApiManagers/DeleteManager.ts | 2 +-
src/server/ApiManagers/DownloadManager.ts | 385 ++++---
src/server/ApiManagers/GeneralGoogleManager.ts | 39 +-
src/server/ApiManagers/MongoStore.js | 414 ++++++++
src/server/ApiManagers/SearchManager.ts | 8 +-
src/server/ApiManagers/SessionManager.ts | 61 +-
src/server/ApiManagers/UploadManager.ts | 346 +++----
src/server/ApiManagers/UserManager.ts | 60 +-
src/server/Client.ts | 8 +-
src/server/DashSession/DashSessionAgent.ts | 28 +-
src/server/DashStats.ts | 241 +++--
src/server/DashUploadUtils.ts | 748 +++++++-------
src/server/Message.ts | 91 +-
src/server/RouteManager.ts | 92 +-
src/server/SocketData.ts | 35 +
src/server/apis/google/GoogleApiServerUtils.ts | 8 +-
src/server/authentication/AuthenticationManager.ts | 2 +-
src/server/authentication/DashUserModel.ts | 10 +-
src/server/database.ts | 166 +--
src/server/index.ts | 16 +-
src/server/remapUrl.ts | 69 +-
src/server/server_Initialization.ts | 251 ++---
src/server/websocket.ts | 380 +++----
tsconfig.json | 2 +-
webpack.config.js | 7 +-
202 files changed, 7525 insertions(+), 7291 deletions(-)
create mode 100644 src/ServerUtils.ts
delete mode 100644 src/client/apis/youtube/YoutubeBox.scss
delete mode 100644 src/client/apis/youtube/YoutubeBox.tsx
create mode 100644 src/client/util/DropActionTypes.ts
delete mode 100644 src/client/views/Touchable.tsx
create mode 100644 src/pen-gestures/GestureTypes.ts
create mode 100644 src/server/ApiManagers/MongoStore.js
create mode 100644 src/server/SocketData.ts
(limited to 'src/client/views/nodes/KeyValueBox.tsx')
diff --git a/.eslintrc.json b/.eslintrc.json
index 43bb53566..0c4e375a9 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -1,18 +1,77 @@
{
- "extends": ["airbnb", "prettier", "plugin:node/recommended"],
- "plugins": ["prettier"],
+ "extends": ["airbnb", "prettier", "plugin:node/recommended", "plugin:import/react"],
+ "plugins": ["prettier", "import", "@typescript-eslint"],
+ "settings": {
+ "import/parsers": {
+ "@typescript-eslint/parser": [".ts", ".tsx"]
+ },
+ "import/resolver": {
+ "typescript": {
+ "alwaysTryTypes": true // always try to resolve types under `@types` directory even it doesn't contain any source code, like `@types/unist`
+ },
+ "node": {
+ "extensions": [".js", ".ts", ".tsx"]
+ }
+ },
+ "import/extensions": [".js", ".jsx", ".ts", ".tsx"]
+ },
"rules": {
+ "import/extensions": [
+ "warn",
+ "ignorePackages",
+ {
+ "": "never",
+ "ts": "never",
+ "tsx": "never"
+ }
+ ],
+ "node/no-missing-import": 0,
"prettier/prettier": "error",
- "no-unused-vars": "warn",
"no-console": "off",
"func-names": "off",
"no-process-exit": "off",
"object-shorthand": "off",
"class-methods-use-this": "off",
- "single-quote": "off"
+ "single-quote": "off",
+ "max-classes-per-file": 0,
+ "node/no-unsupported-features/es-syntax": ["error", { "ignores": ["modules"] }],
+ "react/jsx-filename-extension": [2, { "extensions": [".js", ".jsx", ".ts", ".tsx"] }],
+ "import/prefer-default-export": "off",
+ "no-unused-expressions": "off",
+ "prefer-template": "off",
+ "no-inner-declarations": "off",
+ "no-plusplus": "off",
+ "no-multi-assign": "off",
+ "no-underscore-dangle": "off",
+ "no-nested-ternary": "off",
+ "lines-between-class-members": "off",
+ // Note: you must disable the base rule as it can report incorrect errors
+ "no-shadow": "off",
+ "@typescript-eslint/no-shadow": "warn",
+ "no-unused-vars": "off",
+ "@typescript-eslint/no-unused-vars": "error",
+ "react/destructuring-assignment": 0,
+ "no-restricted-globals": ["error", "event"],
+ "no-param-reassign": ["error", { "props": false }],
+ "no-alert": 0,
+ "radix": "off"
},
+ "overrides": [
+ {
+ "files": ["*.ts", "*.tsx"],
+ "rules": {
+ "no-undef": "off"
+ }
+ }
+ ],
+ "parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 11,
- "sourceType": "module"
+ "sourceType": "module",
+ "ecmaFeatures": {
+ "jsx": true,
+ "modules": true
+ },
+ "useJSXTextNode": false
}
}
diff --git a/package-lock.json b/package-lock.json
index 6d0df30e8..d366bb717 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -48,6 +48,8 @@
"@types/reveal": "^4.2.0",
"@types/supercluster": "^7.1.3",
"@types/web": "^0.0.143",
+ "@types/webpack-hot-middleware": "^2.25.9",
+ "@typescript-eslint/parser": "^7.6.0",
"@webscopeio/react-textarea-autocomplete": "^4.9.2",
"adm-zip": "^0.5.10",
"archiver": "^7.0.1",
@@ -84,6 +86,7 @@
"D": "^1.0.0",
"d3": "^7.8.5",
"depcheck": "^1.4.7",
+ "eslint-webpack-plugin": "^4.1.0",
"exif": "^0.6.0",
"exifr": "^7.1.3",
"express": "^4.18.2",
@@ -203,7 +206,9 @@
"tough-cookie": "^4.1.3",
"tslint": "^6.1.3",
"tslint-loader": "^3.5.4",
+ "typescript": "^5.3.3",
"typescript-collections": "^1.3.3",
+ "typescript-eslint": "^7.6.0",
"typescript-language-server": "^4.1.3",
"uninstall": "^0.0.0",
"url": "^0.11.3",
@@ -215,6 +220,7 @@
"webpack": "^5.89.0",
"webpack-cli": "^5.1.4",
"webpack-dev-middleware": "^7.0.0",
+ "webpack-hot-middleware": "^2.25.4",
"wikijs": "^6.4.1",
"words-to-numbers": "^1.5.1",
"xoauth2": "^1.2.0",
@@ -263,7 +269,6 @@
"@types/uuid": "^9.0.7",
"@types/valid-url": "^1.0.7",
"@types/webpack": "^5.28.5",
- "@types/webpack-hot-middleware": "^2.25.9",
"@types/youtube": "0.0.50",
"chai": "^5.0.0",
"cross-env": "^7.0.3",
@@ -272,7 +277,8 @@
"eslint-config-airbnb": "^19.0.4",
"eslint-config-node": "^4.1.0",
"eslint-config-prettier": "^9.1.0",
- "eslint-plugin-import": "^2.29.0",
+ "eslint-import-resolver-typescript": "^3.6.1",
+ "eslint-plugin-import": "^2.29.1",
"eslint-plugin-jsx-a11y": "^6.8.0",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-prettier": "^5.0.1",
@@ -287,16 +293,16 @@
"ts-loader": "^9.5.1",
"ts-node": "^10.9.1",
"ts-node-dev": "^2.0.0",
- "typescript": "^5.3.3",
- "webpack-dev-server": "^5.0.4",
- "webpack-hot-middleware": "^2.25.4"
+ "webpack-dev-server": "^5.0.4"
+ },
+ "engines": {
+ "node": ">=10.0.0"
}
},
"node_modules/@aashutoshrathi/word-wrap": {
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz",
"integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==",
- "dev": true,
"engines": {
"node": ">=0.10.0"
}
@@ -2415,7 +2421,6 @@
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
"integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==",
- "dev": true,
"dependencies": {
"eslint-visitor-keys": "^3.3.0"
},
@@ -2430,7 +2435,6 @@
"version": "4.10.0",
"resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz",
"integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==",
- "dev": true,
"engines": {
"node": "^12.0.0 || ^14.0.0 || >=16.0.0"
}
@@ -2439,7 +2443,6 @@
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz",
"integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==",
- "dev": true,
"dependencies": {
"ajv": "^6.12.4",
"debug": "^4.3.2",
@@ -2461,14 +2464,12 @@
"node_modules/@eslint/eslintrc/node_modules/argparse": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
- "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
- "dev": true
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
},
"node_modules/@eslint/eslintrc/node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
- "dev": true,
"dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@@ -2478,7 +2479,6 @@
"version": "13.24.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz",
"integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==",
- "dev": true,
"dependencies": {
"type-fest": "^0.20.2"
},
@@ -2493,7 +2493,6 @@
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
- "dev": true,
"dependencies": {
"argparse": "^2.0.1"
},
@@ -2505,7 +2504,6 @@
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
- "dev": true,
"dependencies": {
"brace-expansion": "^1.1.7"
},
@@ -2517,7 +2515,6 @@
"version": "8.57.0",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz",
"integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==",
- "dev": true,
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}
@@ -2758,7 +2755,6 @@
"version": "0.11.14",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz",
"integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==",
- "dev": true,
"dependencies": {
"@humanwhocodes/object-schema": "^2.0.2",
"debug": "^4.3.1",
@@ -2772,7 +2768,6 @@
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
- "dev": true,
"dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@@ -2782,7 +2777,6 @@
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
- "dev": true,
"dependencies": {
"brace-expansion": "^1.1.7"
},
@@ -2794,7 +2788,6 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
"integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
- "dev": true,
"engines": {
"node": ">=12.22"
},
@@ -2806,8 +2799,7 @@
"node_modules/@humanwhocodes/object-schema": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz",
- "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==",
- "dev": true
+ "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA=="
},
"node_modules/@icons/material": {
"version": "0.2.4",
@@ -2891,6 +2883,97 @@
"url": "https://github.com/chalk/strip-ansi?sponsor=1"
}
},
+ "node_modules/@jest/schemas": {
+ "version": "29.6.3",
+ "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz",
+ "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==",
+ "dependencies": {
+ "@sinclair/typebox": "^0.27.8"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/@jest/types": {
+ "version": "29.6.3",
+ "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz",
+ "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==",
+ "dependencies": {
+ "@jest/schemas": "^29.6.3",
+ "@types/istanbul-lib-coverage": "^2.0.0",
+ "@types/istanbul-reports": "^3.0.0",
+ "@types/node": "*",
+ "@types/yargs": "^17.0.8",
+ "chalk": "^4.0.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/@jest/types/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/@jest/types/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/@jest/types/node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/@jest/types/node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+ },
+ "node_modules/@jest/types/node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@jest/types/node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/@jimp/bmp": {
"version": "0.22.12",
"resolved": "https://registry.npmjs.org/@jimp/bmp/-/bmp-0.22.12.tgz",
@@ -3817,7 +3900,6 @@
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
"integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
- "dev": true,
"dependencies": {
"@nodelib/fs.stat": "2.0.5",
"run-parallel": "^1.1.9"
@@ -3830,7 +3912,6 @@
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
"integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
- "dev": true,
"engines": {
"node": ">= 8"
}
@@ -3839,7 +3920,6 @@
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
"integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
- "dev": true,
"dependencies": {
"@nodelib/fs.scandir": "2.1.5",
"fastq": "^1.6.0"
@@ -6733,6 +6813,11 @@
"url": "https://ko-fi.com/killymxi"
}
},
+ "node_modules/@sinclair/typebox": {
+ "version": "0.27.8",
+ "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz",
+ "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA=="
+ },
"node_modules/@sindresorhus/is": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-6.2.0.tgz",
@@ -8503,7 +8588,6 @@
"version": "3.4.38",
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz",
"integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==",
- "dev": true,
"dependencies": {
"@types/node": "*"
}
@@ -8978,6 +9062,27 @@
"@types/node": "*"
}
},
+ "node_modules/@types/istanbul-lib-coverage": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz",
+ "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w=="
+ },
+ "node_modules/@types/istanbul-lib-report": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz",
+ "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==",
+ "dependencies": {
+ "@types/istanbul-lib-coverage": "*"
+ }
+ },
+ "node_modules/@types/istanbul-reports": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz",
+ "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==",
+ "dependencies": {
+ "@types/istanbul-lib-report": "*"
+ }
+ },
"node_modules/@types/jquery": {
"version": "3.5.29",
"resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.29.tgz",
@@ -9359,6 +9464,11 @@
"integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==",
"optional": true
},
+ "node_modules/@types/semver": {
+ "version": "7.5.8",
+ "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz",
+ "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ=="
+ },
"node_modules/@types/send": {
"version": "0.17.4",
"resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz",
@@ -9494,7 +9604,6 @@
"version": "2.25.9",
"resolved": "https://registry.npmjs.org/@types/webpack-hot-middleware/-/webpack-hot-middleware-2.25.9.tgz",
"integrity": "sha512-fad4T9VfocBjS2fZxlqkGoXoVUAjVp0EEnKBRqPwnhEEDN/FqJoFkSP5t9O1gPH75qsyG2kkT/GSUqSNTn1ZPg==",
- "dev": true,
"dependencies": {
"@types/connect": "*",
"tapable": "^2.2.0",
@@ -9518,12 +9627,311 @@
"@types/node": "*"
}
},
+ "node_modules/@types/yargs": {
+ "version": "17.0.32",
+ "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz",
+ "integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==",
+ "dependencies": {
+ "@types/yargs-parser": "*"
+ }
+ },
+ "node_modules/@types/yargs-parser": {
+ "version": "21.0.3",
+ "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz",
+ "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ=="
+ },
"node_modules/@types/youtube": {
"version": "0.0.50",
"resolved": "https://registry.npmjs.org/@types/youtube/-/youtube-0.0.50.tgz",
"integrity": "sha512-d4GpH4uPYp9W07kc487tiq6V/EUHl18vZWFMbQoe4Sk9LXEWzFi/BMf9x7TI4m7/j7gU3KeX8H6M8aPBgykeLw==",
"dev": true
},
+ "node_modules/@typescript-eslint/eslint-plugin": {
+ "version": "7.6.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.6.0.tgz",
+ "integrity": "sha512-gKmTNwZnblUdnTIJu3e9kmeRRzV2j1a/LUO27KNNAnIC5zjy1aSvXSRp4rVNlmAoHlQ7HzX42NbKpcSr4jF80A==",
+ "dependencies": {
+ "@eslint-community/regexpp": "^4.10.0",
+ "@typescript-eslint/scope-manager": "7.6.0",
+ "@typescript-eslint/type-utils": "7.6.0",
+ "@typescript-eslint/utils": "7.6.0",
+ "@typescript-eslint/visitor-keys": "7.6.0",
+ "debug": "^4.3.4",
+ "graphemer": "^1.4.0",
+ "ignore": "^5.3.1",
+ "natural-compare": "^1.4.0",
+ "semver": "^7.6.0",
+ "ts-api-utils": "^1.3.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || >=20.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "@typescript-eslint/parser": "^7.0.0",
+ "eslint": "^8.56.0"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin/node_modules/lru-cache": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+ "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+ "dependencies": {
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": {
+ "version": "7.6.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz",
+ "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==",
+ "dependencies": {
+ "lru-cache": "^6.0.0"
+ },
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin/node_modules/yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
+ },
+ "node_modules/@typescript-eslint/parser": {
+ "version": "7.6.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.6.0.tgz",
+ "integrity": "sha512-usPMPHcwX3ZoPWnBnhhorc14NJw9J4HpSXQX4urF2TPKG0au0XhJoZyX62fmvdHONUkmyUe74Hzm1//XA+BoYg==",
+ "dependencies": {
+ "@typescript-eslint/scope-manager": "7.6.0",
+ "@typescript-eslint/types": "7.6.0",
+ "@typescript-eslint/typescript-estree": "7.6.0",
+ "@typescript-eslint/visitor-keys": "7.6.0",
+ "debug": "^4.3.4"
+ },
+ "engines": {
+ "node": "^18.18.0 || >=20.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.56.0"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/scope-manager": {
+ "version": "7.6.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.6.0.tgz",
+ "integrity": "sha512-ngttyfExA5PsHSx0rdFgnADMYQi+Zkeiv4/ZxGYUWd0nLs63Ha0ksmp8VMxAIC0wtCFxMos7Lt3PszJssG/E6w==",
+ "dependencies": {
+ "@typescript-eslint/types": "7.6.0",
+ "@typescript-eslint/visitor-keys": "7.6.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || >=20.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/type-utils": {
+ "version": "7.6.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.6.0.tgz",
+ "integrity": "sha512-NxAfqAPNLG6LTmy7uZgpK8KcuiS2NZD/HlThPXQRGwz6u7MDBWRVliEEl1Gj6U7++kVJTpehkhZzCJLMK66Scw==",
+ "dependencies": {
+ "@typescript-eslint/typescript-estree": "7.6.0",
+ "@typescript-eslint/utils": "7.6.0",
+ "debug": "^4.3.4",
+ "ts-api-utils": "^1.3.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || >=20.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.56.0"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/types": {
+ "version": "7.6.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.6.0.tgz",
+ "integrity": "sha512-h02rYQn8J+MureCvHVVzhl69/GAfQGPQZmOMjG1KfCl7o3HtMSlPaPUAPu6lLctXI5ySRGIYk94clD/AUMCUgQ==",
+ "engines": {
+ "node": "^18.18.0 || >=20.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree": {
+ "version": "7.6.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.6.0.tgz",
+ "integrity": "sha512-+7Y/GP9VuYibecrCQWSKgl3GvUM5cILRttpWtnAu8GNL9j11e4tbuGZmZjJ8ejnKYyBRb2ddGQ3rEFCq3QjMJw==",
+ "dependencies": {
+ "@typescript-eslint/types": "7.6.0",
+ "@typescript-eslint/visitor-keys": "7.6.0",
+ "debug": "^4.3.4",
+ "globby": "^11.1.0",
+ "is-glob": "^4.0.3",
+ "minimatch": "^9.0.4",
+ "semver": "^7.6.0",
+ "ts-api-utils": "^1.3.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || >=20.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/lru-cache": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+ "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+ "dependencies": {
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
+ "version": "9.0.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz",
+ "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": {
+ "version": "7.6.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz",
+ "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==",
+ "dependencies": {
+ "lru-cache": "^6.0.0"
+ },
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
+ },
+ "node_modules/@typescript-eslint/utils": {
+ "version": "7.6.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.6.0.tgz",
+ "integrity": "sha512-x54gaSsRRI+Nwz59TXpCsr6harB98qjXYzsRxGqvA5Ue3kQH+FxS7FYU81g/omn22ML2pZJkisy6Q+ElK8pBCA==",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.4.0",
+ "@types/json-schema": "^7.0.15",
+ "@types/semver": "^7.5.8",
+ "@typescript-eslint/scope-manager": "7.6.0",
+ "@typescript-eslint/types": "7.6.0",
+ "@typescript-eslint/typescript-estree": "7.6.0",
+ "semver": "^7.6.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || >=20.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.56.0"
+ }
+ },
+ "node_modules/@typescript-eslint/utils/node_modules/lru-cache": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+ "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+ "dependencies": {
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@typescript-eslint/utils/node_modules/semver": {
+ "version": "7.6.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz",
+ "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==",
+ "dependencies": {
+ "lru-cache": "^6.0.0"
+ },
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@typescript-eslint/utils/node_modules/yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
+ },
+ "node_modules/@typescript-eslint/visitor-keys": {
+ "version": "7.6.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.6.0.tgz",
+ "integrity": "sha512-4eLB7t+LlNUmXzfOu1VAIAdkjbu5xNSerURS9X/S5TUKWFRpXRQZbmtPqgKmYx8bj3J0irtQXSiWAOY82v+cgw==",
+ "dependencies": {
+ "@typescript-eslint/types": "7.6.0",
+ "eslint-visitor-keys": "^3.4.3"
+ },
+ "engines": {
+ "node": "^18.18.0 || >=20.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
"node_modules/@ungap/structured-clone": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz",
@@ -9993,7 +10401,6 @@
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz",
"integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==",
- "dev": true,
"engines": [
"node >= 0.8.0"
],
@@ -14367,6 +14774,20 @@
"node": ">=6.0"
}
},
+ "node_modules/ci-info": {
+ "version": "3.9.0",
+ "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz",
+ "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/sibiraj-s"
+ }
+ ],
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/class-transformer": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz",
@@ -15906,8 +16327,7 @@
"node_modules/deep-is": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
- "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
- "dev": true
+ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="
},
"node_modules/deepmerge": {
"version": "4.3.1",
@@ -16188,6 +16608,17 @@
"node": ">=0.3.1"
}
},
+ "node_modules/dir-glob": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
+ "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
+ "dependencies": {
+ "path-type": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/dns-packet": {
"version": "5.6.1",
"resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz",
@@ -16204,7 +16635,6 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
"integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
- "dev": true,
"dependencies": {
"esutils": "^2.0.2"
},
@@ -16699,7 +17129,6 @@
"version": "8.57.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz",
"integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==",
- "dev": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.6.1",
@@ -17678,6 +18107,31 @@
"ms": "^2.1.1"
}
},
+ "node_modules/eslint-import-resolver-typescript": {
+ "version": "3.6.1",
+ "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.6.1.tgz",
+ "integrity": "sha512-xgdptdoi5W3niYeuQxKmzVDTATvLYqhpwmykwsh7f6HIOStGWEIL9iqZgQDF9u9OEzrRwR8no5q2VT+bjAujTg==",
+ "dev": true,
+ "dependencies": {
+ "debug": "^4.3.4",
+ "enhanced-resolve": "^5.12.0",
+ "eslint-module-utils": "^2.7.4",
+ "fast-glob": "^3.3.1",
+ "get-tsconfig": "^4.5.0",
+ "is-core-module": "^2.11.0",
+ "is-glob": "^4.0.3"
+ },
+ "engines": {
+ "node": "^14.18.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/unts/projects/eslint-import-resolver-ts"
+ },
+ "peerDependencies": {
+ "eslint": "*",
+ "eslint-plugin-import": "*"
+ }
+ },
"node_modules/eslint-module-utils": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.1.tgz",
@@ -18044,7 +18498,6 @@
"version": "7.2.2",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz",
"integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==",
- "dev": true,
"dependencies": {
"esrecurse": "^4.3.0",
"estraverse": "^5.2.0"
@@ -18084,7 +18537,6 @@
"version": "3.4.3",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
"integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
- "dev": true,
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
@@ -18092,11 +18544,69 @@
"url": "https://opencollective.com/eslint"
}
},
+ "node_modules/eslint-webpack-plugin": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/eslint-webpack-plugin/-/eslint-webpack-plugin-4.1.0.tgz",
+ "integrity": "sha512-C3wAG2jyockIhN0YRLuKieKj2nx/gnE/VHmoHemD5ifnAtY6ZU+jNPfzPoX4Zd6RIbUyWTiZUh/ofUlBhoAX7w==",
+ "dependencies": {
+ "@types/eslint": "^8.56.5",
+ "jest-worker": "^29.7.0",
+ "micromatch": "^4.0.5",
+ "normalize-path": "^3.0.0",
+ "schema-utils": "^4.2.0"
+ },
+ "engines": {
+ "node": ">= 14.15.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ },
+ "peerDependencies": {
+ "eslint": "^8.0.0",
+ "webpack": "^5.0.0"
+ }
+ },
+ "node_modules/eslint-webpack-plugin/node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/eslint-webpack-plugin/node_modules/jest-worker": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz",
+ "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==",
+ "dependencies": {
+ "@types/node": "*",
+ "jest-util": "^29.7.0",
+ "merge-stream": "^2.0.0",
+ "supports-color": "^8.0.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/eslint-webpack-plugin/node_modules/supports-color": {
+ "version": "8.1.1",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
+ "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/supports-color?sponsor=1"
+ }
+ },
"node_modules/eslint/node_modules/ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "dev": true,
"dependencies": {
"color-convert": "^2.0.1"
},
@@ -18110,14 +18620,12 @@
"node_modules/eslint/node_modules/argparse": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
- "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
- "dev": true
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
},
"node_modules/eslint/node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
- "dev": true,
"dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@@ -18127,7 +18635,6 @@
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
- "dev": true,
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
@@ -18143,7 +18650,6 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "dev": true,
"dependencies": {
"color-name": "~1.1.4"
},
@@ -18154,14 +18660,12 @@
"node_modules/eslint/node_modules/color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
- "dev": true
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
"node_modules/eslint/node_modules/globals": {
"version": "13.24.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz",
"integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==",
- "dev": true,
"dependencies": {
"type-fest": "^0.20.2"
},
@@ -18176,7 +18680,6 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
- "dev": true,
"engines": {
"node": ">=8"
}
@@ -18185,7 +18688,6 @@
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
- "dev": true,
"dependencies": {
"argparse": "^2.0.1"
},
@@ -18197,7 +18699,6 @@
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
- "dev": true,
"dependencies": {
"brace-expansion": "^1.1.7"
},
@@ -18209,7 +18710,6 @@
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
- "dev": true,
"dependencies": {
"has-flag": "^4.0.0"
},
@@ -18221,7 +18721,6 @@
"version": "9.6.1",
"resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz",
"integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==",
- "dev": true,
"dependencies": {
"acorn": "^8.9.0",
"acorn-jsx": "^5.3.2",
@@ -18250,7 +18749,6 @@
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz",
"integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==",
- "dev": true,
"dependencies": {
"estraverse": "^5.1.0"
},
@@ -18599,6 +19097,32 @@
"resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz",
"integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ=="
},
+ "node_modules/fast-glob": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz",
+ "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==",
+ "dependencies": {
+ "@nodelib/fs.stat": "^2.0.2",
+ "@nodelib/fs.walk": "^1.2.3",
+ "glob-parent": "^5.1.2",
+ "merge2": "^1.3.0",
+ "micromatch": "^4.0.4"
+ },
+ "engines": {
+ "node": ">=8.6.0"
+ }
+ },
+ "node_modules/fast-glob/node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/fast-json-stable-stringify": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
@@ -18607,8 +19131,7 @@
"node_modules/fast-levenshtein": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
- "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
- "dev": true
+ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="
},
"node_modules/fastest-levenshtein": {
"version": "1.0.16",
@@ -18622,7 +19145,6 @@
"version": "1.17.1",
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz",
"integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==",
- "dev": true,
"dependencies": {
"reusify": "^1.0.4"
}
@@ -18693,7 +19215,6 @@
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
"integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
- "dev": true,
"dependencies": {
"flat-cache": "^3.0.4"
},
@@ -18848,7 +19369,6 @@
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
"integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
- "dev": true,
"dependencies": {
"locate-path": "^6.0.0",
"path-exists": "^4.0.0"
@@ -18891,7 +19411,6 @@
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz",
"integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==",
- "dev": true,
"dependencies": {
"flatted": "^3.2.9",
"keyv": "^4.5.3",
@@ -18905,7 +19424,6 @@
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
- "dev": true,
"dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@@ -18915,7 +19433,6 @@
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
- "dev": true,
"dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
@@ -18935,7 +19452,6 @@
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
- "dev": true,
"dependencies": {
"brace-expansion": "^1.1.7"
},
@@ -18947,7 +19463,6 @@
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
- "dev": true,
"dependencies": {
"glob": "^7.1.3"
},
@@ -18961,8 +19476,7 @@
"node_modules/flatted": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz",
- "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==",
- "dev": true
+ "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw=="
},
"node_modules/flexlayout-react": {
"version": "0.7.15",
@@ -19693,6 +20207,18 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/get-tsconfig": {
+ "version": "4.7.3",
+ "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.3.tgz",
+ "integrity": "sha512-ZvkrzoUA0PQZM6fy6+/Hce561s+faD1rsNwhnO5FelNjyy7EMGJ3Rz1AQ8GYDWjhRs/7dBLOEJvhK8MiEJOAFg==",
+ "dev": true,
+ "dependencies": {
+ "resolve-pkg-maps": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
+ }
+ },
"node_modules/get-value": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz",
@@ -19748,7 +20274,6 @@
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
"integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
- "dev": true,
"dependencies": {
"is-glob": "^4.0.3"
},
@@ -19846,6 +20371,25 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/globby": {
+ "version": "11.1.0",
+ "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
+ "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==",
+ "dependencies": {
+ "array-union": "^2.1.0",
+ "dir-glob": "^3.0.1",
+ "fast-glob": "^3.2.9",
+ "ignore": "^5.2.0",
+ "merge2": "^1.4.1",
+ "slash": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/golden-layout": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/golden-layout/-/golden-layout-2.6.0.tgz",
@@ -19963,8 +20507,7 @@
"node_modules/graphemer": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
- "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
- "dev": true
+ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag=="
},
"node_modules/grid-index": {
"version": "1.1.0",
@@ -20326,7 +20869,6 @@
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.5.2.tgz",
"integrity": "sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA==",
- "dev": true,
"funding": [
{
"type": "github",
@@ -20973,7 +21515,6 @@
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
"integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
- "dev": true,
"engines": {
"node": ">=0.8.19"
}
@@ -21700,7 +22241,6 @@
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
"integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
- "dev": true,
"engines": {
"node": ">=8"
}
@@ -21987,6 +22527,86 @@
"@pkgjs/parseargs": "^0.11.0"
}
},
+ "node_modules/jest-util": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz",
+ "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==",
+ "dependencies": {
+ "@jest/types": "^29.6.3",
+ "@types/node": "*",
+ "chalk": "^4.0.0",
+ "ci-info": "^3.2.0",
+ "graceful-fs": "^4.2.9",
+ "picomatch": "^2.2.3"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/jest-util/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/jest-util/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/jest-util/node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/jest-util/node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+ },
+ "node_modules/jest-util/node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/jest-util/node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/jest-worker": {
"version": "27.5.1",
"resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz",
@@ -22243,8 +22863,7 @@
"node_modules/json-stable-stringify-without-jsonify": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
- "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
- "dev": true
+ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="
},
"node_modules/json-stringify-pretty-compact": {
"version": "3.0.0",
@@ -22588,7 +23207,6 @@
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
"integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
- "dev": true,
"dependencies": {
"prelude-ls": "^1.2.1",
"type-check": "~0.4.0"
@@ -22650,7 +23268,6 @@
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
"integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
- "dev": true,
"dependencies": {
"p-locate": "^5.0.0"
},
@@ -23341,6 +23958,14 @@
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
"integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w=="
},
+ "node_modules/merge2": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+ "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
"node_modules/methods": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
@@ -24484,8 +25109,7 @@
"node_modules/natural-compare": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
- "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
- "dev": true
+ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="
},
"node_modules/needle": {
"version": "2.9.1",
@@ -27705,7 +28329,6 @@
"version": "0.9.3",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz",
"integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==",
- "dev": true,
"dependencies": {
"@aashutoshrathi/word-wrap": "^1.2.3",
"deep-is": "^0.1.3",
@@ -27758,7 +28381,6 @@
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
"integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
- "dev": true,
"dependencies": {
"p-limit": "^3.0.2"
},
@@ -27773,7 +28395,6 @@
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
"integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
- "dev": true,
"dependencies": {
"yocto-queue": "^0.1.0"
},
@@ -27788,7 +28409,6 @@
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
"integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
- "dev": true,
"engines": {
"node": ">=10"
},
@@ -28519,7 +29139,6 @@
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
"integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
- "dev": true,
"engines": {
"node": ">= 0.8.0"
}
@@ -28941,7 +29560,6 @@
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
"integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
- "dev": true,
"funding": [
{
"type": "github",
@@ -30155,6 +30773,15 @@
"node": ">=8"
}
},
+ "node_modules/resolve-pkg-maps": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
+ "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
+ "dev": true,
+ "funding": {
+ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
+ }
+ },
"node_modules/resolve-protobuf-schema": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz",
@@ -30209,7 +30836,6 @@
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
"integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
- "dev": true,
"engines": {
"iojs": ">=1.0.0",
"node": ">=0.10.0"
@@ -30281,7 +30907,6 @@
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
"integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
- "dev": true,
"funding": [
{
"type": "github",
@@ -31026,6 +31651,14 @@
"resolved": "https://registry.npmjs.org/skmeans/-/skmeans-0.9.7.tgz",
"integrity": "sha512-hNj1/oZ7ygsfmPZ7ZfN5MUBRoGg1gtpnImuJBgLO0ljQ67DtJuiQaiYdS4lUA6s0KCwnPhGivtC/WRwIZLkHyg=="
},
+ "node_modules/slash": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
+ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/slice-ansi": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz",
@@ -31688,7 +32321,6 @@
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
"integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
- "dev": true,
"engines": {
"node": ">=8"
},
@@ -32090,8 +32722,7 @@
"node_modules/text-table": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
- "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
- "dev": true
+ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw=="
},
"node_modules/textarea-caret": {
"version": "3.1.0",
@@ -32333,6 +32964,17 @@
"resolved": "https://registry.npmjs.org/tryit/-/tryit-1.0.3.tgz",
"integrity": "sha512-6C5h3CE+0qjGp+YKYTs74xR0k/Nw/ePtl/Lp6CCf44hqBQ66qnH1sDFR5mV/Gc48EsrHLB53lCFSffQCkka3kg=="
},
+ "node_modules/ts-api-utils": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz",
+ "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==",
+ "engines": {
+ "node": ">=16"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.2.0"
+ }
+ },
"node_modules/ts-loader": {
"version": "9.5.1",
"resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.1.tgz",
@@ -32934,7 +33576,6 @@
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
"integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
- "dev": true,
"dependencies": {
"prelude-ls": "^1.2.1"
},
@@ -32946,7 +33587,6 @@
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
"integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
- "dev": true,
"engines": {
"node": ">=10"
},
@@ -33056,6 +33696,31 @@
"resolved": "https://registry.npmjs.org/typescript-collections/-/typescript-collections-1.3.3.tgz",
"integrity": "sha512-7sI4e/bZijOzyURng88oOFZCISQPTHozfE2sUu5AviFYk5QV7fYGb6YiDl+vKjF/pICA354JImBImL9XJWUvdQ=="
},
+ "node_modules/typescript-eslint": {
+ "version": "7.6.0",
+ "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-7.6.0.tgz",
+ "integrity": "sha512-LY6vH6F1l5jpGqRtU+uK4+mOecIb4Cd4kaz1hAiJrgnNiHUA8wiw8BkJyYS+MRLM69F1QuSKwtGlQqnGl1Rc6w==",
+ "dependencies": {
+ "@typescript-eslint/eslint-plugin": "7.6.0",
+ "@typescript-eslint/parser": "7.6.0",
+ "@typescript-eslint/utils": "7.6.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || >=20.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.56.0"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
"node_modules/typescript-language-server": {
"version": "4.3.3",
"resolved": "https://registry.npmjs.org/typescript-language-server/-/typescript-language-server-4.3.3.tgz",
@@ -33952,7 +34617,6 @@
"version": "2.26.1",
"resolved": "https://registry.npmjs.org/webpack-hot-middleware/-/webpack-hot-middleware-2.26.1.tgz",
"integrity": "sha512-khZGfAeJx6I8K9zKohEWWYN6KDlVw2DHownoe+6Vtwj1LP9WFgegXnVMSkZ/dBEBtXFwrkkydsaPFlB7f8wU2A==",
- "dev": true,
"dependencies": {
"ansi-html-community": "0.0.8",
"html-entities": "^2.1.0",
diff --git a/package.json b/package.json
index 13d97b45c..3f2f5a70f 100644
--- a/package.json
+++ b/package.json
@@ -3,6 +3,9 @@
"version": "1.0.0",
"description": "",
"main": "index.js",
+ "engines": {
+ "node": ">=12.0.0"
+ },
"browser": {
"child_process": false
},
@@ -62,7 +65,6 @@
"@types/uuid": "^9.0.7",
"@types/valid-url": "^1.0.7",
"@types/webpack": "^5.28.5",
- "@types/webpack-hot-middleware": "^2.25.9",
"@types/youtube": "0.0.50",
"chai": "^5.0.0",
"cross-env": "^7.0.3",
@@ -71,7 +73,8 @@
"eslint-config-airbnb": "^19.0.4",
"eslint-config-node": "^4.1.0",
"eslint-config-prettier": "^9.1.0",
- "eslint-plugin-import": "^2.29.0",
+ "eslint-import-resolver-typescript": "^3.6.1",
+ "eslint-plugin-import": "^2.29.1",
"eslint-plugin-jsx-a11y": "^6.8.0",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-prettier": "^5.0.1",
@@ -86,9 +89,7 @@
"ts-loader": "^9.5.1",
"ts-node": "^10.9.1",
"ts-node-dev": "^2.0.0",
- "typescript": "^5.3.3",
- "webpack-dev-server": "^5.0.4",
- "webpack-hot-middleware": "^2.25.4"
+ "webpack-dev-server": "^5.0.4"
},
"dependencies": {
"@adobe/react-spectrum": "^3.32.2",
@@ -131,6 +132,8 @@
"@types/reveal": "^4.2.0",
"@types/supercluster": "^7.1.3",
"@types/web": "^0.0.143",
+ "@types/webpack-hot-middleware": "^2.25.9",
+ "@typescript-eslint/parser": "^7.6.0",
"@webscopeio/react-textarea-autocomplete": "^4.9.2",
"adm-zip": "^0.5.10",
"archiver": "^7.0.1",
@@ -167,6 +170,7 @@
"D": "^1.0.0",
"d3": "^7.8.5",
"depcheck": "^1.4.7",
+ "eslint-webpack-plugin": "^4.1.0",
"exif": "^0.6.0",
"exifr": "^7.1.3",
"express": "^4.18.2",
@@ -286,7 +290,9 @@
"tough-cookie": "^4.1.3",
"tslint": "^6.1.3",
"tslint-loader": "^3.5.4",
+ "typescript": "^5.3.3",
"typescript-collections": "^1.3.3",
+ "typescript-eslint": "^7.6.0",
"typescript-language-server": "^4.1.3",
"uninstall": "^0.0.0",
"url": "^0.11.3",
@@ -298,6 +304,7 @@
"webpack": "^5.89.0",
"webpack-cli": "^5.1.4",
"webpack-dev-middleware": "^7.0.0",
+ "webpack-hot-middleware": "^2.25.4",
"wikijs": "^6.4.1",
"words-to-numbers": "^1.5.1",
"xoauth2": "^1.2.0",
diff --git a/src/ServerUtils.ts b/src/ServerUtils.ts
new file mode 100644
index 000000000..ade4ca35d
--- /dev/null
+++ b/src/ServerUtils.ts
@@ -0,0 +1,27 @@
+import { Socket } from 'socket.io';
+import { Message } from './server/Message';
+import { Utils } from './Utils';
+
+export namespace ServerUtils {
+ export function Emit(socket: Socket, message: Message, args: T) {
+ Utils.log('Emit', message.Name, args, false);
+ socket.emit(message.Message, args);
+ }
+
+ export function AddServerHandler(socket: Socket, message: Message, handler: (args: T) => any) {
+ socket.on(message.Message, Utils.loggingCallback('Incoming', handler, message.Name));
+ }
+
+ export function AddServerHandlerCallback(socket: Socket, message: Message, handler: (args: [T, (res: any) => any]) => any) {
+ socket.on(message.Message, (arg: T, fn: (res: any) => any) => {
+ Utils.log('S receiving', message.Name, arg, true);
+ handler([arg, Utils.loggingCallback('S sending', fn, message.Name)]);
+ });
+ }
+ export type RoomHandler = (socket: Socket, room: string) => any;
+ export type UsedSockets = Socket;
+ export type RoomMessage = 'create or join' | 'created' | 'joined';
+ export function AddRoomHandler(socket: Socket, message: RoomMessage, handler: RoomHandler) {
+ socket.on(message, (room: any) => handler(socket, room));
+ }
+}
diff --git a/src/Utils.ts b/src/Utils.ts
index 291d7c799..0455fd19a 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -1,52 +1,12 @@
-import * as Color from 'color';
-import { ColorResult } from 'react-color';
-import * as rp from 'request-promise';
-import { Socket } from 'socket.io';
import * as uuid from 'uuid';
-import { DocumentType } from './client/documents/DocumentTypes';
-import { Colors } from './client/views/global/globalEnums';
-import { DocumentView } from './client/views/nodes/DocumentView';
-import { Message } from './server/Message';
+export function clamp(n: number, lower: number, upper: number) {
+ return Math.max(lower, Math.min(upper, n));
+}
export namespace Utils {
- export let CLICK_TIME = 300;
- export let DRAG_THRESHOLD = 4;
- export let SNAP_THRESHOLD = 10;
- export function isClick(x: number, y: number, downX: number, downY: number, downTime: number) {
- return Date.now() - downTime < Utils.CLICK_TIME && Math.abs(x - downX) < Utils.DRAG_THRESHOLD && Math.abs(y - downY) < Utils.DRAG_THRESHOLD;
- }
-
- export function cleanDocumentType(type: DocumentType) {
- switch (type) {
- case DocumentType.IMG:
- return 'Image';
- case DocumentType.AUDIO:
- return 'Audio';
- case DocumentType.COL:
- return 'Collection';
- case DocumentType.RTF:
- return 'Text';
- default:
- return type.charAt(0).toUpperCase() + type.slice(1);
- }
- }
-
- export function readUploadedFileAsText(inputFile: File) {
- const temporaryFileReader = new FileReader();
-
- return new Promise((resolve, reject) => {
- temporaryFileReader.onerror = () => {
- temporaryFileReader.abort();
- reject(new DOMException('Problem parsing input file.'));
- };
-
- temporaryFileReader.onload = () => {
- resolve(temporaryFileReader.result);
- };
- temporaryFileReader.readAsText(inputFile);
- });
+ export function GuestID() {
+ return '__guest__';
}
-
export function GenerateGuid(): string {
return uuid.v4();
}
@@ -55,49 +15,33 @@ export namespace Utils {
return uuid.v5(seed, uuid.v5.URL);
}
- export function GuestID() {
- return '__guest__';
- }
+ export const loggingEnabled: Boolean = false;
+ export const logFilter: number | undefined = undefined;
- /**
- * Uploads an image buffer to the server and stores with specified filename. by default the image
- * is stored at multiple resolutions each retrieved by using the filename appended with _o, _s, _m, _l (indicating original, small, medium, or large)
- * @param imageUri the bytes of the image
- * @param returnedFilename the base filename to store the image on the server
- * @param nosuffix optionally suppress creating multiple resolution images
- */
- export async function convertDataUri(imageUri: string, returnedFilename: string, nosuffix = false, replaceRootFilename?: string) {
- try {
- const posting = Utils.prepend('/uploadURI');
- const returnedUri = await rp.post(posting, {
- body: {
- uri: imageUri,
- name: returnedFilename,
- nosuffix,
- replaceRootFilename,
- },
- json: true,
- });
- return returnedUri;
- } catch (e) {
- console.log('ConvertDataURI :' + e);
+ export function log(prefixIn: string, messageName: string, messageIn: any, receiving: boolean) {
+ let prefix = prefixIn;
+ let message = messageIn;
+ if (!loggingEnabled) {
+ return;
}
- }
-
- export function GetScreenTransform(ele?: HTMLElement | null): { scale: number; translateX: number; translateY: number } {
- if (!ele) {
- return { scale: 1, translateX: 1, translateY: 1 };
+ message = message || {};
+ if (logFilter !== undefined && logFilter !== message.type) {
+ return;
}
- const rect = ele.getBoundingClientRect();
- const scale = ele.offsetWidth === 0 && rect.width === 0 ? 1 : rect.width / ele.offsetWidth;
- const translateX = rect.left;
- const translateY = rect.top;
+ const idString = (message.id || '').padStart(36, ' ');
+ prefix = prefix.padEnd(16, ' ');
+ console.log(`${prefix}: ${idString}, ${receiving ? 'receiving' : 'sending'} ${messageName} with data ${JSON.stringify(message)} `);
+ }
- return { scale, translateX, translateY };
+ export function loggingCallback(prefix: string, func: (args: any) => any, messageName: string) {
+ return (args: any) => {
+ log(prefix, messageName, args, true);
+ func(args);
+ };
}
export function TraceConsoleLog() {
- ['log', 'warn'].forEach(function (method) {
+ ['log', 'warn'].forEach(method => {
const old = (console as any)[method];
(console as any)[method] = function () {
let stack = new Error('').stack?.split(/\n/);
@@ -112,342 +56,97 @@ export namespace Utils {
});
}
- /**
- * A convenience method. Prepends the full path (i.e. http://localhost:) to the
- * requested extension
- * @param extension the specified sub-path to append to the window origin
- */
- export function prepend(extension: string): string {
- return window.location.origin + extension;
- }
- export function fileUrl(filename: string): string {
- return prepend(`/files/${filename}`);
- }
-
- export function shareUrl(documentId: string): string {
- return prepend(`/doc/${documentId}?sharing=true`);
- }
-
- export function CorsProxy(url: string): string {
- return prepend('/corsProxy/') + encodeURIComponent(url);
- }
-
- export function CopyText(text: string) {
- navigator.clipboard.writeText(text);
- }
-
- export function decimalToHexString(number: number) {
- if (number < 0) {
- number = 0xffffffff + number + 1;
- }
- return (number < 16 ? '0' : '') + number.toString(16).toUpperCase();
- }
-
- export function colorString(color: ColorResult) {
- return color.hex.startsWith('#') && color.hex.length < 8 ? color.hex + (color.rgb.a ? decimalToHexString(Math.round(color.rgb.a * 255)) : 'ff') : color.hex;
- }
-
- export function fromRGBAstr(rgba: string) {
- const rm = rgba.match(/rgb[a]?\(([ 0-9]+)/);
- const r = rm ? Number(rm[1]) : 0;
- const gm = rgba.match(/rgb[a]?\([ 0-9]+,([ 0-9]+)/);
- const g = gm ? Number(gm[1]) : 0;
- const bm = rgba.match(/rgb[a]?\([ 0-9]+,[ 0-9]+,([ 0-9]+)/);
- const b = bm ? Number(bm[1]) : 0;
- const am = rgba.match(/rgba?\([ 0-9]+,[ 0-9]+,[ 0-9]+,([ .0-9]+)/);
- const a = am ? Number(am[1]) : 1;
- return { r: r, g: g, b: b, a: a };
- }
-
- const isTransparentFunctionHack = 'isTransparent(__value__)';
- export const noRecursionHack = '__noRecursion';
-
- // special case filters
- export const noDragDocsFilter = 'noDragDocs::any::check';
- export const TransparentBackgroundFilter = `backgroundColor::${isTransparentFunctionHack},${noRecursionHack}::check`; // bcz: hack. noRecursion should probably be either another ':' delimited field, or it should be a modifier to the comparision (eg., check, x, etc) field
- export const OpaqueBackgroundFilter = `backgroundColor::${isTransparentFunctionHack},${noRecursionHack}::x`; // bcz: hack. noRecursion should probably be either another ':' delimited field, or it should be a modifier to the comparision (eg., check, x, etc) field
-
- export function IsRecursiveFilter(val: string) {
- return !val.includes(noRecursionHack);
- }
- export function HasFunctionFilter(val: string) {
- if (val.includes(isTransparentFunctionHack)) return (color: string) => color !== '' && DashColor(color).alpha() !== 1;
- // add other function filters here...
- return undefined;
- }
-
- export function toRGBAstr(col: { r: number; g: number; b: number; a?: number }) {
- return 'rgba(' + col.r + ',' + col.g + ',' + col.b + (col.a !== undefined ? ',' + col.a : '') + ')';
- }
-
- export function HSLtoRGB(h: number, s: number, l: number) {
- // Must be fractions of 1
- // s /= 100;
- // l /= 100;
-
- const c = (1 - Math.abs(2 * l - 1)) * s,
- x = c * (1 - Math.abs(((h / 60) % 2) - 1)),
- m = l - c / 2;
- let r = 0,
- g = 0,
- b = 0;
- if (0 <= h && h < 60) {
- r = c;
- g = x;
- b = 0;
- } else if (60 <= h && h < 120) {
- r = x;
- g = c;
- b = 0;
- } else if (120 <= h && h < 180) {
- r = 0;
- g = c;
- b = x;
- } else if (180 <= h && h < 240) {
- r = 0;
- g = x;
- b = c;
- } else if (240 <= h && h < 300) {
- r = x;
- g = 0;
- b = c;
- } else if (300 <= h && h < 360) {
- r = c;
- g = 0;
- b = x;
- }
- r = Math.round((r + m) * 255);
- g = Math.round((g + m) * 255);
- b = Math.round((b + m) * 255);
- return { r: r, g: g, b: b };
- }
-
- export function RGBToHSL(r: number, g: number, b: number) {
- // Make r, g, and b fractions of 1
- r /= 255;
- g /= 255;
- b /= 255;
-
- // Find greatest and smallest channel values
- const cmin = Math.min(r, g, b),
- cmax = Math.max(r, g, b),
- delta = cmax - cmin;
- let h = 0,
- s = 0,
- l = 0;
- // Calculate hue
-
- // No difference
- if (delta === 0) h = 0;
- // Red is max
- else if (cmax === r) h = ((g - b) / delta) % 6;
- // Green is max
- else if (cmax === g) h = (b - r) / delta + 2;
- // Blue is max
- else h = (r - g) / delta + 4;
-
- h = Math.round(h * 60);
-
- // Make negative hues positive behind 360°
- if (h < 0) h += 360; // Calculate lightness
-
- l = (cmax + cmin) / 2;
-
- // Calculate saturation
- s = delta === 0 ? 0 : delta / (1 - Math.abs(2 * l - 1));
-
- // Multiply l and s by 100
- // s = +(s * 100).toFixed(1);
- // l = +(l * 100).toFixed(1);
-
- return { h: h, s: s, l: l };
- }
-
- export function scrollIntoView(targetY: number, targetHgt: number, scrollTop: number, contextHgt: number, minSpacing: number, scrollHeight: number) {
- if (!targetHgt) return targetY; // if there's no height, then assume that
- if (scrollTop + contextHgt < Math.min(scrollHeight, targetY + minSpacing + targetHgt)) {
- return Math.ceil(targetY + minSpacing + targetHgt - contextHgt);
- }
- if (scrollTop >= Math.max(0, targetY - minSpacing)) {
- return Math.max(0, Math.floor(targetY - minSpacing));
- }
- }
-
- export function clamp(n: number, lower: number, upper: number) {
- return Math.max(lower, Math.min(upper, n));
- }
-
- export function distanceBetweenHorizontalLines(xs: number, xe: number, y: number, xs2: number, xe2: number, y2: number): [number, number[]] {
- if ((xs2 <= xs && xe2 >= xs) || (xs2 <= xe && xe2 >= xe) || (xs2 >= xs && xe2 <= xe)) return [Math.abs(y - y2), [Math.max(xs, xs2), y, Math.min(xe, xe2), y]];
- if (xe2 <= xs) return [Math.sqrt((xe2 - xs) * (xe2 - xs) + (y2 - y) * (y2 - y)), [xs, y, xs, y]];
- //if (xs2 > xe)
- return [Math.sqrt((xs2 - xe) * (xs2 - xe) + (y2 - y) * (y2 - y)), [xe, y, xe, y]];
- }
- export function distanceBetweenVerticalLines(x: number, ys: number, ye: number, x2: number, ys2: number, ye2: number): [number, number[]] {
- if ((ys2 <= ys && ye2 >= ys) || (ys2 <= ye && ye2 >= ye) || (ys2 >= ys && ye2 <= ye)) return [Math.abs(x - x2), [x, Math.max(ys, ys2), x, Math.min(ye, ye2)]];
- if (ye2 <= ys) return [Math.sqrt((ye2 - ys) * (ye2 - ys) + (x2 - x) * (x2 - x)), [x, ys, x, ys]];
- //if (ys2 > ye)
- return [Math.sqrt((ys2 - ye) * (ys2 - ye) + (x2 - x) * (x2 - x)), [x, ye, x, ye]];
- }
-
export function rotPt(x: number, y: number, radAng: number) {
return { x: x * Math.cos(radAng) - y * Math.sin(radAng), y: x * Math.sin(radAng) + y * Math.cos(radAng) };
}
- function project(px: number, py: number, ax: number, ay: number, bx: number, by: number) {
- if (ax === bx && ay === by) return { point: { x: ax, y: ay }, left: false, dot: 0, t: 0 };
- const atob = { x: bx - ax, y: by - ay };
- const atop = { x: px - ax, y: py - ay };
- const len = atob.x * atob.x + atob.y * atob.y;
- var dot = atop.x * atob.x + atop.y * atob.y;
- const t = Math.min(1, Math.max(0, dot / len));
-
- dot = (bx - ax) * (py - ay) - (by - ay) * (px - ax);
-
- return {
- point: {
- x: ax + atob.x * t,
- y: ay + atob.y * t,
- },
- left: dot < 1,
- dot: dot,
- t: t,
- };
- }
-
- export function closestPtBetweenRectangles(l: number, t: number, w: number, h: number, l1: number, t1: number, w1: number, h1: number, x: number, y: number) {
- const r = l + w,
- b = t + h;
- const r1 = l1 + w1,
- b1 = t1 + h1;
- const hsegs = [
- [l, r, t, l1, r1, t1],
- [l, r, b, l1, r1, t1],
- [l, r, t, l1, r1, b1],
- [l, r, b, l1, r1, b1],
- ];
- const vsegs = [
- [l, t, b, l1, t1, b1],
- [r, t, b, l1, t1, b1],
- [l, t, b, r1, t1, b1],
- [r, t, b, r1, t1, b1],
- ];
- const res = hsegs.reduce(
- (closest, seg) => {
- const res = distanceBetweenHorizontalLines(seg[0], seg[1], seg[2], seg[3], seg[4], seg[5]);
- return res[0] < closest[0] ? res : closest;
- },
- [Number.MAX_VALUE, []] as [number, number[]]
- );
- const fres = vsegs.reduce((closest, seg) => {
- const res = distanceBetweenVerticalLines(seg[0], seg[1], seg[2], seg[3], seg[4], seg[5]);
- return res[0] < closest[0] ? res : closest;
- }, res);
+ export function getNearestPointInPerimeter(l: number, t: number, w: number, h: number, xIn: number, yIn: number) {
+ const r = l + w;
+ const b = t + h;
- const near = project(x, y, fres[1][0], fres[1][1], fres[1][2], fres[1][3]);
- return project(near.point.x, near.point.y, fres[1][0], fres[1][1], fres[1][2], fres[1][3]);
- }
+ const x = clamp(xIn, l, r);
+ const y = clamp(yIn, t, b);
- export function getNearestPointInPerimeter(l: number, t: number, w: number, h: number, x: number, y: number) {
- const r = l + w,
- b = t + h;
-
- (x = clamp(x, l, r)), (y = clamp(y, t, b));
-
- const dl = Math.abs(x - l),
- dr = Math.abs(x - r),
- dt = Math.abs(y - t),
- db = Math.abs(y - b);
+ const dl = Math.abs(x - l);
+ const dr = Math.abs(x - r);
+ const dt = Math.abs(y - t);
+ const db = Math.abs(y - b);
const m = Math.min(dl, dr, dt, db);
return m === dt ? [x, t] : m === db ? [x, b] : m === dl ? [l, y] : [r, y];
}
+}
+export function decimalToHexString(numberIn: number) {
+ const number = numberIn < 0 ? 0xffffffff + numberIn + 1 : numberIn;
+ return (number < 16 ? '0' : '') + number.toString(16).toUpperCase();
+}
- export function GetClipboardText(): string {
- const textArea = document.createElement('textarea');
- document.body.appendChild(textArea);
- textArea.focus();
- textArea.select();
-
- try {
- document.execCommand('paste');
- } catch (err) {}
-
- const val = textArea.value;
- document.body.removeChild(textArea);
- return val;
- }
-
- export const loggingEnabled: Boolean = false;
- export const logFilter: number | undefined = undefined;
-
- export function log(prefix: string, messageName: string, message: any, receiving: boolean) {
- if (!loggingEnabled) {
- return;
- }
- message = message || {};
- if (logFilter !== undefined && logFilter !== message.type) {
- return;
- }
- const idString = (message.id || '').padStart(36, ' ');
- prefix = prefix.padEnd(16, ' ');
- console.log(`${prefix}: ${idString}, ${receiving ? 'receiving' : 'sending'} ${messageName} with data ${JSON.stringify(message)} `);
- }
-
- export function loggingCallback(prefix: string, func: (args: any) => any, messageName: string) {
- return (args: any) => {
- log(prefix, messageName, args, true);
- func(args);
- };
- }
+export function distanceBetweenHorizontalLines(xs: number, xe: number, y: number, xs2: number, xe2: number, y2: number): [number, number[]] {
+ if ((xs2 <= xs && xe2 >= xs) || (xs2 <= xe && xe2 >= xe) || (xs2 >= xs && xe2 <= xe)) return [Math.abs(y - y2), [Math.max(xs, xs2), y, Math.min(xe, xe2), y]];
+ if (xe2 <= xs) return [Math.sqrt((xe2 - xs) * (xe2 - xs) + (y2 - y) * (y2 - y)), [xs, y, xs, y]];
+ // if (xs2 > xe)
+ return [Math.sqrt((xs2 - xe) * (xs2 - xe) + (y2 - y) * (y2 - y)), [xe, y, xe, y]];
+}
+export function distanceBetweenVerticalLines(x: number, ys: number, ye: number, x2: number, ys2: number, ye2: number): [number, number[]] {
+ if ((ys2 <= ys && ye2 >= ys) || (ys2 <= ye && ye2 >= ye) || (ys2 >= ys && ye2 <= ye)) return [Math.abs(x - x2), [x, Math.max(ys, ys2), x, Math.min(ye, ye2)]];
+ if (ye2 <= ys) return [Math.sqrt((ye2 - ys) * (ye2 - ys) + (x2 - x) * (x2 - x)), [x, ys, x, ys]];
+ // if (ys2 > ye)
+ return [Math.sqrt((ys2 - ye) * (ys2 - ye) + (x2 - x) * (x2 - x)), [x, ye, x, ye]];
+}
- export function Emit(socket: Socket, message: Message, args: T) {
- log('Emit', message.Name, args, false);
- socket.emit(message.Message, args);
- }
+function project(px: number, py: number, ax: number, ay: number, bx: number, by: number) {
+ if (ax === bx && ay === by) return { point: { x: ax, y: ay }, left: false, dot: 0, t: 0 };
+ const atob = { x: bx - ax, y: by - ay };
+ const atop = { x: px - ax, y: py - ay };
+ const len = atob.x * atob.x + atob.y * atob.y;
+ let dot = atop.x * atob.x + atop.y * atob.y;
+ const t = Math.min(1, Math.max(0, dot / len));
- export function AddServerHandler(socket: Socket, message: Message, handler: (args: T) => any) {
- socket.on(message.Message, loggingCallback('Incoming', handler, message.Name));
- }
+ dot = (bx - ax) * (py - ay) - (by - ay) * (px - ax);
- export function AddServerHandlerCallback(socket: Socket, message: Message, handler: (args: [T, (res: any) => any]) => any) {
- socket.on(message.Message, (arg: T, fn: (res: any) => any) => {
- log('S receiving', message.Name, arg, true);
- handler([arg, loggingCallback('S sending', fn, message.Name)]);
- });
- }
- export type RoomHandler = (socket: Socket, room: string) => any;
- export type UsedSockets = Socket;
- export type RoomMessage = 'create or join' | 'created' | 'joined';
- export function AddRoomHandler(socket: Socket, message: RoomMessage, handler: RoomHandler) {
- socket.on(message, (room: any) => handler(socket, room));
- }
+ return {
+ point: {
+ x: ax + atob.x * t,
+ y: ay + atob.y * t,
+ },
+ left: dot < 1,
+ dot: dot,
+ t: t,
+ };
}
-export function OmitKeys(obj: any, keys: string[], pattern?: string, addKeyFunc?: (dup: any) => void): { omit: any; extract: any } {
- const omit: any = { ...obj };
- const extract: any = {};
- keys.forEach(key => {
- extract[key] = omit[key];
- delete omit[key];
- });
- pattern &&
- Array.from(Object.keys(omit))
- .filter(key => key.match(pattern))
- .forEach(key => {
- extract[key] = omit[key];
- delete omit[key];
- });
- addKeyFunc?.(omit);
- return { omit, extract };
-}
+export function closestPtBetweenRectangles(l: number, t: number, w: number, h: number, l1: number, t1: number, w1: number, h1: number, x: number, y: number) {
+ const r = l + w;
+ const b = t + h;
+ const r1 = l1 + w1;
+ const b1 = t1 + h1;
+ const hsegs = [
+ [l, r, t, l1, r1, t1],
+ [l, r, b, l1, r1, t1],
+ [l, r, t, l1, r1, b1],
+ [l, r, b, l1, r1, b1],
+ ];
+ const vsegs = [
+ [l, t, b, l1, t1, b1],
+ [r, t, b, l1, t1, b1],
+ [l, t, b, r1, t1, b1],
+ [r, t, b, r1, t1, b1],
+ ];
+ const res = hsegs.reduce(
+ (closest, seg) => {
+ const res = distanceBetweenHorizontalLines(seg[0], seg[1], seg[2], seg[3], seg[4], seg[5]);
+ return res[0] < closest[0] ? res : closest;
+ },
+ [Number.MAX_VALUE, []] as [number, number[]]
+ );
+ const fres = vsegs.reduce((closest, seg) => {
+ const res = distanceBetweenVerticalLines(seg[0], seg[1], seg[2], seg[3], seg[4], seg[5]);
+ return res[0] < closest[0] ? res : closest;
+ }, res);
-export function WithKeys(obj: any, keys: string[], addKeyFunc?: (dup: any) => void) {
- const dup: any = {};
- keys.forEach(key => (dup[key] = obj[key]));
- addKeyFunc && addKeyFunc(dup);
- return dup;
+ const near = project(x, y, fres[1][0], fres[1][1], fres[1][2], fres[1][3]);
+ return project(near.point.x, near.point.y, fres[1][0], fres[1][1], fres[1][2], fres[1][3]);
}
export function timenow() {
@@ -455,7 +154,6 @@ export function timenow() {
let ampm = 'am';
let h = now.getHours();
let m: any = now.getMinutes();
- const s: any = now.getSeconds();
if (h >= 12) {
if (h > 12) h -= 12;
ampm = 'pm';
@@ -464,14 +162,8 @@ export function timenow() {
return now.toLocaleDateString() + ' ' + h + ':' + m + ' ' + ampm;
}
-export function incrementTitleCopy(title: string) {
- const numstr = title.match(/.*(\{([0-9]*)\})+/);
- const copyNumStr = `{${1 + (numstr ? +numstr[2] : 0)}}`;
- return (numstr ? title.replace(numstr[1], '') : title) + copyNumStr;
-}
-
-export function formatTime(time: number) {
- time = Math.round(time);
+export function formatTime(timeIn: number) {
+ const time = Math.round(timeIn);
const hours = Math.floor(time / 60 / 60);
const minutes = Math.floor(time / 60) - hours * 60;
const seconds = time % 60;
@@ -503,93 +195,31 @@ export function intersectRect(r1: { left: number; top: number; width: number; he
}
export function stringHash(s?: string) {
+ // eslint-disable-next-line no-bitwise
return !s ? undefined : Math.abs(s.split('').reduce((a: any, b: any) => (a => a & a)((a << 5) - a + b.charCodeAt(0)), 0));
}
export function percent2frac(percent: string) {
return Number(percent.substr(0, percent.length - 1)) / 100;
}
+export type Without = Pick>;
+
+export type Predicate = (entry: [K, V]) => boolean;
export function numberRange(num: number) {
return num > 0 && num < 1000 ? Array.from(Array(num)).map((v, i) => i) : [];
}
-export function returnTransparent() {
- return 'transparent';
-}
-
-export function returnTrue() {
- return true;
-}
-
-export function returnIgnore(): 'ignore' {
- return 'ignore';
-}
-export function returnAlways(): 'always' {
- return 'always';
-}
-export function returnNever(): 'never' {
- return 'never';
-}
-
-export function returnDefault(): 'default' {
- return 'default';
-}
-
-export function return18() {
- return 18;
-}
-
-export function returnFalse() {
- return false;
-}
-
-export function returnAll(): 'all' {
- return 'all';
-}
-
-export function returnNone(): 'none' {
- return 'none';
-}
-
-export function returnVal(val1?: number, val2?: number) {
- return val1 ? val1 : val2 !== undefined ? val2 : 0;
-}
-
-export function returnOne() {
- return 1;
-}
-
-export function returnZero() {
- return 0;
-}
-
-export function returnEmptyString() {
- return '';
-}
-
-export function returnEmptyFilter() {
- return [] as string[];
-}
-
-export function returnEmptyDoclist() {
- return [] as any[];
-}
-
-export let emptyPath: DocumentView[] = [];
-
export function emptyFunction() {
return undefined;
}
+export const emptyPath: any[] = [];
+
export function unimplementedFunction() {
throw new Error('This function is not implemented, but should be.');
}
-export type Without = Pick>;
-
-export type Predicate = (entry: [K, V]) => boolean;
-
export function DeepCopy(source: Map, predicate?: Predicate) {
const deepCopy = new Map();
const entries = source.entries();
@@ -616,197 +246,6 @@ export namespace JSONUtils {
}
}
-const easeFunc = (transition: 'ease' | 'linear' | undefined, currentTime: number, start: number, change: number, duration: number) => {
- if (transition === 'linear') {
- let newCurrentTime = currentTime / duration; // currentTime / (duration / 2);
- return start + newCurrentTime * change;
- }
-
- let newCurrentTime = currentTime / (duration / 2);
- if (newCurrentTime < 1) {
- return (change / 2) * newCurrentTime * newCurrentTime + start;
- }
-
- newCurrentTime -= 1;
- return (-change / 2) * (newCurrentTime * (newCurrentTime - 2) - 1) + start;
-};
-
-export function smoothScroll(duration: number, element: HTMLElement | HTMLElement[], to: number, transition: 'ease' | 'linear' | undefined, stopper?: () => void) {
- stopper?.();
- const elements = element instanceof HTMLElement ? [element] : element;
- const starts = elements.map(element => element.scrollTop);
- const startDate = new Date().getTime();
- let _stop = false;
- const stop = () => (_stop = true);
- const animateScroll = () => {
- const currentDate = new Date().getTime();
- const currentTime = currentDate - startDate;
- const setScrollTop = (element: HTMLElement, value: number) => (element.scrollTop = value);
- if (!_stop) {
- if (currentTime < duration) {
- elements.forEach((element, i) => currentTime && setScrollTop(element, easeFunc(transition, Math.min(currentTime, duration), starts[i], to - starts[i], duration)));
- requestAnimationFrame(animateScroll);
- } else {
- elements.forEach(element => setScrollTop(element, to));
- }
- }
- };
- animateScroll();
- return stop;
-}
-
-export function smoothScrollHorizontal(duration: number, element: HTMLElement | HTMLElement[], to: number) {
- const elements = element instanceof HTMLElement ? [element] : element;
- const starts = elements.map(element => element.scrollLeft);
- const startDate = new Date().getTime();
-
- const animateScroll = () => {
- const currentDate = new Date().getTime();
- const currentTime = currentDate - startDate;
- elements.map((element, i) => (element.scrollLeft = easeFunc('ease', currentTime, starts[i], to - starts[i], duration)));
-
- if (currentTime < duration) {
- requestAnimationFrame(animateScroll);
- } else {
- elements.forEach(element => (element.scrollLeft = to));
- }
- };
- animateScroll();
-}
-
-export function addStyleSheet(styleType: string = 'text/css') {
- const style = document.createElement('style');
- style.type = styleType;
- const sheets = document.head.appendChild(style);
- return (sheets as any).sheet;
-}
-export function addStyleSheetRule(sheet: any, selector: any, css: any, selectorPrefix = '.') {
- const propText =
- typeof css === 'string'
- ? css
- : Object.keys(css)
- .map(p => p + ':' + (p === 'content' ? "'" + css[p] + "'" : css[p]))
- .join(';');
- return sheet.insertRule(selectorPrefix + selector + '{' + propText + '}', sheet.cssRules.length);
-}
-export function removeStyleSheetRule(sheet: any, rule: number) {
- if (sheet.rules.length) {
- sheet.removeRule(rule);
- return true;
- }
- return false;
-}
-export function clearStyleSheetRules(sheet: any) {
- if (sheet.rules.length) {
- numberRange(sheet.rules.length).map(n => sheet.removeRule(0));
- return true;
- }
- return false;
-}
-
-export function simulateMouseClick(element: Element | null | undefined, x: number, y: number, sx: number, sy: number, rightClick = true) {
- if (!element) return;
- ['pointerdown', 'pointerup'].map(event => {
- const me = new PointerEvent(event, {
- view: window,
- bubbles: true,
- cancelable: true,
- button: 2,
- pointerType: 'mouse',
- clientX: x,
- clientY: y,
- screenX: sx,
- screenY: sy,
- });
- (me as any).dash = true;
- element.dispatchEvent(me);
- });
-
- if (rightClick) {
- const me = new MouseEvent('contextmenu', {
- view: window,
- bubbles: true,
- cancelable: true,
- button: 2,
- clientX: x,
- clientY: y,
- movementX: 0,
- movementY: 0,
- screenX: sx,
- screenY: sy,
- });
- (me as any).dash = true;
- element.dispatchEvent(me);
- }
-}
-
-export function DashColor(color: string) {
- try {
- return color ? Color(color.toLowerCase()) : Color('transparent');
- } catch (e) {
- if (color.includes('gradient')) console.log("using color 'white' in place of :" + color);
- else console.log('COLOR error:', e);
- return Color('white');
- }
-}
-
-export function lightOrDark(color: any) {
- if (color === 'transparent' || !color) return Colors.BLACK;
- if (color.startsWith?.('linear')) return Colors.BLACK;
- if (DashColor(color).isLight()) return Colors.BLACK;
- return Colors.WHITE;
-}
-
-export function getWordAtPoint(elem: any, x: number, y: number): string | undefined {
- if (elem.tagName === 'INPUT') return 'input';
- if (elem.tagName === 'TEXTAREA') return 'textarea';
- if (elem.nodeType === elem.TEXT_NODE) {
- const range = elem.ownerDocument.createRange();
- range.selectNodeContents(elem);
- var currentPos = 0;
- const endPos = range.endOffset;
- while (currentPos + 1 <= endPos) {
- range.setStart(elem, currentPos);
- range.setEnd(elem, currentPos + 1);
- const rangeRect = range.getBoundingClientRect();
- if (rangeRect.left <= x && rangeRect.right >= x && rangeRect.top <= y && rangeRect.bottom >= y) {
- range.expand?.('word'); // doesn't exist in firefox
- const ret = range.toString();
- range.detach();
- return ret;
- }
- currentPos += 1;
- }
- } else {
- for (const childNode of elem.childNodes) {
- const range = childNode.ownerDocument.createRange();
- range.selectNodeContents(childNode);
- const rangeRect = range.getBoundingClientRect();
- if (rangeRect.left <= x && rangeRect.right >= x && rangeRect.top <= y && rangeRect.bottom >= y) {
- range.detach();
- const word = getWordAtPoint(childNode, x, y);
- if (word) return word;
- } else {
- range.detach();
- }
- }
- }
- return undefined;
-}
-
-export function isTargetChildOf(ele: HTMLDivElement | null, target: Element | null) {
- let entered = false;
- for (let child = target; !entered && child; child = child.parentElement) {
- entered = child === ele;
- }
- return entered;
-}
-
-export function StopEvent(e: React.PointerEvent | React.MouseEvent) {
- e.stopPropagation();
- e.preventDefault();
-}
-
/**
* Helper method for converting pixel string eg. '32px' into number eg. 32
* @param value: string with 'px' ending
@@ -816,100 +255,10 @@ export function StopEvent(e: React.PointerEvent | React.MouseEvent) {
* '32px' -> 32
*/
export function numberValue(value: string | undefined): number {
- if (value == undefined) return 0;
+ if (value === undefined) return 0;
return parseInt(value);
}
export function numbersAlmostEqual(num1: number, num2: number) {
return Math.abs(num1 - num2) < 0.2;
}
-
-export function setupMoveUpEvents(
- target: object,
- e: React.PointerEvent,
- moveEvent: (e: PointerEvent, down: number[], delta: number[]) => boolean,
- upEvent: (e: PointerEvent, movement: number[], isClick: boolean) => any,
- clickEvent: (e: PointerEvent, doubleTap?: boolean) => any,
- stopPropagation: boolean = true,
- stopMovePropagation: boolean = true,
- noDoubleTapTimeout?: () => void
-) {
- const doubleTapTimeout = 300;
- (target as any)._doubleTap = Date.now() - (target as any)._lastTap < doubleTapTimeout;
- (target as any)._lastTap = Date.now();
- (target as any)._downX = (target as any)._lastX = e.clientX;
- (target as any)._downY = (target as any)._lastY = e.clientY;
- (target as any)._noClick = false;
- var moving = false;
-
- const _moveEvent = (e: PointerEvent): void => {
- if (moving || Math.abs(e.clientX - (target as any)._downX) > Utils.DRAG_THRESHOLD || Math.abs(e.clientY - (target as any)._downY) > Utils.DRAG_THRESHOLD) {
- moving = true;
- if ((target as any)._doubleTime) {
- clearTimeout((target as any)._doubleTime);
- (target as any)._doubleTime = undefined;
- }
- if (moveEvent(e, [(target as any)._downX, (target as any)._downY], [e.clientX - (target as any)._lastX, e.clientY - (target as any)._lastY])) {
- document.removeEventListener('pointermove', _moveEvent);
- document.removeEventListener('pointerup', _upEvent);
- }
- }
- (target as any)._lastX = e.clientX;
- (target as any)._lastY = e.clientY;
- stopMovePropagation && e.stopPropagation();
- };
- const _upEvent = (e: PointerEvent): void => {
- const isClick = Math.abs(e.clientX - (target as any)._downX) < 4 && Math.abs(e.clientY - (target as any)._downY) < 4;
- upEvent(e, [e.clientX - (target as any)._downX, e.clientY - (target as any)._downY], isClick);
- if (isClick) {
- if (!(target as any)._doubleTime && noDoubleTapTimeout) {
- (target as any)._doubleTime = setTimeout(() => {
- noDoubleTapTimeout?.();
- (target as any)._doubleTime = undefined;
- }, doubleTapTimeout);
- }
- if ((target as any)._doubleTime && (target as any)._doubleTap) {
- clearTimeout((target as any)._doubleTime);
- (target as any)._doubleTime = undefined;
- }
- (target as any)._noClick = clickEvent(e, (target as any)._doubleTap);
- }
- document.removeEventListener('pointermove', _moveEvent);
- document.removeEventListener('pointerup', _upEvent, true);
- };
- const _clickEvent = (e: MouseEvent): void => {
- if ((target as any)._noClick) e.stopPropagation();
- document.removeEventListener('click', _clickEvent, true);
- };
- if (stopPropagation) {
- e.stopPropagation();
- e.preventDefault();
- }
- document.addEventListener('pointermove', _moveEvent);
- document.addEventListener('pointerup', _upEvent, true);
- document.addEventListener('click', _clickEvent, true);
-}
-
-export function DivHeight(ele: HTMLElement): number {
- return Number(getComputedStyle(ele).height.replace('px', ''));
-}
-export function DivWidth(ele: HTMLElement): number {
- return Number(getComputedStyle(ele).width.replace('px', ''));
-}
-
-export function dateRangeStrToDates(dateStr: string) {
- // dateStr in yyyy-mm-dd format
- const dateRangeParts = dateStr.split('|'); // splits into from and to date
- const fromParts = dateRangeParts[0].split('-');
- const toParts = dateRangeParts[1].split('-');
-
- const fromYear = parseInt(fromParts[0]);
- const fromMonth = parseInt(fromParts[1]) - 1;
- const fromDay = parseInt(fromParts[2]);
-
- const toYear = parseInt(toParts[0]);
- const toMonth = parseInt(toParts[1]) - 1;
- const toDay = parseInt(toParts[2]);
-
- return [new Date(fromYear, fromMonth, fromDay), new Date(toYear, toMonth, toDay)];
-}
diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts
index 321572071..804e6d1d7 100644
--- a/src/client/DocServer.ts
+++ b/src/client/DocServer.ts
@@ -1,20 +1,15 @@
import { runInAction } from 'mobx';
-import * as rp from 'request-promise';
-import { Doc, DocListCast, Opt } from '../fields/Doc';
+import { Socket, io } from 'socket.io-client';
+import { ClientUtils } from '../ClientUtils';
+import { Utils, emptyFunction } from '../Utils';
+import { Doc, Opt } from '../fields/Doc';
import { UpdatingFromServer } from '../fields/DocSymbols';
import { FieldLoader } from '../fields/FieldLoader';
import { HandleUpdate, Id, Parent } from '../fields/FieldSymbols';
-import { ObjectField } from '../fields/ObjectField';
+import { ObjectField, SetObjGetRefField, SetObjGetRefFields } from '../fields/ObjectField';
import { RefField } from '../fields/RefField';
-import { DocCast, StrCast } from '../fields/Types';
-//import MobileInkOverlay from '../mobile/MobileInkOverlay';
-import { io, Socket } from 'socket.io-client';
-import { emptyFunction, Utils } from '../Utils';
import { GestureContent, Message, MessageStore, MobileDocumentUploadContent, MobileInkOverlayContent, UpdateMobileInkOverlayPositionContent, YoutubeQueryTypes } from './../server/Message';
-import { DocumentType } from './documents/DocumentTypes';
-import { LinkManager } from './util/LinkManager';
import { SerializationHelper } from './util/SerializationHelper';
-//import { GestureOverlay } from './views/GestureOverlay';
/**
* This class encapsulates the transfer and cross-client synchronization of
@@ -30,23 +25,32 @@ import { SerializationHelper } from './util/SerializationHelper';
* or update ourselves based on the server's update message, that occurs here
*/
export namespace DocServer {
- let _cache: { [id: string]: RefField | Promise> } = {};
+ // eslint-disable-next-line import/no-mutable-exports
+ export let _cache: { [id: string]: RefField | Promise> } = {};
+
+ function errorFunc(): never {
+ throw new Error("Can't use DocServer without calling init first");
+ }
+ let _UpdateField: (id: string, diff: any) => void = errorFunc;
+ let _CreateField: (field: RefField) => void = errorFunc;
export function AddServerHandler(socket: Socket, message: Message, handler: (args: T) => any) {
socket.on(message.Message, Utils.loggingCallback('Incoming', handler, message.Name));
}
export function Emit(socket: Socket, message: Message, args: T) {
- //log('Emit', message.Name, args, false);
+ // log('Emit', message.Name, args, false);
socket.emit(message.Message, args);
}
export function EmitCallback(socket: Socket, message: Message, args: T): Promise;
export function EmitCallback(socket: Socket, message: Message, args: T, fn: (args: any) => any): void;
export function EmitCallback(socket: Socket, message: Message, args: T, fn?: (args: any) => any): void | Promise {
- //log('Emit', message.Name, args, false);
+ // log('Emit', message.Name, args, false);
if (fn) {
socket.emit(message.Message, args, Utils.loggingCallback('Receiving', fn, message.Name));
} else {
- return new Promise(res => socket.emit(message.Message, args, Utils.loggingCallback('Receiving', res, message.Name)));
+ return new Promise(res => {
+ socket.emit(message.Message, args, Utils.loggingCallback('Receiving', res, message.Name));
+ });
}
}
@@ -59,57 +63,21 @@ export namespace DocServer {
return foundDocId ? (_cache[foundDocId] as Doc) : undefined;
}
- let cacheDocumentIds = ''; // ; separate string of all documents ids in the user's working set (cached on the server)
- export let CacheNeedsUpdate = false;
- export function UPDATE_SERVER_CACHE() {
- const prototypes = Object.values(DocumentType)
- .filter(type => type !== DocumentType.NONE)
- .map(type => _cache[type + 'Proto'])
- .filter(doc => doc instanceof Doc)
- .map(doc => doc as Doc);
- const references = new Set(prototypes);
- Doc.FindReferences(Doc.UserDoc(), references, undefined);
- DocListCast(DocCast(Doc.UserDoc().myLinkDatabase).data).forEach(link => {
- if (!references.has(DocCast(link.link_anchor_1)) && !references.has(DocCast(link.link_anchor_2))) {
- Doc.RemoveDocFromList(DocCast(Doc.UserDoc().myLinkDatabase), 'data', link);
- Doc.AddDocToList(Doc.MyRecentlyClosed, undefined, link);
- }
- });
- LinkManager.Instance.userLinkDBs.forEach(linkDb => Doc.FindReferences(linkDb, references, undefined));
- const filtered = Array.from(references);
-
- const newCacheUpdate = filtered.map(doc => doc[Id]).join(';');
- if (newCacheUpdate === cacheDocumentIds) return;
- cacheDocumentIds = newCacheUpdate;
-
- // print out cached docs
- Doc.MyDockedBtns.linearView_IsOpen && console.log('Set cached docs = ');
- const is_filtered = filtered.filter(doc => !Doc.IsSystem(doc));
- const strings = is_filtered.map(doc => StrCast(doc.title) + ' ' + (Doc.IsDataProto(doc) ? '(data)' : '(embedding)'));
- Doc.MyDockedBtns.linearView_IsOpen && strings.sort().forEach((str, i) => console.log(i.toString() + ' ' + str));
-
- rp.post(Utils.prepend('/setCacheDocumentIds'), {
- body: {
- cacheDocumentIds,
- },
- json: true,
- });
- }
export let _socket: Socket;
// this client's distinct GUID created at initialization
let USER_ID: string;
// indicates whether or not a document is currently being udpated, and, if so, its id
export enum WriteMode {
- Default = 0, //Anything goes
- Playground = 1, //Playground (write own/no read other updates)
- LiveReadonly = 2, //Live Readonly (no write/read others)
- LivePlayground = 3, //Live Playground (write own/read others)
+ Default = 0, // Anything goes
+ Playground = 1, // Playground (write own/no read other updates)
+ LiveReadonly = 2, // Live Readonly (no write/read others)
+ LivePlayground = 3, // Live Playground (write own/read others)
}
const fieldWriteModes: { [field: string]: WriteMode } = {};
const docsWithUpdates: { [field: string]: Set } = {};
- export var PlaygroundFields: string[] = [];
+ export const PlaygroundFields: string[] = [];
export function setLivePlaygroundFields(livePlaygroundFields: string[]) {
DocServer.PlaygroundFields.push(...livePlaygroundFields);
livePlaygroundFields.forEach(f => DocServer.setFieldWriteMode(f, DocServer.WriteMode.LivePlayground));
@@ -134,7 +102,7 @@ export namespace DocServer {
}
export function getFieldWriteMode(field: string) {
- return Doc.CurrentUserEmail === 'guest' ? WriteMode.LivePlayground : fieldWriteModes[field] || WriteMode.Default;
+ return ClientUtils.CurrentUserEmail === 'guest' ? WriteMode.LivePlayground : fieldWriteModes[field] || WriteMode.Default;
}
export function registerDocWithCachedUpdate(doc: Doc, field: string, oldValue: any) {
@@ -185,56 +153,14 @@ export namespace DocServer {
window.location.reload();
}
- export function init(protocol: string, hostname: string, port: number, identifier: string) {
- _cache = {};
- USER_ID = identifier;
- protocol = protocol.startsWith('https') ? 'wss' : 'ws';
- _socket = io(`${protocol}://${hostname}:${port}`, { transports: ['websocket'], rejectUnauthorized: false });
- _socket.on("connect_error", (err:any) => console.log(err));
- // io.connect(`https://7f079dda.ngrok.io`);// if using ngrok, create a special address for the websocket
-
- _GetCachedRefField = _GetCachedRefFieldImpl;
- _GetRefField = _GetRefFieldImpl;
- _GetRefFields = _GetRefFieldsImpl;
- _CreateField = _CreateFieldImpl;
- _UpdateField = _UpdateFieldImpl;
-
- /**
- * Whenever the server sends us its handshake message on our
- * websocket, we use the above function to return the handshake.
- */
- DocServer.AddServerHandler(_socket, MessageStore.Foo, onConnection);
- DocServer.AddServerHandler(_socket, MessageStore.UpdateField, respondToUpdate);
- DocServer.AddServerHandler(_socket, MessageStore.DeleteField, respondToDelete);
- DocServer.AddServerHandler(_socket, MessageStore.DeleteFields, respondToDelete);
- DocServer.AddServerHandler(_socket, MessageStore.ConnectionTerminated, alertUser);
-
- // // mobile ink overlay socket events to communicate between mobile view and desktop view
- // _socket.addEventListener('receiveGesturePoints', (content: GestureContent) => {
- // // MobileInkOverlay.Instance.drawStroke(content);
- // });
- // _socket.addEventListener('receiveOverlayTrigger', (content: MobileInkOverlayContent) => {
- // //GestureOverlay.Instance.enableMobileInkOverlay(content);
- // // MobileInkOverlay.Instance.initMobileInkOverlay(content);
- // });
- // _socket.addEventListener('receiveUpdateOverlayPosition', (content: UpdateMobileInkOverlayPositionContent) => {
- // // MobileInkOverlay.Instance.updatePosition(content);
- // });
- // _socket.addEventListener('receiveMobileDocumentUpload', (content: MobileDocumentUploadContent) => {
- // // MobileInkOverlay.Instance.uploadDocument(content);
- // });
- }
-
- function errorFunc(): never {
- throw new Error("Can't use DocServer without calling init first");
- }
-
export namespace Control {
let _isReadOnly = false;
export function makeReadOnly() {
if (!_isReadOnly) {
_isReadOnly = true;
- _CreateField = field => (_cache[field[Id]] = field);
+ _CreateField = field => {
+ _cache[field[Id]] = field;
+ };
_UpdateField = emptyFunction;
// _RespondToUpdate = emptyFunction; // bcz: option: don't clear RespondToUpdate to continue to receive updates as others change the DB
}
@@ -313,7 +239,8 @@ export namespace DocServer {
});
cached[UpdatingFromServer] = false;
return cached;
- } else if (field !== undefined) {
+ }
+ if (field !== undefined) {
_cache[id] = field;
} else {
delete _cache[id];
@@ -327,24 +254,25 @@ export namespace DocServer {
// being retrieved and cached
!force && (_cache[id] = deserializeField);
return force ? (cached as any) : deserializeField;
- } else if (cached instanceof Promise) {
+ }
+ if (cached instanceof Promise) {
// BEING RETRIEVED AND CACHED => some other caller previously (likely recently) called GetRefField(s),
// and requested the document I'm looking for. Shouldn't fetch again, just
// return this promise which will resolve to the field itself (see 7)
return cached;
- } else {
- // CACHED => great, let's just return the cached field we have
- return Promise.resolve(cached).then(field => {
- //(field instanceof Doc) && fetchProto(field);
- return field;
- });
}
+ // CACHED => great, let's just return the cached field we have
+ return Promise.resolve(cached).then(
+ field => field
+ // (field instanceof Doc) && fetchProto(field);
+ );
};
const _GetCachedRefFieldImpl = (id: string): Opt => {
const cached = _cache[id];
if (cached !== undefined && !(cached instanceof Promise)) {
return cached;
}
+ return undefined;
};
let _GetRefField: (id: string, force: boolean) => Promise> = errorFunc;
@@ -358,7 +286,7 @@ export namespace DocServer {
}
export async function getYoutubeChannels() {
- return await DocServer.EmitCallback(_socket, MessageStore.YoutubeApiQuery, { type: YoutubeQueryTypes.Channels });
+ return DocServer.EmitCallback(_socket, MessageStore.YoutubeApiQuery, { type: YoutubeQueryTypes.Channels });
}
export function getYoutubeVideos(videoTitle: string, callBack: (videos: any[]) => void) {
@@ -379,21 +307,24 @@ export namespace DocServer {
const requestedIds: string[] = [];
const promises: Promise[] = [];
- let defaultRes: any = undefined;
- const defaultPromise = new Promise(res => (defaultRes = res));
- let defaultPromises: { p: Promise; id: string }[] = [];
+ let defaultRes: any;
+ const defaultPromise = new Promise(res => {
+ defaultRes = res;
+ });
+ const defaultPromises: { p: Promise; id: string }[] = [];
// 1) an initial pass through the cache to determine
// i) which documents need to be fetched
// ii) which are already in the process of being fetched
// iii) which already exist in the cache
+ // eslint-disable-next-line no-restricted-syntax
for (const id of ids.filter(id => id)) {
const cached = _cache[id];
if (cached === undefined) {
defaultPromises.push({
id,
- p: (_cache[id] = new Promise(async res => {
- await defaultPromise;
- res(_cache[id]);
+ // eslint-disable-next-line no-loop-func
+ p: (_cache[id] = new Promise(res => {
+ defaultPromise.then(() => res(_cache[id]));
})),
});
// NOT CACHED => we'll have to send a request to the server
@@ -415,7 +346,11 @@ export namespace DocServer {
// fields for the given ids. This returns a promise, which, when resolved, indicates that all the JSON serialized versions of
// the fields have been returned from the server
console.log('Requesting ' + requestedIds.length);
- setTimeout(() => runInAction(() => (FieldLoader.ServerLoadStatus.requested = requestedIds.length)));
+ setTimeout(() =>
+ runInAction(() => {
+ FieldLoader.ServerLoadStatus.requested = requestedIds.length;
+ })
+ );
const serializedFields = await DocServer.EmitCallback(_socket, MessageStore.GetRefFields, requestedIds);
// 3) when the serialized RefFields have been received, go head and begin deserializing them into objects.
@@ -423,11 +358,17 @@ export namespace DocServer {
// future .proto calls on the Doc won't have to go farther than the cache to get their actual value.
let processed = 0;
console.log('deserializing ' + serializedFields.length + ' fields');
+ // eslint-disable-next-line no-restricted-syntax
for (const field of serializedFields) {
processed++;
if (processed % 150 === 0) {
- runInAction(() => (FieldLoader.ServerLoadStatus.retrieved = processed));
- await new Promise(res => setTimeout(res)); // force loading to yield to splash screen rendering to update progress
+ runInAction(() => {
+ FieldLoader.ServerLoadStatus.retrieved = processed;
+ });
+ // eslint-disable-next-line no-await-in-loop
+ await new Promise(res => {
+ setTimeout(res);
+ }); // force loading to yield to splash screen rendering to update progress
}
const cached = _cache[field.id];
if (!cached || (cached instanceof Promise && defaultPromises.some(dp => dp.p === cached))) {
@@ -435,7 +376,7 @@ export namespace DocServer {
// adds to a list of promises that will be awaited asynchronously
promises.push(
(_cache[field.id] = SerializationHelper.Deserialize(field).then(deserialized => {
- //overwrite or delete any promises (that we inserted as flags
+ // overwrite or delete any promises (that we inserted as flags
// to indicate that the field was in the process of being fetched). Now everything
// should be an actual value within or entirely absent from the cache.
if (deserialized !== undefined) {
@@ -455,7 +396,7 @@ export namespace DocServer {
// get the resolved field
} else if (cached instanceof Promise) {
console.log('.');
- //promises.push(cached);
+ // promises.push(cached);
} else if (field) {
// console.log('-');
}
@@ -472,7 +413,7 @@ export namespace DocServer {
// 6) with this confidence, we can now go through and update the cache at the ids of the fields that
// we explicitly had to fetch. To finish it off, we add whatever value we've come up with for a given
// id to the soon-to-be-returned field mapping.
- //ids.forEach(id => (map[id] = _cache[id] as any));
+ // ids.forEach(id => (map[id] = _cache[id] as any));
// 7) those promises we encountered in the else if of 1), which represent
// other callers having already submitted a request to the server for (a) document(s)
@@ -481,7 +422,7 @@ export namespace DocServer {
//
// fortunately, those other callers will also hit their own version of 6) and clean up
// the shared cache when these promises resolve, so all we have to do is...
- //const otherCallersFetching = await Promise.all(promises);
+ // const otherCallersFetching = await Promise.all(promises);
// ...extract the RefFields returned from the resolution of those promises and add them to our
// own map.
// waitingIds.forEach((id, index) => (map[id] = otherCallersFetching[index]));
@@ -506,6 +447,7 @@ export namespace DocServer {
}
// WRITE A NEW DOCUMENT TO THE SERVER
+ export let CacheNeedsUpdate = false;
/**
* A wrapper around the function local variable _createField.
@@ -521,11 +463,9 @@ export namespace DocServer {
function _CreateFieldImpl(field: RefField) {
_cache[field[Id]] = field;
const initialState = SerializationHelper.Serialize(field);
- Doc.CurrentUserEmail !== 'guest' && DocServer.Emit(_socket, MessageStore.CreateField, initialState);
+ ClientUtils.CurrentUserEmail !== 'guest' && DocServer.Emit(_socket, MessageStore.CreateField, initialState);
}
- let _CreateField: (field: RefField) => void = errorFunc;
-
// NOTIFY THE SERVER OF AN UPDATE TO A DOC'S STATE
/**
@@ -541,13 +481,11 @@ export namespace DocServer {
}
function _UpdateFieldImpl(id: string, diff: any) {
- !DocServer.Control.isReadOnly() && Doc.CurrentUserEmail !== 'guest' && DocServer.Emit(_socket, MessageStore.UpdateField, { id, diff });
+ !DocServer.Control.isReadOnly() && ClientUtils.CurrentUserEmail !== 'guest' && DocServer.Emit(_socket, MessageStore.UpdateField, { id, diff });
}
- let _UpdateField: (id: string, diff: any) => void = errorFunc;
-
function _respondToUpdateImpl(diff: any) {
- const id = diff.id;
+ const { id } = diff;
// to be valid, the Diff object must reference
// a document's id
if (id === undefined) {
@@ -578,11 +516,11 @@ export namespace DocServer {
}
export function DeleteDocument(id: string) {
- Doc.CurrentUserEmail !== 'guest' && DocServer.Emit(_socket, MessageStore.DeleteField, id);
+ ClientUtils.CurrentUserEmail !== 'guest' && DocServer.Emit(_socket, MessageStore.DeleteField, id);
}
export function DeleteDocuments(ids: string[]) {
- Doc.CurrentUserEmail !== 'guest' && DocServer.Emit(_socket, MessageStore.DeleteFields, ids);
+ ClientUtils.CurrentUserEmail !== 'guest' && DocServer.Emit(_socket, MessageStore.DeleteFields, ids);
}
function _respondToDeleteImpl(ids: string | string[]) {
@@ -596,7 +534,7 @@ export namespace DocServer {
}
}
- let _RespondToUpdate = _respondToUpdateImpl;
+ const _RespondToUpdate = _respondToUpdateImpl;
const _respondToDelete = _respondToDeleteImpl;
function respondToUpdate(diff: any) {
@@ -606,4 +544,28 @@ export namespace DocServer {
function respondToDelete(ids: string | string[]) {
_respondToDelete(ids);
}
+
+ export function init(protocol: string, hostname: string, port: number, identifier: string) {
+ _cache = {};
+ USER_ID = identifier;
+ _socket = io(`${protocol.startsWith('https') ? 'wss' : 'ws'}://${hostname}:${port}`, { transports: ['websocket'], rejectUnauthorized: false });
+ _socket.on('connect_error', (err: any) => console.log(err));
+ // io.connect(`https://7f079dda.ngrok.io`);// if using ngrok, create a special address for the websocket
+
+ _GetCachedRefField = _GetCachedRefFieldImpl;
+ SetObjGetRefField((_GetRefField = _GetRefFieldImpl));
+ SetObjGetRefFields((_GetRefFields = _GetRefFieldsImpl));
+ _CreateField = _CreateFieldImpl;
+ _UpdateField = _UpdateFieldImpl;
+
+ /**
+ * Whenever the server sends us its handshake message on our
+ * websocket, we use the above function to return the handshake.
+ */
+ DocServer.AddServerHandler(_socket, MessageStore.Foo, onConnection);
+ DocServer.AddServerHandler(_socket, MessageStore.UpdateField, respondToUpdate);
+ DocServer.AddServerHandler(_socket, MessageStore.DeleteField, respondToDelete);
+ DocServer.AddServerHandler(_socket, MessageStore.DeleteFields, respondToDelete);
+ DocServer.AddServerHandler(_socket, MessageStore.ConnectionTerminated, alertUser);
+ }
}
diff --git a/src/client/Network.ts b/src/client/Network.ts
index cdcd5225a..968c407b2 100644
--- a/src/client/Network.ts
+++ b/src/client/Network.ts
@@ -1,5 +1,6 @@
-import { Utils } from '../Utils';
import * as requestPromise from 'request-promise';
+import { ClientUtils } from '../ClientUtils';
+import { Utils } from '../Utils';
import { Upload } from '../server/SharedMediaTypes';
/**
@@ -14,7 +15,7 @@ export namespace Networking {
export async function PostToServer(relativeRoute: string, body?: any) {
const options = {
- uri: Utils.prepend(relativeRoute),
+ uri: ClientUtils.prepend(relativeRoute),
method: 'POST',
body,
json: true,
@@ -49,14 +50,14 @@ export namespace Networking {
}
const maxFileSize = 50000000;
if (fileguidpairs.some(f => f.file.size > maxFileSize)) {
- return new Promise(res =>
+ return new Promise(res => {
res([
{
source: { name: '', type: '', size: 0, toJson: () => ({ name: '', type: '' }) },
result: { name: '', message: `max file size (${maxFileSize / 1000000}MB) exceeded` },
},
- ])
- );
+ ]);
+ });
}
formData.set('fileguids', fileguidpairs.map(pair => pair.guid).join(';'));
formData.set('filesize', fileguidpairs.reduce((sum, pair) => sum + pair.file.size, 0).toString());
diff --git a/src/client/apis/google_docs/GooglePhotosClientUtils.ts b/src/client/apis/google_docs/GooglePhotosClientUtils.ts
index e8fd8fb8a..757031fec 100644
--- a/src/client/apis/google_docs/GooglePhotosClientUtils.ts
+++ b/src/client/apis/google_docs/GooglePhotosClientUtils.ts
@@ -1,5 +1,6 @@
import { AssertionError } from 'assert';
import { EditorState } from 'prosemirror-state';
+import { ClientUtils } from '../../../ClientUtils';
import { Doc, DocListCastAsync, Opt } from '../../../fields/Doc';
import { Id } from '../../../fields/FieldSymbols';
import { RichTextField } from '../../../fields/RichTextField';
@@ -7,9 +8,8 @@ import { RichTextUtils } from '../../../fields/RichTextUtils';
import { Cast, StrCast } from '../../../fields/Types';
import { ImageField } from '../../../fields/URLField';
import { MediaItem, NewMediaItemResult } from '../../../server/apis/google/SharedTypes';
-import { Utils } from '../../../Utils';
-import { Docs, DocumentOptions, DocUtils } from '../../documents/Documents';
import { Networking } from '../../Network';
+import { DocUtils, Docs, DocumentOptions } from '../../documents/Documents';
import { FormattedTextBox } from '../../views/nodes/formattedText/FormattedTextBox';
import { GoogleAuthenticationManager } from '../GoogleAuthenticationManager';
import Photos = require('googlephotos');
@@ -111,7 +111,7 @@ export namespace GooglePhotos {
await Query.TagChildImages(collection);
}
collection.albumId = id;
- Transactions.AddTextEnrichment(collection, `Find me at ${Utils.prepend(`/doc/${collection[Id]}?sharing=true`)}`);
+ Transactions.AddTextEnrichment(collection, `Find me at ${ClientUtils.prepend(`/doc/${collection[Id]}?sharing=true`)}`);
return { albumId: id, mediaItems };
}
};
@@ -124,7 +124,7 @@ export namespace GooglePhotos {
await GoogleAuthenticationManager.Instance.fetchOrGenerateAccessToken();
const response = await Query.ContentSearch(requested);
const uploads = await Transactions.WriteMediaItemsToServer(response);
- const children = uploads.map((upload: Transactions.UploadInformation) => Docs.Create.ImageDocument(Utils.fileUrl(upload.fileNames.clean) /*, {"data_contentSize":upload.contentSize}*/));
+ const children = uploads.map((upload: Transactions.UploadInformation) => Docs.Create.ImageDocument(ClientUtils.fileUrl(upload.fileNames.clean) /*, {"data_contentSize":upload.contentSize}*/));
const options = { _width: 500, _height: 500 };
return constructor(children, options);
};
@@ -327,7 +327,7 @@ export namespace GooglePhotos {
};
const parseDescription = (document: Doc, descriptionKey: string) => {
- let description: string = Utils.prepend(`/doc/${document[Id]}?sharing=true`);
+ let description: string = ClientUtils.prepend(`/doc/${document[Id]}?sharing=true`);
const target = document[descriptionKey];
if (typeof target === 'string') {
description = target;
diff --git a/src/client/apis/gpt/GPT.ts b/src/client/apis/gpt/GPT.ts
index fb51278ae..63563cb79 100644
--- a/src/client/apis/gpt/GPT.ts
+++ b/src/client/apis/gpt/GPT.ts
@@ -25,14 +25,18 @@ const callTypeMap: { [type: string]: GPTCallOpts } = {
* @param inputText Text to process
* @returns AI Output
*/
+let lastCall = '';
+let lastResp = '';
const gptAPICall = async (inputText: string, callType: GPTCallType) => {
if (callType === GPTCallType.SUMMARY) inputText += '.';
const opts: GPTCallOpts = callTypeMap[callType];
+ if (lastCall === inputText) return lastResp;
try {
const configuration: ClientOptions = {
apiKey: process.env.OPENAI_KEY,
dangerouslyAllowBrowser: true,
};
+ lastCall = inputText;
const openai = new OpenAI(configuration);
const response = await openai.completions.create({
model: opts.model,
@@ -40,6 +44,7 @@ const gptAPICall = async (inputText: string, callType: GPTCallType) => {
temperature: opts.temp,
prompt: `${opts.prompt}${inputText}`,
});
+ lastResp = response.choices[0].text;
return response.choices[0].text;
} catch (err) {
console.log(err);
diff --git a/src/client/apis/youtube/YoutubeBox.scss b/src/client/apis/youtube/YoutubeBox.scss
deleted file mode 100644
index eabdbb1ac..000000000
--- a/src/client/apis/youtube/YoutubeBox.scss
+++ /dev/null
@@ -1,126 +0,0 @@
-.youtubeBox-cont {
- ul {
- list-style-type: none;
- padding-inline-start: 10px;
- }
-
-
- li {
- margin: 4px;
- display: inline-flex;
- }
-
- li:hover {
- cursor: pointer;
- opacity: 0.8;
- }
-
- .search_wrapper {
- width: 100%;
- display: inline-flex;
- height: 175px;
-
- .video_duration {
- // margin: 0;
- // padding: 0;
- border: 0;
- background: transparent;
- display: inline-block;
- position: relative;
- bottom: 25px;
- left: 85%;
- margin: 4px;
- color: #FFFFFF;
- background-color: rgba(0, 0, 0, 0.80);
- padding: 2px 4px;
- border-radius: 2px;
- letter-spacing: .5px;
- font-size: 1.2rem;
- font-weight: 500;
- line-height: 1.2rem;
-
- }
-
- .textual_info {
- font-family: Arial, Helvetica, sans-serif;
-
- .videoTitle {
- margin-left: 4px;
- // display: inline-block;
- color: #0D0D0D;
- -webkit-line-clamp: 2;
- display: block;
- max-height: 4.8rem;
- overflow: hidden;
- font-size: 1.8rem;
- font-weight: 400;
- line-height: 2.4rem;
- -webkit-box-orient: vertical;
- text-overflow: ellipsis;
- white-space: normal;
- display: -webkit-box;
- }
-
- .channelName {
- color: #606060;
- margin-left: 4px;
- font-size: 1.3rem;
- font-weight: 400;
- line-height: 1.8rem;
- text-transform: none;
- margin-top: 0px;
- display: inline-block;
- }
-
- .video_description {
- margin-left: 4px;
- // font-size: 12px;
- color: #606060;
- padding-top: 8px;
- margin-bottom: 8px;
- display: block;
- line-height: 1.8rem;
- max-height: 4.2rem;
- overflow: hidden;
- font-size: 1.3rem;
- font-weight: 400;
- text-transform: none;
- }
-
- .publish_time {
- //display: inline-block;
- margin-left: 8px;
- padding: 0;
- border: 0;
- background: transparent;
- color: #606060;
- max-width: 100%;
- line-height: 1.8rem;
- max-height: 3.6rem;
- overflow: hidden;
- font-size: 1.3rem;
- font-weight: 400;
- text-transform: none;
- }
-
- .viewCount {
-
- margin-left: 8px;
- padding: 0;
- border: 0;
- background: transparent;
- color: #606060;
- max-width: 100%;
- line-height: 1.8rem;
- max-height: 3.6rem;
- overflow: hidden;
- font-size: 1.3rem;
- font-weight: 400;
- text-transform: none;
- }
-
-
-
- }
- }
-}
\ No newline at end of file
diff --git a/src/client/apis/youtube/YoutubeBox.tsx b/src/client/apis/youtube/YoutubeBox.tsx
deleted file mode 100644
index d3a15cd84..000000000
--- a/src/client/apis/youtube/YoutubeBox.tsx
+++ /dev/null
@@ -1,369 +0,0 @@
-import { action, observable, runInAction } from 'mobx';
-import { observer } from 'mobx-react';
-import { Doc, DocListCastAsync } from '../../../fields/Doc';
-import { InkTool } from '../../../fields/InkField';
-import { Cast, NumCast, StrCast } from '../../../fields/Types';
-import { Utils } from '../../../Utils';
-import { DocServer } from '../../DocServer';
-import { Docs } from '../../documents/Documents';
-import { DocumentView } from '../../views/nodes/DocumentView';
-import { FieldView, FieldViewProps } from '../../views/nodes/FieldView';
-import '../../views/nodes/WebBox.scss';
-import './YoutubeBox.scss';
-import * as React from 'react';
-import { SnappingManager } from '../../util/SnappingManager';
-
-interface VideoTemplate {
- thumbnailUrl: string;
- videoTitle: string;
- videoId: string;
- duration: string;
- channelTitle: string;
- viewCount: string;
- publishDate: string;
- videoDescription: string;
-}
-
-/**
- * This class models the youtube search document that can be dropped on to canvas.
- */
-@observer
-export class YoutubeBox extends React.Component {
- @observable YoutubeSearchElement: HTMLInputElement | undefined;
- @observable searchResultsFound: boolean = false;
- @observable searchResults: any[] = [];
- @observable videoClicked: boolean = false;
- @observable selectedVideoUrl: string = '';
- @observable lisOfBackUp: JSX.Element[] = [];
- @observable videoIds: string | undefined;
- @observable videoDetails: any[] = [];
- @observable curVideoTemplates: VideoTemplate[] = [];
-
- public static LayoutString(fieldKey: string) {
- return FieldView.LayoutString(YoutubeBox, fieldKey);
- }
-
- /**
- * When component mounts, last search's results are laoded in based on the back up stored
- * in the document of the props.
- */
- async componentDidMount() {
- //DocServer.getYoutubeChannels();
- const castedSearchBackUp = Cast(this.props.Document.cachedSearchResults, Doc);
- const awaitedBackUp = await castedSearchBackUp;
- const castedDetailBackUp = Cast(this.props.Document.cachedDetails, Doc);
- const awaitedDetails = await castedDetailBackUp;
-
- if (awaitedBackUp) {
- const jsonList = await DocListCastAsync(awaitedBackUp.json);
- const jsonDetailList = await DocListCastAsync(awaitedDetails!.json);
-
- if (jsonList!.length !== 0) {
- runInAction(() => (this.searchResultsFound = true));
- let index = 0;
- //getting the necessary information from backUps and building templates that will be used to map in render
- for (const video of jsonList!) {
- const videoId = await Cast(video.id, Doc);
- const id = StrCast(videoId!.videoId);
- const snippet = await Cast(video.snippet, Doc);
- const videoTitle = this.filterYoutubeTitleResult(StrCast(snippet!.title));
- const thumbnail = await Cast(snippet!.thumbnails, Doc);
- const thumbnailMedium = await Cast(thumbnail!.medium, Doc);
- const thumbnailUrl = StrCast(thumbnailMedium!.url);
- const videoDescription = StrCast(snippet!.description);
- const pusblishDate = this.roundPublishTime(StrCast(snippet!.publishedAt))!;
- const channelTitle = StrCast(snippet!.channelTitle);
- let duration: string = '';
- let viewCount: string = '';
- if (jsonDetailList!.length !== 0) {
- const contentDetails = await Cast(jsonDetailList![index].contentDetails, Doc);
- const statistics = await Cast(jsonDetailList![index].statistics, Doc);
- duration = this.convertIsoTimeToDuration(StrCast(contentDetails!.duration));
- viewCount = this.abbreviateViewCount(parseInt(StrCast(statistics!.viewCount)))!;
- }
- index = index + 1;
- const newTemplate: VideoTemplate = {
- videoId: id,
- videoTitle: videoTitle,
- thumbnailUrl: thumbnailUrl,
- publishDate: pusblishDate,
- channelTitle: channelTitle,
- videoDescription: videoDescription,
- duration: duration,
- viewCount: viewCount,
- };
- runInAction(() => this.curVideoTemplates.push(newTemplate));
- }
- }
- }
- }
-
- _ignore = 0;
- onPreWheel = (e: React.WheelEvent) => {
- this._ignore = e.timeStamp;
- };
- onPrePointer = (e: React.PointerEvent) => {
- this._ignore = e.timeStamp;
- };
- onPostPointer = (e: React.PointerEvent) => {
- if (this._ignore !== e.timeStamp) {
- e.stopPropagation();
- }
- };
- onPostWheel = (e: React.WheelEvent) => {
- if (this._ignore !== e.timeStamp) {
- e.stopPropagation();
- }
- };
-
- /**
- * Function that submits the title entered by user on enter press.
- */
- onEnterKeyDown = (e: React.KeyboardEvent) => {
- if (e.keyCode === 13) {
- const submittedTitle = this.YoutubeSearchElement!.value;
- this.YoutubeSearchElement!.value = '';
- this.YoutubeSearchElement!.blur();
- DocServer.getYoutubeVideos(submittedTitle, this.processesVideoResults);
- }
- };
-
- /**
- * The callback that is passed in to server, which functions as a way to
- * get videos that is returned by search. It also makes a call to server
- * to get details for the videos found.
- */
- @action
- processesVideoResults = (videos: any[]) => {
- this.searchResults = videos;
- if (this.searchResults.length > 0) {
- this.searchResultsFound = true;
- this.videoIds = '';
- videos.forEach(video => {
- if (this.videoIds === '') {
- this.videoIds = video.id.videoId;
- } else {
- this.videoIds = this.videoIds! + ', ' + video.id.videoId;
- }
- });
- //Asking for details that include duration and viewCount from server for videoIds
- DocServer.getYoutubeVideoDetails(this.videoIds, this.processVideoDetails);
- this.backUpSearchResults(videos);
- if (this.videoClicked) {
- this.videoClicked = false;
- }
- }
- };
-
- /**
- * The callback that is given to server to process and receive returned details about the videos.
- */
- @action
- processVideoDetails = (videoDetails: any[]) => {
- this.videoDetails = videoDetails;
- this.props.Document.cachedDetails = Doc.Get.FromJson({ data: videoDetails, title: 'detailBackUp' });
- };
-
- /**
- * The function that stores the search results in the props document.
- */
- backUpSearchResults = (videos: any[]) => {
- this.props.Document.cachedSearchResults = Doc.Get.FromJson({ data: videos, title: 'videosBackUp' });
- };
-
- /**
- * The function that filters out escaped characters returned by the api
- * in the title of the videos.
- */
- filterYoutubeTitleResult = (resultTitle: string) => {
- let processedTitle: string = resultTitle.replace(/&/g, '&'); //.ReplaceAll("&", "&");
- processedTitle = processedTitle.replace(/"'/g, "'");
- processedTitle = processedTitle.replace(/"/g, '"');
- return processedTitle;
- };
-
- /**
- * The function that converts ISO date, which is passed in, to normal date and finds the
- * difference between today's date and that date, in terms of "ago" to imitate youtube.
- */
- roundPublishTime = (publishTime: string) => {
- const date = new Date(publishTime).getTime();
- const curDate = new Date().getTime();
- const timeDif = curDate - date;
- const totalSeconds = timeDif / 1000;
- const totalMin = totalSeconds / 60;
- const totalHours = totalMin / 60;
- const totalDays = totalHours / 24;
- const totalMonths = totalDays / 30.417;
- const totalYears = totalMonths / 12;
-
- const truncYears = Math.trunc(totalYears);
- const truncMonths = Math.trunc(totalMonths);
- const truncDays = Math.trunc(totalDays);
- const truncHours = Math.trunc(totalHours);
- const truncMin = Math.trunc(totalMin);
- const truncSec = Math.trunc(totalSeconds);
-
- let pluralCase = '';
-
- if (truncYears !== 0) {
- truncYears > 1 ? (pluralCase = 's') : (pluralCase = '');
- return truncYears + ' year' + pluralCase + ' ago';
- } else if (truncMonths !== 0) {
- truncMonths > 1 ? (pluralCase = 's') : (pluralCase = '');
- return truncMonths + ' month' + pluralCase + ' ago';
- } else if (truncDays !== 0) {
- truncDays > 1 ? (pluralCase = 's') : (pluralCase = '');
- return truncDays + ' day' + pluralCase + ' ago';
- } else if (truncHours !== 0) {
- truncHours > 1 ? (pluralCase = 's') : (pluralCase = '');
- return truncHours + ' hour' + pluralCase + ' ago';
- } else if (truncMin !== 0) {
- truncMin > 1 ? (pluralCase = 's') : (pluralCase = '');
- return truncMin + ' minute' + pluralCase + ' ago';
- } else if (truncSec !== 0) {
- truncSec > 1 ? (pluralCase = 's') : (pluralCase = '');
- return truncSec + ' second' + pluralCase + ' ago';
- }
- };
-
- /**
- * The function that converts the passed in ISO time to normal duration time.
- */
- convertIsoTimeToDuration = (isoDur: string) => {
- const convertedTime = isoDur.replace(/D|H|M/g, ':').replace(/P|T|S/g, '').split(':');
-
- if (1 === convertedTime.length) {
- 2 !== convertedTime[0].length && (convertedTime[0] = '0' + convertedTime[0]), (convertedTime[0] = '0:' + convertedTime[0]);
- } else {
- for (var r = 1, l = convertedTime.length - 1; l >= r; r++) {
- 2 !== convertedTime[r].length && (convertedTime[r] = '0' + convertedTime[r]);
- }
- }
-
- return convertedTime.join(':');
- };
-
- /**
- * The function that rounds the viewCount to the nearest
- * thousand, million or billion, given a viewCount number.
- */
- abbreviateViewCount = (viewCount: number) => {
- if (viewCount < 1000) {
- return viewCount.toString();
- } else if (viewCount >= 1000 && viewCount < 1000000) {
- return Math.trunc(viewCount / 1000) + 'K';
- } else if (viewCount >= 1000000 && viewCount < 1000000000) {
- return Math.trunc(viewCount / 1000000) + 'M';
- } else if (viewCount >= 1000000000) {
- return Math.trunc(viewCount / 1000000000) + 'B';
- }
- };
-
- /**
- * The function that is called to decide on what'll be rendered by the component.
- * It renders search Results if found. If user didn't do a new search, it renders from the videoTemplates
- * generated by the backUps. If none present, renders nothing.
- */
- renderSearchResultsOrVideo = () => {
- if (this.searchResultsFound) {
- if (this.searchResults.length !== 0) {
- return (
-
- {this.searchResults.map((video, index) => {
- const filteredTitle = this.filterYoutubeTitleResult(video.snippet.title);
- const channelTitle = video.snippet.channelTitle;
- const videoDescription = video.snippet.description;
- const pusblishDate = this.roundPublishTime(video.snippet.publishedAt);
- let duration;
- let viewCount;
- if (this.videoDetails.length !== 0) {
- duration = this.convertIsoTimeToDuration(this.videoDetails[index].contentDetails.duration);
- viewCount = this.abbreviateViewCount(this.videoDetails[index].statistics.viewCount);
- }
-
- return (
- this.embedVideoOnClick(video.id.videoId, filteredTitle)} key={Utils.GenerateGuid()}>
-
-
-
-
{duration}
-
-
-
{filteredTitle}
-
{channelTitle}
-
{viewCount}
-
{pusblishDate}
-
{videoDescription}
-
-
-
- );
- })}
-
- );
- } else if (this.curVideoTemplates.length !== 0) {
- return (
-
- {this.curVideoTemplates.map((video: VideoTemplate) => {
- return (
- this.embedVideoOnClick(video.videoId, video.videoTitle)} key={Utils.GenerateGuid()}>
-
-
-
-
{video.duration}
-
-
-
{video.videoTitle}
-
{video.channelTitle}
-
{video.viewCount}
-
{video.publishDate}
-
{video.videoDescription}
-
-
-
- );
- })}
-
- );
- }
- } else {
- return null;
- }
- };
-
- /**
- * Given a videoId and title, creates a new youtube embedded url, and uses that
- * to create a new video document.
- */
- @action
- embedVideoOnClick = (videoId: string, filteredTitle: string) => {
- const embeddedUrl = 'https://www.youtube.com/embed/' + videoId;
- this.selectedVideoUrl = embeddedUrl;
- const addFunction = this.props.addDocument!;
- const newVideoX = NumCast(this.props.Document.x);
- const newVideoY = NumCast(this.props.Document.y) + NumCast(this.props.Document.height);
-
- addFunction(Docs.Create.VideoDocument(embeddedUrl, { title: filteredTitle, _width: 400, _height: 315, x: newVideoX, y: newVideoY }));
- this.videoClicked = true;
- };
-
- render() {
- const content = (
-
- (this.YoutubeSearchElement = e!)} />
- {this.renderSearchResultsOrVideo()}
-
- );
-
- const frozen = !this.props.isSelected() || SnappingManager.IsResizing;
-
- const classname = 'webBox-cont' + (this.props.isSelected() && Doc.ActiveTool === InkTool.None && !SnappingManager.IsResizing ? '-interactive' : '');
- return (
- <>
- {content}
- {!frozen ? null :
}
- >
- );
- }
-}
diff --git a/src/client/cognitive_services/CognitiveServices.ts b/src/client/cognitive_services/CognitiveServices.ts
index 408903324..4ad03aab4 100644
--- a/src/client/cognitive_services/CognitiveServices.ts
+++ b/src/client/cognitive_services/CognitiveServices.ts
@@ -1,18 +1,16 @@
-import * as request from 'request-promise';
-import { Doc, Field } from '../../fields/Doc';
-import { Cast } from '../../fields/Types';
-import { Utils } from '../../Utils';
+import * as rp from 'request-promise';
+import { Doc, FieldType } from '../../fields/Doc';
import { InkData } from '../../fields/InkField';
-import { UndoManager } from '../util/UndoManager';
-import requestPromise = require('request-promise');
import { List } from '../../fields/List';
+import { Cast } from '../../fields/Types';
+import { UndoManager } from '../util/UndoManager';
type APIManager = { converter: BodyConverter; requester: RequestExecutor };
type RequestExecutor = (apiKey: string, body: string, service: Service) => Promise;
type AnalysisApplier = (target: Doc, relevantKeys: string[], data: D, ...args: any) => any;
type BodyConverter = (data: D) => string;
-type Converter = (results: any) => Field;
-type TextConverter = (results: any, data: string) => Promise<{ keyterms: Field; external_recommendations: any; kp_string: string[] }>;
+type Converter = (results: any) => FieldType;
+type TextConverter = (results: any, data: string) => Promise<{ keyterms: FieldType; external_recommendations: any; kp_string: string[] }>;
type BingConverter = (results: any) => Promise<{ title_vals: string[]; url_vals: string[] }>;
export type Tag = { name: string; confidence: number };
@@ -95,7 +93,7 @@ export namespace CognitiveServices {
},
};
- return request.post(options);
+ return rp.post(options);
},
};
@@ -344,10 +342,10 @@ export namespace CognitiveServices {
export namespace Appliers {
export async function vectorize(keyterms: any, dataDoc: Doc, mainDoc: boolean = false) {
console.log('vectorizing...');
- //keyterms = ["father", "king"];
+ // keyterms = ["father", "king"];
- const args = { method: 'POST', uri: Utils.prepend('/recommender'), body: { keyphrases: keyterms }, json: true };
- await requestPromise.post(args).then(async wordvecs => {
+ const args = { method: 'POST', uri: ClientUtils.prepend('/recommender'), body: { keyphrases: keyterms }, json: true };
+ await rp.post(args).then(async wordvecs => {
if (wordvecs) {
const indices = Object.keys(wordvecs);
console.log('successful vectorization!');
@@ -355,7 +353,7 @@ export namespace CognitiveServices {
indices.forEach((ind: any) => {
vectorValues.push(wordvecs[ind]);
});
- //ClientRecommender.Instance.processVector(vectorValues, dataDoc, mainDoc);
+ // ClientRecommender.Instance.processVector(vectorValues, dataDoc, mainDoc);
} // adds document to internal doc set
else {
console.log('unsuccessful :( word(s) not in vocabulary');
diff --git a/src/client/documents/DocumentTypes.ts b/src/client/documents/DocumentTypes.ts
index 1123bcac9..4e3496608 100644
--- a/src/client/documents/DocumentTypes.ts
+++ b/src/client/documents/DocumentTypes.ts
@@ -25,14 +25,13 @@ export enum DocumentType {
MAP = 'map',
DATAVIZ = 'dataviz',
LOADING = 'loading',
- SIMULATION = 'simulation', //physics simulation
+ SIMULATION = 'simulation', // physics simulation
// special purpose wrappers that either take no data or are compositions of lower level types
LINK = 'link',
IMPORT = 'import',
PRES = 'presentation',
PRESELEMENT = 'preselement',
- YOUTUBE = 'youtube',
COMPARISON = 'comparison',
GROUP = 'group',
PUSHPIN = 'pushpin',
@@ -56,11 +55,10 @@ export enum CollectionViewType {
Carousel = 'carousel',
Carousel3D = '3D Carousel',
Linear = 'linear',
- //Staff = "staff",
Map = 'map',
Grid = 'grid',
Pile = 'pileup',
StackedTimeline = 'stacked timeline',
NoteTaking = 'notetaking',
- Calendar = 'calendar'
+ Calendar = 'calendar',
}
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index b160379df..d41a96db2 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -1,28 +1,35 @@
import { IconProp } from '@fortawesome/fontawesome-svg-core';
+import { saveAs } from 'file-saver';
+import * as JSZip from 'jszip';
import { action, reaction, runInAction } from 'mobx';
import { basename } from 'path';
-import { OmitKeys, Utils } from '../../Utils';
+import { ClientUtils, OmitKeys } from '../../ClientUtils';
+import * as JSZipUtils from '../../JSZipUtils';
+import { decycle } from '../../decycler/decycler';
import { DateField } from '../../fields/DateField';
-import { Doc, DocListCast, Field, LinkedTo, Opt, StrListCast, updateCachedAcls } from '../../fields/Doc';
+import { Doc, DocListCast, Field, FieldType, LinkedTo, Opt, StrListCast, updateCachedAcls } from '../../fields/Doc';
import { DocData, Initializing } from '../../fields/DocSymbols';
import { Id } from '../../fields/FieldSymbols';
import { HtmlField } from '../../fields/HtmlField';
-import { InkField, PointData } from '../../fields/InkField';
-import { List } from '../../fields/List';
+import { InkDataFieldName, InkField } from '../../fields/InkField';
+import { List, ListFieldName } from '../../fields/List';
+import { ProxyField } from '../../fields/Proxy';
import { RichTextField } from '../../fields/RichTextField';
import { SchemaHeaderField } from '../../fields/SchemaHeaderField';
import { ComputedField, ScriptField } from '../../fields/ScriptField';
import { BoolCast, Cast, DocCast, FieldValue, NumCast, ScriptCast, StrCast } from '../../fields/Types';
-import { AudioField, CsvField, ImageField, PdfField, VideoField, WebField, YoutubeField } from '../../fields/URLField';
+import { AudioField, CsvField, ImageField, PdfField, VideoField, WebField } from '../../fields/URLField';
import { SharingPermissions, inheritParentAcls } from '../../fields/util';
+import { PointData } from '../../pen-gestures/GestureTypes';
import { Upload } from '../../server/SharedMediaTypes';
import { DocServer } from '../DocServer';
import { Networking } from '../Network';
-import { YoutubeBox } from '../apis/youtube/YoutubeBox';
-import { DragManager, dropActionType } from '../util/DragManager';
+import { DragManager } from '../util/DragManager';
+import { dropActionType } from '../util/DropActionTypes';
import { FollowLinkScript } from '../util/LinkFollower';
import { LinkManager } from '../util/LinkManager';
import { ScriptingGlobals } from '../util/ScriptingGlobals';
+import { SerializationHelper } from '../util/SerializationHelper';
import { UndoManager, undoable } from '../util/UndoManager';
import { ContextMenu } from '../views/ContextMenu';
import { ContextMenuProps } from '../views/ContextMenuItem';
@@ -30,7 +37,7 @@ import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveIn
import { CollectionDockingView } from '../views/collections/CollectionDockingView';
import { CollectionView } from '../views/collections/CollectionView';
import { DimUnit } from '../views/collections/collectionMulticolumn/CollectionMulticolumnView';
-import { AudioBox, media_state } from '../views/nodes/AudioBox';
+import { AudioBox, mediaState } from '../views/nodes/AudioBox';
import { ComparisonBox } from '../views/nodes/ComparisonBox';
import { DataVizBox } from '../views/nodes/DataVizBox/DataVizBox';
import { OpenWhere } from '../views/nodes/DocumentView';
@@ -60,6 +67,7 @@ import { PresBox } from '../views/nodes/trails/PresBox';
import { PresElementBox } from '../views/nodes/trails/PresElementBox';
import { SearchBox } from '../views/search/SearchBox';
import { CollectionViewType, DocumentType } from './DocumentTypes';
+
const { DFLT_IMAGE_NATIVE_DIM } = require('../views/global/globalCssVariables.module.scss'); // prettier-ignore
const defaultNativeImageDim = Number(DFLT_IMAGE_NATIVE_DIM.replace('px', ''));
@@ -83,7 +91,7 @@ export class FInfo {
description: string = '';
readOnly: boolean = false;
fieldType?: FInfoFieldType;
- values?: Field[];
+ values?: FieldType[];
filterable?: boolean = true; // can be used as a Filter in FilterPanel
// format?: string; // format to display values (e.g, decimal places, $, etc)
@@ -252,8 +260,8 @@ export class DocumentOptions {
opacity?: NUMt = new NumInfo('document opacity', false);
viewTransitionTime?: NUMt = new NumInfo('transition duration for view parameters', false);
dontRegisterView?: BOOLt = new BoolInfo('are views of this document registered so that they can be found when following links, etc', false);
- _undoIgnoreFields?: List; //'fields that should not be added to the undo stack (opacity for Undo/Redo/and sidebar) AND whether modifications to document are undoable (true for linearview menu buttons to prevent open/close from entering undo stack)'
- undoIgnoreFields?: List; //'fields that should not be added to the undo stack (opacity for Undo/Redo/and sidebar) AND whether modifications to document are undoable (true for linearview menu buttons to prevent open/close from entering undo stack)'
+ _undoIgnoreFields?: List; // 'fields that should not be added to the undo stack (opacity for Undo/Redo/and sidebar) AND whether modifications to document are undoable (true for linearview menu buttons to prevent open/close from entering undo stack)'
+ undoIgnoreFields?: List; // 'fields that should not be added to the undo stack (opacity for Undo/Redo/and sidebar) AND whether modifications to document are undoable (true for linearview menu buttons to prevent open/close from entering undo stack)'
_header_height?: NUMt = new NumInfo('height of document header used for displaying title', false);
_header_fontSize?: NUMt = new NumInfo('font size of header of custom notes', false);
_header_pointerEvents?: PEVt = new PEInfo('types of events the header of a custom text document can consume');
@@ -321,7 +329,7 @@ export class DocumentOptions {
_label_maxFontSize?: NUMt = new NumInfo('maximum font size for labelBoxes', false);
stroke_width?: NUMt = new NumInfo('width of an ink stroke', false);
stroke_showLabel?: BOOLt = new BoolInfo('show label inside of stroke');
- mediaState?: STRt = new StrInfo(`status of audio/video media document: ${media_state.PendingRecording}, ${media_state.Recording}, ${media_state.Paused}, ${media_state.Playing}`, false);
+ mediaState?: STRt = new StrInfo(`status of audio/video media document: ${mediaState.PendingRecording}, ${mediaState.Recording}, ${mediaState.Paused}, ${mediaState.Playing}`, false);
recording?: BOOLt = new BoolInfo('whether WebCam is recording or not');
slides?: DOCt = new DocInfo('presentation slide associated with video recording (bcz: should be renamed!!)');
autoPlayAnchors?: BOOLt = new BoolInfo('whether to play audio/video when an anchor is clicked in a stackedTimeline.');
@@ -401,7 +409,7 @@ export class DocumentOptions {
_freeform_noZoom?: BOOLt = new BoolInfo('disables zooming (used by Pile docs)');
_freeform_fitContentsToBox?: BOOLt = new BoolInfo('whether a freeformview should zoom/scale to create a shrinkwrapped view of its content');
- //BUTTONS
+ // BUTTONS
buttonText?: string;
btnType?: string;
btnList?: List;
@@ -413,7 +421,7 @@ export class DocumentOptions {
switchToggle?: boolean;
badgeValue?: ScriptField;
- //LINEAR VIEW
+ // LINEAR VIEW
linearView_IsOpen?: BOOLt = new BoolInfo('is linear view open');
linearView_Expandable?: BOOLt = new BoolInfo('can linear view be expanded');
linearView_Dropdown?: BOOLt = new BoolInfo('can linear view be opened as a dropdown');
@@ -431,7 +439,7 @@ export class DocumentOptions {
link_anchor_1_useSmallAnchor?: BOOLt = new BoolInfo('whether link_anchor_1 of a link should use a miniature anchor dot (as when the anchor is a text selection)');
link_anchor_2_useSmallAnchor?: BOOLt = new BoolInfo('whether link_anchor_1 of a link should use a miniature anchor dot (as when the anchor is a text selection)');
link_relationshipList?: List; // for storing different link relationships (when set by user in the link editor)
- link_relationshipSizes?: List; //stores number of links contained in each relationship
+ link_relationshipSizes?: List; // stores number of links contained in each relationship
link_colorList?: List; // colors of links corresponding to specific link relationships
followLinkZoom?: BOOLt = new BoolInfo('whether to zoom to the target of a link');
followLinkToggle?: BOOLt = new BoolInfo('whether target of link should be toggled on and off when following a link to it');
@@ -463,7 +471,7 @@ export class DocumentOptions {
dragFactory_count?: NUMt = new NumInfo('number of items created from a drag button (used for setting title with incrementing index)', false, true);
dragFactory?: DOCt = new DocInfo('document to create when dragging with a suitable onDragStart script', false);
clickFactory?: DOCt = new DocInfo('document to create when clicking on a button with a suitable onClick script', false);
- onDragStart?: ScriptField; //script to execute at start of drag operation -- e.g., when a "creator" button is dragged this script generates a different document to drop
+ onDragStart?: ScriptField; // script to execute at start of drag operation -- e.g., when a "creator" button is dragged this script generates a different document to drop
target?: Doc; // available for use in scripts. used to provide a document parameter to the script (Note, this is a convenience entry since any field could be used for parameterizing a script)
tags?: LISTt = new ListInfo('hashtags added to document, typically using a text view', true);
treeView_HideTitle?: BOOLt = new BoolInfo('whether to hide the top document title of a tree view');
@@ -502,8 +510,6 @@ export class DocumentOptions {
export const DocOptions = new DocumentOptions();
export namespace Docs {
- export let newAccount: boolean = false;
-
export namespace Prototypes {
type LayoutSource = { LayoutString: (key: string) => string };
type PrototypeTemplate = {
@@ -646,12 +652,6 @@ export namespace Docs {
options: { systemIcon: 'BsFileEarmarkCodeFill' },
},
],
- [
- DocumentType.YOUTUBE,
- {
- layout: { view: YoutubeBox, dataField: defaultDataKey },
- },
- ],
[
DocumentType.LABEL,
{
@@ -670,7 +670,7 @@ export namespace Docs {
layout_nativeDimEditable: true,
layout_hideDecorationTitle: true,
systemIcon: 'BsCalculatorFill',
- }, ///systemIcon: 'BsSuperscript' + BsSubscript
+ }, // systemIcon: 'BsSuperscript' + BsSubscript
},
],
[
@@ -850,7 +850,7 @@ export namespace Docs {
// fetch the actual prototype documents from the server
const actualProtos = await DocServer.GetRefFields(prototypeIds);
// update this object to include any default values: DocumentOptions for all prototypes
- prototypeIds.map(id => {
+ prototypeIds.forEach(id => {
const existing = actualProtos[id] as Doc;
const type = id.replace(suffix, '') as DocumentType;
// get or create prototype of the specified type...
@@ -908,7 +908,7 @@ export namespace Docs {
if (!template) {
return undefined;
}
- const layout = template.layout;
+ const { layout } = template;
// create title
const upper = suffix.toUpperCase();
const title = prototypeId.toUpperCase().replace(upper, `_${upper}`);
@@ -928,7 +928,7 @@ export namespace Docs {
};
Object.entries(options)
.filter(pair => typeof pair[1] === 'string' && pair[1].startsWith('@'))
- .map(pair => {
+ .forEach(pair => {
if (!existing || ScriptCast(existing[pair[0]])?.script.originalScript !== pair[1].substring(1)) {
(options as any)[pair[0]] = ComputedField.MakeFunction(pair[1].substring(1));
}
@@ -960,7 +960,9 @@ export namespace Docs {
* only when creating a DockDocument from the current user's already existing
* main document.
*/
- function InstanceFromProto(proto: Doc, data: Field | undefined, options: DocumentOptions, delegId?: string, fieldKey: string = 'data', protoId?: string, placeholderDoc?: Doc, noView?: boolean) {
+ // eslint-disable-next-line default-param-last
+ function InstanceFromProto(proto: Doc, data: FieldType | undefined, options: DocumentOptions, delegId?: string, fieldKey: string = 'data', protoId?: string, placeholderDocIn?: Doc, noView?: boolean) {
+ const placeholderDoc = placeholderDocIn;
const viewKeys = ['x', 'y', 'isSystem']; // keys that should be addded to the view document even though they don't begin with an "_"
const { omit: dataProps, extract: viewProps } = OmitKeys(options, viewKeys, '^_');
@@ -968,7 +970,7 @@ export namespace Docs {
dataProps['acl-Guest'] = options['acl-Guest'] ?? (Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.View);
dataProps.isSystem = viewProps.isSystem;
dataProps.isDataDoc = true;
- dataProps.author = Doc.CurrentUserEmail;
+ dataProps.author = ClientUtils.CurrentUserEmail;
dataProps.author_date = new DateField();
if (fieldKey) {
dataProps[`${fieldKey}_modificationDate`] = new DateField();
@@ -988,7 +990,7 @@ export namespace Docs {
}
if (!noView) {
- const viewFirstProps: { [id: string]: any } = { author: Doc.CurrentUserEmail };
+ const viewFirstProps: { [id: string]: any } = { author: ClientUtils.CurrentUserEmail };
viewFirstProps['acl-Guest'] = options['_acl-Guest'] ?? (Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.View);
let viewDoc: Doc;
// determines whether viewDoc should be created using placeholder Doc or default
@@ -998,7 +1000,9 @@ export namespace Docs {
viewDoc = Doc.assign(placeholderDoc, viewFirstProps, true, true);
Array.from(Object.keys(placeholderDoc))
.filter(key => key.startsWith('acl'))
- .forEach(key => (dataDoc[key] = viewDoc[key] = placeholderDoc[key]));
+ .forEach(key => {
+ dataDoc[key] = viewDoc[key] = placeholderDoc[key];
+ });
} else {
viewDoc = Doc.assign(Doc.MakeDelegate(dataDoc, delegId), viewFirstProps, true, true);
}
@@ -1017,6 +1021,7 @@ export namespace Docs {
return dataDoc;
}
+ // eslint-disable-next-line default-param-last
export function ImageDocument(url: string | ImageField, options: DocumentOptions = {}, overwriteDoc?: Doc) {
const imgField = url instanceof ImageField ? url : url ? new ImageField(url) : undefined;
return InstanceFromProto(Prototypes.get(DocumentType.IMG), imgField, { title: basename(imgField?.url.href ?? '-no image-'), ...options }, undefined, undefined, undefined, overwriteDoc);
@@ -1035,18 +1040,16 @@ export namespace Docs {
* @param fieldKey the field that the compiled script is written into.
* @returns the Scripting Doc
*/
+ // eslint-disable-next-line default-param-last
export function ScriptingDocument(script: Opt | null, options: DocumentOptions = {}, fieldKey?: string) {
- return InstanceFromProto(Prototypes.get(DocumentType.SCRIPTING), script ? script : undefined, { ...options, layout: fieldKey ? ScriptingBox.LayoutString(fieldKey) : undefined });
+ return InstanceFromProto(Prototypes.get(DocumentType.SCRIPTING), script || undefined, { ...options, layout: fieldKey ? ScriptingBox.LayoutString(fieldKey) : undefined });
}
+ // eslint-disable-next-line default-param-last
export function VideoDocument(url: string, options: DocumentOptions = {}, overwriteDoc?: Doc) {
return InstanceFromProto(Prototypes.get(DocumentType.VID), new VideoField(url), options, undefined, undefined, undefined, overwriteDoc);
}
- export function YoutubeDocument(url: string, options: DocumentOptions = {}, overwriteDoc?: Doc) {
- return InstanceFromProto(Prototypes.get(DocumentType.YOUTUBE), new YoutubeField(url), options, undefined, undefined, undefined, overwriteDoc);
- }
-
export function WebCamDocument(url: string, options: DocumentOptions = {}) {
return InstanceFromProto(Prototypes.get(DocumentType.WEBCAM), '', options);
}
@@ -1059,6 +1062,7 @@ export namespace Docs {
return InstanceFromProto(Prototypes.get(DocumentType.COMPARISON), undefined, options);
}
+ // eslint-disable-next-line default-param-last
export function AudioDocument(url: string, options: DocumentOptions = {}, overwriteDoc?: Doc) {
return InstanceFromProto(Prototypes.get(DocumentType.AUDIO), new AudioField(url), options, undefined, undefined, undefined, overwriteDoc);
}
@@ -1072,7 +1076,7 @@ export namespace Docs {
}
export function LoadingDocument(file: File | string, options: DocumentOptions) {
- return InstanceFromProto(Prototypes.get(DocumentType.LOADING), undefined, { _height: 150, _width: 200, title: typeof file == 'string' ? file : file.name, ...options }, undefined, '');
+ return InstanceFromProto(Prototypes.get(DocumentType.LOADING), undefined, { _height: 150, _width: 200, title: typeof file === 'string' ? file : file.name, ...options }, undefined, '');
}
export function RTFDocument(field: RichTextField, options: DocumentOptions = {}, fieldKey: string = 'text') {
@@ -1101,6 +1105,7 @@ export namespace Docs {
return InstanceFromProto(Prototypes.get(DocumentType.RTF), field, options, undefined, fieldKey);
}
+ // eslint-disable-next-line default-param-last
export function LinkDocument(source: Doc, target: Doc, options: DocumentOptions = {}, id?: string) {
const linkDoc = InstanceFromProto(
Prototypes.get(DocumentType.LINK),
@@ -1124,7 +1129,7 @@ export namespace Docs {
options: DocumentOptions = {},
strokeWidth = ActiveInkWidth(),
color = ActiveInkColor(),
- stroke_bezier = ActiveInkBezierApprox(),
+ strokeBezier = ActiveInkBezierApprox(),
fillColor = ActiveFillColor(),
arrowStart = ActiveArrowStart(),
arrowEnd = ActiveArrowEnd(),
@@ -1138,7 +1143,7 @@ export namespace Docs {
I.fillColor = fillColor;
I.stroke = new InkField(points);
I.stroke_width = strokeWidth;
- I.stroke_bezier = stroke_bezier;
+ I.stroke_bezier = strokeBezier;
I.stroke_startMarker = arrowStart;
I.stroke_endMarker = arrowEnd;
I.stroke_dash = dash;
@@ -1148,12 +1153,13 @@ export namespace Docs {
I.defaultDoubleClick = 'ignore';
I.author_date = new DateField();
I['acl-Guest'] = Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.View;
- //I['acl-Override'] = SharingPermissions.Unset;
+ // I['acl-Override'] = SharingPermissions.Unset;
I[Initializing] = false;
return ink;
}
+ // eslint-disable-next-line default-param-last
export function PdfDocument(url: string, options: DocumentOptions = {}, overwriteDoc?: Doc) {
const width = options._width || undefined;
const height = options._height || undefined;
@@ -1169,7 +1175,7 @@ export namespace Docs {
const nwid = options._nativeWidth || undefined;
const nhght = options._nativeHeight || undefined;
if (!nhght && width && height && nwid) options._nativeHeight = (Number(nwid) * Number(height)) / Number(width);
- return InstanceFromProto(Prototypes.get(DocumentType.WEB), new WebField(url ? url : 'https://www.wikipedia.org/'), options);
+ return InstanceFromProto(Prototypes.get(DocumentType.WEB), new WebField(url || 'https://www.wikipedia.org/'), options);
}
export function HtmlDocument(html: string, options: DocumentOptions = {}) {
@@ -1324,10 +1330,10 @@ export namespace Docs {
const doc = DockDocument(
configs.map(c => c.doc),
JSON.stringify(layoutConfig),
- Doc.CurrentUserEmail === 'guest' ? options : { 'acl-Guest': SharingPermissions.View, ...options },
+ ClientUtils.CurrentUserEmail === 'guest' ? options : { 'acl-Guest': SharingPermissions.View, ...options },
id
);
- configs.map(c => {
+ configs.forEach(c => {
Doc.SetContainer(c.doc, doc);
inheritParentAcls(doc, c.doc, false);
});
@@ -1345,8 +1351,9 @@ export namespace Docs {
}
export namespace DocUtils {
- function matchFieldValue(doc: Doc, key: string, value: any): boolean {
- const hasFunctionFilter = Utils.HasFunctionFilter(value);
+ function matchFieldValue(doc: Doc, key: string, valueIn: any): boolean {
+ let value = valueIn;
+ const hasFunctionFilter = ClientUtils.HasFunctionFilter(value);
if (hasFunctionFilter) {
return hasFunctionFilter(StrCast(doc[key]));
}
@@ -1365,7 +1372,7 @@ export namespace DocUtils {
matchLink(value,DocCast(link.link_anchor_2)) ));
}
if (typeof value === 'string') {
- value = value.replace(`,${Utils.noRecursionHack}`, '');
+ value = value.replace(`,${ClientUtils.noRecursionHack}`, '');
}
const fieldVal = doc[key];
// prettier-ignore
@@ -1377,7 +1384,7 @@ export namespace DocUtils {
if (vals.length) {
return vals.some(v => typeof v === 'string' && v.includes(value)); // bcz: arghh: Todo: comparison should be parameterized as exact, or substring
}
- return Field.toString(fieldVal as Field).includes(value); // bcz: arghh: Todo: comparison should be parameterized as exact, or substring
+ return Field.toString(fieldVal as FieldType).includes(value); // bcz: arghh: Todo: comparison should be parameterized as exact, or substring
}
/**
* @param docs
@@ -1410,7 +1417,9 @@ export namespace DocUtils {
if (d.cookies && (!filterFacets.cookies || !Object.keys(filterFacets.cookies).some(key => d.cookies === key))) {
return false;
}
- for (const facetKey of Object.keys(filterFacets).filter(fkey => fkey !== 'cookies' && fkey !== Utils.noDragDocsFilter.split(Doc.FilterSep)[0])) {
+ const facetKeys = Object.keys(filterFacets).filter(fkey => fkey !== 'cookies' && fkey !== ClientUtils.noDragDocsFilter.split(Doc.FilterSep)[0]);
+ // eslint-disable-next-line no-restricted-syntax
+ for (const facetKey of facetKeys) {
const facet = filterFacets[facetKey];
// facets that match some value in the field of the document (e.g. some text field)
@@ -1431,8 +1440,8 @@ export namespace DocUtils {
if (!unsets.length && !exists.length && !xs.length && !checks.length && !matches.length) return true;
const failsNotEqualFacets = !xs.length ? false : xs.some(value => matchFieldValue(d, facetKey, value));
const satisfiesCheckFacets = !checks.length ? true : checks.some(value => matchFieldValue(d, facetKey, value));
- const satisfiesExistsFacets = !exists.length ? true : exists.some(value => (facetKey !== LinkedTo ? d[facetKey] !== undefined : LinkManager.Instance.getAllRelatedLinks(d).length));
- const satisfiesUnsetsFacets = !unsets.length ? true : unsets.some(value => d[facetKey] === undefined);
+ const satisfiesExistsFacets = !exists.length ? true : facetKey !== LinkedTo ? d[facetKey] !== undefined : LinkManager.Instance.getAllRelatedLinks(d).length;
+ const satisfiesUnsetsFacets = !unsets.length ? true : d[facetKey] === undefined;
const satisfiesMatchFacets = !matches.length
? true
: matches.some(value => {
@@ -1441,20 +1450,18 @@ export namespace DocUtils {
const allKeys = Array.from(Object.keys(d));
allKeys.push(...Object.keys(Doc.GetProto(d)));
const keys = allKeys.filter(key => key.includes(facetKey.substring(1)));
- return keys.some(key => Field.toString(d[key] as Field).includes(value));
+ return keys.some(key => Field.toString(d[key] as FieldType).includes(value));
}
- return Field.toString(d[facetKey] as Field).includes(value);
+ return Field.toString(d[facetKey] as FieldType).includes(value);
});
// if we're ORing them together, the default return is false, and we return true for a doc if it satisfies any one set of criteria
if (parentCollection?.childFilters_boolean === 'OR') {
if (satisfiesUnsetsFacets && satisfiesExistsFacets && satisfiesCheckFacets && !failsNotEqualFacets && satisfiesMatchFacets) return true;
}
// if we're ANDing them together, the default return is true, and we return false for a doc if it doesn't satisfy any set of criteria
- else {
- if (!satisfiesUnsetsFacets || !satisfiesExistsFacets || !satisfiesCheckFacets || failsNotEqualFacets || (matches.length && !satisfiesMatchFacets)) return false;
- }
+ else if (!satisfiesUnsetsFacets || !satisfiesExistsFacets || !satisfiesCheckFacets || failsNotEqualFacets || (matches.length && !satisfiesMatchFacets)) return false;
}
- return parentCollection?.childFilters_boolean === 'OR' ? false : true;
+ return parentCollection?.childFilters_boolean !== 'OR';
})
: childDocs;
const rangeFilteredDocs = filteredDocs.filter(d => {
@@ -1464,7 +1471,7 @@ export namespace DocUtils {
const max = Number(childFiltersByRanges[i + 2]);
const val = typeof d[key] === 'string' ? (Number(StrCast(d[key])).toString() === StrCast(d[key]) ? Number(StrCast(d[key])) : undefined) : Cast(d[key], 'number', null);
if (val === undefined) {
- //console.log("Should 'undefined' pass range filter or not?")
+ // console.log("Should 'undefined' pass range filter or not?")
} else if (val < min || val > max) return false;
}
return true;
@@ -1472,10 +1479,10 @@ export namespace DocUtils {
return rangeFilteredDocs;
}
- export let ActiveRecordings: { props: FieldViewProps; getAnchor: (addAsAnnotation: boolean) => Doc }[] = [];
+ export const ActiveRecordings: { props: FieldViewProps; getAnchor: (addAsAnnotation: boolean) => Doc }[] = [];
export function MakeLinkToActiveAudio(getSourceDoc: () => Doc | undefined, broadcastEvent = true) {
- broadcastEvent && runInAction(() => (Doc.RecordingEvent = Doc.RecordingEvent + 1));
+ broadcastEvent && runInAction(() => { Doc.RecordingEvent += 1; }); // prettier-ignore
return DocUtils.ActiveRecordings.map(audio => {
const sourceDoc = getSourceDoc();
return sourceDoc && DocUtils.MakeLink(sourceDoc, audio.getAnchor(true) || audio.props.Document, { link_displayLine: false, link_relationship: 'recording annotation:linked recording', link_description: 'recording timeline' });
@@ -1510,7 +1517,9 @@ export namespace DocUtils {
}
setTimeout(
- action(() => (TaskCompletionBox.taskCompleted = false)),
+ action(() => {
+ TaskCompletionBox.taskCompleted = false;
+ }),
2500
);
}
@@ -1550,7 +1559,7 @@ export namespace DocUtils {
export function AssignScripts(doc: Doc, scripts?: { [key: string]: string | undefined }, funcs?: { [key: string]: string }) {
scripts &&
- Object.keys(scripts).map(key => {
+ Object.keys(scripts).forEach(key => {
const script = scripts[key];
if (ScriptCast(doc[key])?.script.originalScript !== scripts[key] && script) {
(key.startsWith('_') ? doc : Doc.GetProto(doc))[key] = ScriptField.MakeScript(script, {
@@ -1573,7 +1582,7 @@ export namespace DocUtils {
funcs &&
Object.keys(funcs)
.filter(key => !key.endsWith('-setter'))
- .map(key => {
+ .forEach(key => {
const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
if (ScriptCast(cfield)?.script.originalScript !== funcs[key]) {
const setFunc = Cast(funcs[key + '-setter'], 'string', null);
@@ -1602,6 +1611,7 @@ export namespace DocUtils {
return doc;
}
export function AssignDocField(doc: Doc, field: string, creator: (reqdOpts: DocumentOptions, items?: Doc[]) => Doc, reqdOpts: DocumentOptions, items?: Doc[], scripts?: { [key: string]: string }, funcs?: { [key: string]: string }) {
+ // eslint-disable-next-line no-return-assign
return DocUtils.AssignScripts(DocUtils.AssignOpts(DocCast(doc[field]), reqdOpts, items) ?? (doc[field] = creator(reqdOpts, items)), scripts, funcs);
}
@@ -1649,7 +1659,7 @@ export namespace DocUtils {
* @returns
*/
export async function DocumentFromType(type: string, path: string, options: DocumentOptions, overwriteDoc?: Doc): Promise> {
- let ctor: ((path: string, options: DocumentOptions, overwriteDoc?: Doc) => Doc | Promise) | undefined = undefined;
+ let ctor: ((path: string, options: DocumentOptions, overwriteDoc?: Doc) => Doc | Promise) | undefined;
if (type.indexOf('image') !== -1) {
ctor = Docs.Create.ImageDocument;
if (!options._width) options._width = 300;
@@ -1672,7 +1682,7 @@ export namespace DocUtils {
if (!options._width) options._width = 400;
if (!options._height) options._height = ((options._width as number) * 1200) / 927;
}
- //TODO:al+glr
+ // TODO:al+glr
// if (type.indexOf("map") !== -1) {
// ctor = Docs.Create.MapDocument;
// if (!options._width) options._width = 800;
@@ -1695,6 +1705,7 @@ export namespace DocUtils {
});
}
ctor = Docs.Create.WebDocument;
+ // eslint-disable-next-line no-param-reassign
options = { ...options, _width: 400, _height: 512, title: path };
}
@@ -1706,12 +1717,12 @@ export namespace DocUtils {
.filter(btnDoc => !btnDoc.hidden)
.map(btnDoc => Cast(btnDoc?.dragFactory, Doc, null))
.filter(doc => doc && doc !== Doc.UserDoc().emptyTrail && doc.title)
- .map((dragDoc, i) => ({
+ .map(dragDoc => ({
description: ':' + StrCast(dragDoc.title).replace('Untitled ', ''),
- event: undoable((args: { x: number; y: number }) => {
+ event: undoable(() => {
const newDoc = DocUtils.copyDragFactory(dragDoc);
if (newDoc) {
- newDoc.author = Doc.CurrentUserEmail;
+ newDoc.author = ClientUtils.CurrentUserEmail;
newDoc.x = x;
newDoc.y = y;
EquationBox.SelectOnLoad = newDoc[Id];
@@ -1732,9 +1743,9 @@ export namespace DocUtils {
!simpleMenu &&
ContextMenu.Instance.addItem({
description: 'Styled Notes',
- subitems: DocListCast((Doc.UserDoc().template_notes as Doc).data).map((note, i) => ({
+ subitems: DocListCast((Doc.UserDoc().template_notes as Doc).data).map(note => ({
description: ':' + StrCast(note.title),
- event: undoable((args: { x: number; y: number }) => {
+ event: undoable(() => {
const textDoc = Docs.Create.TextDocument('', {
_width: 200,
x,
@@ -1757,12 +1768,12 @@ export namespace DocUtils {
.filter(btnDoc => !btnDoc.hidden)
.map(btnDoc => Cast(btnDoc?.dragFactory, Doc, null))
.filter(doc => doc && doc !== Doc.UserDoc().emptyTrail && doc !== Doc.UserDoc().emptyNote && doc.title)
- .map((dragDoc, i) => ({
+ .map(dragDoc => ({
description: ':' + StrCast(dragDoc.title).replace('Untitled ', ''),
- event: undoable((args: { x: number; y: number }) => {
+ event: undoable(() => {
const newDoc = DocUtils.delegateDragFactory(dragDoc);
if (newDoc) {
- newDoc.author = Doc.CurrentUserEmail;
+ newDoc.author = ClientUtils.CurrentUserEmail;
newDoc.x = x;
newDoc.y = y;
EquationBox.SelectOnLoad = newDoc[Id];
@@ -1802,11 +1813,11 @@ export namespace DocUtils {
}
export function findTemplate(templateName: string, type: string, signature: string) {
let docLayoutTemplate: Opt;
- const iconViews = DocListCast(Cast(Doc.UserDoc()['template_icons'], Doc, null)?.data);
- const templBtns = DocListCast(Cast(Doc.UserDoc()['template_buttons'], Doc, null)?.data);
- const noteTypes = DocListCast(Cast(Doc.UserDoc()['template_notes'], Doc, null)?.data);
- const userTypes = DocListCast(Cast(Doc.UserDoc()['template_user'], Doc, null)?.data);
- const clickFuncs = DocListCast(Cast(Doc.UserDoc()['template_clickFuncs'], Doc, null)?.data);
+ const iconViews = DocListCast(Cast(Doc.UserDoc().template_icons, Doc, null)?.data);
+ const templBtns = DocListCast(Cast(Doc.UserDoc().template_buttons, Doc, null)?.data);
+ const noteTypes = DocListCast(Cast(Doc.UserDoc().template_notes, Doc, null)?.data);
+ const userTypes = DocListCast(Cast(Doc.UserDoc().template_user, Doc, null)?.data);
+ const clickFuncs = DocListCast(Cast(Doc.UserDoc().template_clickFuncs, Doc, null)?.data);
const allTemplates = iconViews
.concat(templBtns)
.concat(noteTypes)
@@ -1816,13 +1827,20 @@ export namespace DocUtils {
.filter(doc => doc.isTemplateDoc);
// bcz: this is hacky -- want to have different templates be applied depending on the "type" of a document. but type is not reliable and there could be other types of template searches so this should be generalized
// first try to find a template that matches the specific document type (_). otherwise, fallback to a general match on
- !docLayoutTemplate && allTemplates.forEach(tempDoc => StrCast(tempDoc.title) === templateName + '_' + type && (docLayoutTemplate = tempDoc));
- !docLayoutTemplate && allTemplates.forEach(tempDoc => StrCast(tempDoc.title) === templateName && (docLayoutTemplate = tempDoc));
+ !docLayoutTemplate &&
+ allTemplates.forEach(tempDoc => {
+ StrCast(tempDoc.title) === templateName + '_' + type && (docLayoutTemplate = tempDoc);
+ });
+ !docLayoutTemplate &&
+ allTemplates.forEach(tempDoc => {
+ StrCast(tempDoc.title) === templateName && (docLayoutTemplate = tempDoc);
+ });
return docLayoutTemplate;
}
export function createCustomView(doc: Doc, creator: Opt<(documents: Array, options: DocumentOptions, id?: string) => Doc>, templateSignature: string = 'custom', docLayoutTemplate?: Doc) {
const templateName = templateSignature.replace(/\(.*\)/, '');
- doc.layout_fieldKey = 'layout_' + (templateSignature || docLayoutTemplate?.title);
+ doc.layout_fieldKey = 'layout_' + (templateSignature || (docLayoutTemplate?.title ?? ''));
+ // eslint-disable-next-line no-param-reassign
docLayoutTemplate = docLayoutTemplate || findTemplate(templateName, StrCast(doc.isGroup && doc.transcription ? 'transcription' : doc.type), templateSignature);
const customName = 'layout_' + templateSignature;
@@ -1859,14 +1877,15 @@ export namespace DocUtils {
}
}
export function iconify(doc: Doc) {
- const layout_fieldKey = Cast(doc.layout_fieldKey, 'string', null);
+ const layoutFieldKey = Cast(doc.layout_fieldKey, 'string', null);
DocUtils.makeCustomViewClicked(doc, Docs.Create.StackingDocument, 'icon', undefined);
- if (layout_fieldKey && layout_fieldKey !== 'layout' && layout_fieldKey !== 'layout_icon') doc.deiconifyLayout = layout_fieldKey.replace('layout_', '');
+ if (layoutFieldKey && layoutFieldKey !== 'layout' && layoutFieldKey !== 'layout_icon') doc.deiconifyLayout = layoutFieldKey.replace('layout_', '');
}
export function pileup(docList: Doc[], x?: number, y?: number, size: number = 55, create: boolean = true) {
runInAction(() => {
- docList.forEach((d, i) => {
+ docList.forEach((doc, i) => {
+ const d = doc;
DocUtils.iconify(d);
d.x = Math.cos((Math.PI * 2 * i) / docList.length) * size - size;
d.y = Math.sin((Math.PI * 2 * i) / docList.length) * size - size;
@@ -1880,6 +1899,7 @@ export namespace DocUtils {
newCollection._width = newCollection._height = size * 2;
return newCollection;
}
+ return undefined;
}
export function makeIntoPortal(doc: Doc, layoutDoc: Doc, allLinks: Doc[]) {
const portalLink = allLinks.find(d => d.link_anchor_1 === doc && d.link_relationship === 'portal to:portal from');
@@ -1921,7 +1941,7 @@ export namespace DocUtils {
_timecodeToShow: Cast(doc._timecodeToShow, 'number', null),
});
Doc.AddDocToList(context, annotationField, pushpin);
- const pushpinLink = DocUtils.MakeLink(pushpin, doc, { link_relationship: 'pushpin' }, '');
+ DocUtils.MakeLink(pushpin, doc, { link_relationship: 'pushpin' }, '');
doc._timecodeToShow = undefined;
return pushpin;
}
@@ -1948,23 +1968,24 @@ export namespace DocUtils {
// }
function ConvertDMSToDD(degrees: number, minutes: number, seconds: number, direction: string) {
- var dd = degrees + minutes / 60 + seconds / (60 * 60);
+ let dd = degrees + minutes / 60 + seconds / (60 * 60);
if (direction === 'S' || direction === 'W') {
- dd = dd * -1;
+ dd *= -1;
} // Don't do anything for N or E
return dd;
}
- export function assignImageInfo(result: Upload.FileInformation, proto: Doc) {
+ export function assignImageInfo(result: Upload.FileInformation, protoIn: Doc) {
+ const proto = protoIn;
if (Upload.isImageInformation(result)) {
const maxNativeDim = Math.min(Math.max(result.nativeHeight, result.nativeWidth), defaultNativeImageDim);
const exifRotation = StrCast((result.exifData?.data as any)?.Orientation).toLowerCase();
- proto['data-nativeOrientation'] = result.exifData?.data?.image?.Orientation ?? (exifRotation.includes('rotate 90') || exifRotation.includes('rotate 270') ? 5 : undefined);
- proto['data_nativeWidth'] = result.nativeWidth < result.nativeHeight ? (maxNativeDim * result.nativeWidth) / result.nativeHeight : maxNativeDim;
- proto['data_nativeHeight'] = result.nativeWidth < result.nativeHeight ? maxNativeDim : maxNativeDim / (result.nativeWidth / result.nativeHeight);
- if (NumCast(proto['data-nativeOrientation']) >= 5) {
- proto['data_nativeHeight'] = result.nativeWidth < result.nativeHeight ? (maxNativeDim * result.nativeWidth) / result.nativeHeight : maxNativeDim;
- proto['data_nativeWidth'] = result.nativeWidth < result.nativeHeight ? maxNativeDim : maxNativeDim / (result.nativeWidth / result.nativeHeight);
+ proto.data_nativeOrientation = result.exifData?.data?.image?.Orientation ?? (exifRotation.includes('rotate 90') || exifRotation.includes('rotate 270') ? 5 : undefined);
+ proto.data_nativeWidth = result.nativeWidth < result.nativeHeight ? (maxNativeDim * result.nativeWidth) / result.nativeHeight : maxNativeDim;
+ proto.data_nativeHeight = result.nativeWidth < result.nativeHeight ? maxNativeDim : maxNativeDim / (result.nativeWidth / result.nativeHeight);
+ if (NumCast(proto.data_nativeOrientation) >= 5) {
+ proto.data_nativeHeight = result.nativeWidth < result.nativeHeight ? (maxNativeDim * result.nativeWidth) / result.nativeHeight : maxNativeDim;
+ proto.data_nativeWidth = result.nativeWidth < result.nativeHeight ? maxNativeDim : maxNativeDim / (result.nativeWidth / result.nativeHeight);
}
proto.data_exif = JSON.stringify(result.exifData?.data);
proto.data_contentSize = result.contentSize;
@@ -2033,7 +2054,7 @@ export namespace DocUtils {
const generatedDocuments: Doc[] = [];
Networking.UploadYoutubeToServer(videoId, overwriteDoc?.[Id]).then(upfiles => {
const {
- source: { newFilename, originalFilename, mimetype },
+ source: { newFilename, mimetype },
result,
} = upfiles.lastElement();
if ((result as any).message) {
@@ -2061,12 +2082,9 @@ export namespace DocUtils {
const fileNoGuidPairs: Networking.FileGuidPair[] = files.map(file => ({ file }));
const upfiles = await Networking.UploadFilesToServer(fileNoGuidPairs);
- for (const {
- source: { newFilename, mimetype },
- result,
- } of upfiles) {
+ upfiles.forEach(({ source: { newFilename, mimetype }, result }) => {
newFilename && mimetype && processFileupload(generatedDocuments, newFilename, mimetype, result, options);
- }
+ });
return generatedDocuments;
}
@@ -2091,34 +2109,115 @@ export namespace DocUtils {
export function copyDragFactory(dragFactory: Doc) {
if (!dragFactory) return undefined;
const ndoc = dragFactory.isTemplateDoc ? Doc.ApplyTemplate(dragFactory) : Doc.MakeCopy(dragFactory, true);
- if (ndoc && dragFactory['dragFactory_count'] !== undefined) {
- dragFactory['dragFactory_count'] = NumCast(dragFactory['dragFactory_count']) + 1;
- Doc.SetInPlace(ndoc, 'title', ndoc.title + ' ' + NumCast(dragFactory['dragFactory_count']).toString(), true);
+ if (ndoc && dragFactory.dragFactory_count !== undefined) {
+ dragFactory.dragFactory_count = NumCast(dragFactory.dragFactory_count) + 1;
+ Doc.SetInPlace(ndoc, 'title', ndoc.title + ' ' + NumCast(dragFactory.dragFactory_count).toString(), true);
}
return ndoc;
}
export function delegateDragFactory(dragFactory: Doc) {
const ndoc = Doc.MakeDelegateWithProto(dragFactory);
- if (ndoc && dragFactory['dragFactory_count'] !== undefined) {
- dragFactory['dragFactory_count'] = NumCast(dragFactory['dragFactory_count']) + 1;
- Doc.GetProto(ndoc).title = ndoc.title + ' ' + NumCast(dragFactory['dragFactory_count']).toString();
+ if (ndoc && dragFactory.dragFactory_count !== undefined) {
+ dragFactory.dragFactory_count = NumCast(dragFactory.dragFactory_count) + 1;
+ Doc.GetProto(ndoc).title = ndoc.title + ' ' + NumCast(dragFactory.dragFactory_count).toString();
}
return ndoc;
}
+
+ export async function Zip(doc: Doc, zipFilename = 'dashExport.zip') {
+ const { clone, map, linkMap } = await Doc.MakeClone(doc);
+ const proms = new Set();
+ function replacer(key: any, value: any) {
+ if (key && ['branchOf', 'cloneOf', 'cursors'].includes(key)) return undefined;
+ if (value?.__type === 'image') {
+ const extension = value.url.replace(/.*\./, '');
+ proms.add(value.url.replace('.' + extension, '_o.' + extension));
+ return SerializationHelper.Serialize(new ImageField(value.url));
+ }
+ if (value?.__type === 'pdf') {
+ proms.add(value.url);
+ return SerializationHelper.Serialize(new PdfField(value.url));
+ }
+ if (value?.__type === 'audio') {
+ proms.add(value.url);
+ return SerializationHelper.Serialize(new AudioField(value.url));
+ }
+ if (value?.__type === 'video') {
+ proms.add(value.url);
+ return SerializationHelper.Serialize(new VideoField(value.url));
+ }
+ if (
+ value instanceof Doc ||
+ value instanceof ScriptField ||
+ value instanceof RichTextField ||
+ value instanceof InkField ||
+ value instanceof CsvField ||
+ value instanceof WebField ||
+ value instanceof DateField ||
+ value instanceof ProxyField ||
+ value instanceof ComputedField
+ ) {
+ return SerializationHelper.Serialize(value);
+ }
+ if (value instanceof Array && key !== ListFieldName && key !== InkDataFieldName) return { fields: value, __type: 'list' };
+ return value;
+ }
+
+ const docs: { [id: string]: any } = {};
+ const links: { [id: string]: any } = {};
+ Array.from(map.entries()).forEach(f => (docs[f[0]] = f[1]));
+ Array.from(linkMap.entries()).forEach(l => (links[l[0]] = l[1]));
+ const jsonDocs = JSON.stringify({ id: clone[Id], docs, links }, decycle(replacer));
+
+ const zip = new JSZip();
+ var count = 0;
+ const promArr = Array.from(proms)
+ .filter(url => url?.startsWith('/files'))
+ .map(url => url.replace('/', '')); // window.location.origin));
+ console.log(promArr.length);
+ if (!promArr.length) {
+ zip.file('docs.json', jsonDocs);
+ zip.generateAsync({ type: 'blob' }).then(content => saveAs(content, zipFilename));
+ } else
+ promArr.forEach((url, i) => {
+ // loading a file and add it in a zip file
+ JSZipUtils.getBinaryContent(window.location.origin + '/' + url, (err: any, data: any) => {
+ if (err) throw err; // or handle the error
+ // // Generate a directory within the Zip file structure
+ // const assets = zip.folder("assets");
+ // assets.file(filename, data, {binary: true});
+ const assetPathOnServer = promArr[i].replace(window.location.origin + '/', '').replace(/\//g, '%%%');
+ zip.file(assetPathOnServer, data, { binary: true });
+ console.log(' => ' + url);
+ if (++count === promArr.length) {
+ zip.file('docs.json', jsonDocs);
+ zip.generateAsync({ type: 'blob' }).then(content => saveAs(content, zipFilename));
+ // const a = document.createElement("a");
+ // const url = Utils.prepend(`/downloadId/${this.props.Document[Id]}`);
+ // a.href = url;
+ // a.download = `DocExport-${this.props.Document[Id]}.zip`;
+ // a.click();
+ }
+ });
+ });
+ }
}
ScriptingGlobals.add('Docs', Docs);
+// eslint-disable-next-line prefer-arrow-callback
ScriptingGlobals.add(function copyDragFactory(dragFactory: Doc, asDelegate?: boolean) {
return dragFactory instanceof Doc ? (asDelegate ? DocUtils.delegateDragFactory(dragFactory) : DocUtils.copyDragFactory(dragFactory)) : dragFactory;
});
+// eslint-disable-next-line prefer-arrow-callback
ScriptingGlobals.add(function makeDelegate(proto: any) {
const d = Docs.Create.DelegateDocument(proto, { title: 'child of ' + proto.title });
return d;
});
+// eslint-disable-next-line prefer-arrow-callback
ScriptingGlobals.add(function generateLinkTitle(link: Doc) {
- const link_anchor_1title = link.link_anchor_1 && link.link_anchor_1 !== link ? Cast(link.link_anchor_1, Doc, null)?.title : '>';
- const link_anchor_2title = link.link_anchor_2 && link.link_anchor_2 !== link ? Cast(link.link_anchor_2, Doc, null)?.title : '>';
+ const linkAnchor1title = link.link_anchor_1 && link.link_anchor_1 !== link ? Cast(link.link_anchor_1, Doc, null)?.title : '>';
+ const linkAnchor2title = link.link_anchor_2 && link.link_anchor_2 !== link ? Cast(link.link_anchor_2, Doc, null)?.title : '>';
const relation = link.link_relationship || 'to';
- return `${link_anchor_1title} (${relation}) ${link_anchor_2title}`;
+ return `${linkAnchor1title} (${relation}) ${linkAnchor2title}`;
});
diff --git a/src/client/util/CaptureManager.tsx b/src/client/util/CaptureManager.tsx
index 8451357ef..2939ba581 100644
--- a/src/client/util/CaptureManager.tsx
+++ b/src/client/util/CaptureManager.tsx
@@ -2,7 +2,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { action, computed, makeObservable, observable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
-import { addStyleSheet } from '../../Utils';
+import { addStyleSheet } from '../../ClientUtils';
import { Doc } from '../../fields/Doc';
import { DocCast, StrCast } from '../../fields/Types';
import { LightboxView } from '../views/LightboxView';
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 081115879..7811f8605 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -1,6 +1,6 @@
import { observable, reaction, runInAction } from "mobx";
import * as rp from 'request-promise';
-import { OmitKeys, Utils } from "../../Utils";
+import { ClientUtils, OmitKeys } from "../../ClientUtils";
import { Doc, DocListCast, DocListCastAsync, Opt } from "../../fields/Doc";
import { DocData } from "../../fields/DocSymbols";
import { InkTool } from "../../fields/InkField";
@@ -12,7 +12,7 @@ import { ScriptField } from "../../fields/ScriptField";
import { Cast, DateCast, DocCast, StrCast } from "../../fields/Types";
import { WebField, nullAudio } from "../../fields/URLField";
import { SetCachedGroups, SharingPermissions } from "../../fields/util";
-import { GestureUtils } from "../../pen-gestures/GestureUtils";
+import { Gestures } from "../../pen-gestures/GestureTypes";
import { DocServer } from "../DocServer";
import { CollectionViewType, DocumentType } from "../documents/DocumentTypes";
import { DocUtils, Docs, DocumentOptions, FInfo, FInfoFieldType } from "../documents/Documents";
@@ -20,20 +20,21 @@ import { DashboardView } from "../views/DashboardView";
import { OverlayView } from "../views/OverlayView";
import { CollectionTreeView, TreeViewType } from "../views/collections/CollectionTreeView";
import { Colors } from "../views/global/globalEnums";
-import { media_state } from "../views/nodes/AudioBox";
+import { mediaState } from "../views/nodes/AudioBox";
import { OpenWhere } from "../views/nodes/DocumentView";
import { ButtonType, FontIconBox } from "../views/nodes/FontIconBox/FontIconBox";
+import { ImageBox } from "../views/nodes/ImageBox";
+import { LabelBox } from "../views/nodes/LabelBox";
import { ImportElementBox } from "../views/nodes/importBox/ImportElementBox";
-import { DragManager, dropActionType } from "./DragManager";
+import { DragManager } from "./DragManager";
+import { dropActionType } from "./DropActionTypes";
import { MakeTemplate } from "./DropConverter";
import { FollowLinkScript } from "./LinkFollower";
-import { LinkManager } from "./LinkManager";
+import { LinkManager, UPDATE_SERVER_CACHE } from "./LinkManager";
import { ScriptingGlobals } from "./ScriptingGlobals";
import { ColorScheme } from "./SettingsManager";
import { SnappingManager } from "./SnappingManager";
import { UndoManager } from "./UndoManager";
-import { LabelBox } from "../views/nodes/LabelBox";
-import { ImageBox } from "../views/nodes/ImageBox";
interface Button {
// DocumentOptions fields a button can set
@@ -60,6 +61,7 @@ interface Button {
subMenu?: Button[];
}
+// eslint-disable-next-line import/no-mutable-exports
export let resolvedPorts: { server: number, socket: number };
export class CurrentUserUtils {
@@ -90,6 +92,7 @@ export class CurrentUserUtils {
});
const reqdOpts:DocumentOptions = { title: "child click editors", _height:75, isSystem: true};
+ // eslint-disable-next-line no-return-assign
return DocUtils.AssignOpts(tempClicks, reqdOpts, reqdClickList) ?? (doc[field] = Docs.Create.TreeDocument(reqdClickList, reqdOpts));
}
@@ -114,6 +117,7 @@ export class CurrentUserUtils {
});
const reqdOpts:DocumentOptions = {title: "click editor templates", _height:75, isSystem: true};
+ // eslint-disable-next-line no-return-assign
return DocUtils.AssignOpts(tempClicks, reqdOpts, reqdClickList) ?? (doc[field] = Docs.Create.TreeDocument(reqdClickList, reqdOpts));
}
@@ -131,6 +135,7 @@ export class CurrentUserUtils {
}), ... DocListCast(tempNotes?.data).filter(note => !reqdTempOpts.find(reqd => reqd.title === note.title))];
const reqdOpts:DocumentOptions = { title: "Note Layouts", _height: 75, isSystem: true };
+ // eslint-disable-next-line no-return-assign
return DocUtils.AssignOpts(tempNotes, reqdOpts, reqdNoteList) ?? (doc[field] = Docs.Create.TreeDocument(reqdNoteList, reqdOpts));
}
static setupUserTemplates(doc: Doc, field="template_user") {
@@ -138,6 +143,7 @@ export class CurrentUserUtils {
const reqdUserList = DocListCast(tempUsers?.data);
const reqdOpts:DocumentOptions = { title: "User Layouts", _height: 75, isSystem: true };
+ // eslint-disable-next-line no-return-assign
return DocUtils.AssignOpts(tempUsers, reqdOpts, reqdUserList) ?? (doc[field] = Docs.Create.TreeDocument(reqdUserList, reqdOpts));
}
@@ -159,24 +165,26 @@ export class CurrentUserUtils {
const reqdOpts = { title: "icon templates", _height: 75, isSystem: true };
const templateIconsDoc = DocUtils.AssignOpts(DocCast(doc[field]), reqdOpts) ?? (doc[field] = Docs.Create.TreeDocument([], reqdOpts));
+ const labelBox = (opts: DocumentOptions, fieldKey:string) => Docs.Create.LabelDocument({
+ layout: LabelBox.LayoutString(fieldKey), textTransform: "unset", letterSpacing: "unset", _singleLine: false, _label_minFontSize: 14, _label_maxFontSize: 14, layout_borderRounding: "5px", _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, ...opts
+ });
+ const imageBox = (opts: DocumentOptions, fieldKey:string) => Docs.Create.ImageDocument( "http://www.cs.brown.edu/~bcz/noImage.png", { layout:ImageBox.LayoutString(fieldKey), "icon_nativeWidth": 360 / 4, "icon_nativeHeight": 270 / 4, iconTemplate:DocumentType.IMG, _width: 360 / 4, _height: 270 / 4, _layout_showTitle: "title", ...opts });
+ const fontBox = (opts:DocumentOptions, fieldKey:string) => Docs.Create.FontIconDocument({ layout:FontIconBox.LayoutString(fieldKey), _nativeHeight: 30, _nativeWidth: 30, _width: 30, _height: 30, ...opts });
+
const makeIconTemplate = (type: DocumentType | undefined, templateField: string, opts:DocumentOptions) => {
const title = "icon" + (type ? "_" + type : "");
const curIcon = DocCast(templateIconsDoc[title]);
- let creator = labelBox;
- switch (opts.iconTemplate) {
- case DocumentType.IMG : creator = imageBox; break;
- case DocumentType.FONTICON: creator = fontBox; break;
- }
+ const creator = (() => { switch (opts.iconTemplate) {
+ case DocumentType.IMG : return imageBox;
+ case DocumentType.FONTICON: return fontBox;
+ default: return labelBox;
+ }})();
const allopts = {isSystem: true, onClickScriptDisable: "never", ...opts, title};
+ // eslint-disable-next-line no-return-assign
return DocUtils.AssignScripts( (curIcon?.iconTemplate === opts.iconTemplate ?
DocUtils.AssignOpts(curIcon, allopts):undefined) ?? ((templateIconsDoc[title] = MakeTemplate(creator(allopts, templateField)))),
{onClick:"deiconifyView(documentView)", onDoubleClick: "deiconifyViewToLightbox(documentView)", });
};
- const labelBox = (opts: DocumentOptions, fieldKey:string) => Docs.Create.LabelDocument({
- layout: LabelBox.LayoutString(fieldKey), textTransform: "unset", letterSpacing: "unset", _singleLine: false, _label_minFontSize: 14, _label_maxFontSize: 14, layout_borderRounding: "5px", _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, ...opts
- });
- const imageBox = (opts: DocumentOptions, fieldKey:string) => Docs.Create.ImageDocument( "http://www.cs.brown.edu/~bcz/noImage.png", { layout:ImageBox.LayoutString(fieldKey), "icon_nativeWidth": 360 / 4, "icon_nativeHeight": 270 / 4, iconTemplate:DocumentType.IMG, _width: 360 / 4, _height: 270 / 4, _layout_showTitle: "title", ...opts });
- const fontBox = (opts:DocumentOptions, fieldKey:string) => Docs.Create.FontIconDocument({ layout:FontIconBox.LayoutString(fieldKey), _nativeHeight: 30, _nativeWidth: 30, _width: 30, _height: 30, ...opts });
const iconTemplates = [
makeIconTemplate(undefined, "title", { iconTemplate:DocumentType.LABEL, backgroundColor: "dimgray"}),
makeIconTemplate(DocumentType.AUDIO, "title", { iconTemplate:DocumentType.LABEL, backgroundColor: "lightgreen"}),
@@ -188,9 +196,9 @@ export class CurrentUserUtils {
makeIconTemplate(DocumentType.COL, "icon", { iconTemplate:DocumentType.IMG}),
makeIconTemplate(DocumentType.VID, "icon", { iconTemplate:DocumentType.IMG}),
makeIconTemplate(DocumentType.BUTTON,"title", { iconTemplate:DocumentType.FONTICON}),
- //nasty hack .. templates are looked up exclusively by type -- but we want a template for a document with a certain field (transcription) .. so this hack and the companion hack in createCustomView does this for now
+ // nasty hack .. templates are looked up exclusively by type -- but we want a template for a document with a certain field (transcription) .. so this hack and the companion hack in createCustomView does this for now
makeIconTemplate("transcription" as any, "transcription", { iconTemplate:DocumentType.LABEL, backgroundColor: "orange" }),
- //makeIconTemplate(DocumentType.PDF, "icon", {iconTemplate:DocumentType.IMG}, (opts) => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", opts))
+ // makeIconTemplate(DocumentType.PDF, "icon", {iconTemplate:DocumentType.IMG}, (opts) => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", opts))
].filter(d => d).map(d => d!);
DocUtils.AssignOpts(DocCast(doc[field]), {}, iconTemplates);
}
@@ -241,14 +249,15 @@ export class CurrentUserUtils {
Docs.Create.TextDocument("", { title: "text", _layout_fitWidth:true, _height: 100, isSystem: true, _text_fontFamily: StrCast(Doc.UserDoc().fontFamily), _text_fontSize: StrCast(Doc.UserDoc().fontSize) })
], {...opts, title: "Slide View Template"}));
const plotlyApi = () => {
- var plotly = Doc.MyPublishedDocs.find(doc => doc.title === "@plotly");
+ let plotly = Doc.MyPublishedDocs.find(doc => doc.title === "@plotly");
if (!plotly) {
- const plotly = Docs.Create.TextDocument(
+ plotly = Docs.Create.TextDocument(
`await import("https://cdn.plot.ly/plotly-2.27.0.min.js");
Plotly.newPlot(dashDiv.id, [ --DOCDATA-- ])`
, {title: "@plotly", title_custom: true, _layout_showTitle:"title", _width:300,_height:400});
Doc.AddToMyPublished(plotly);
}
+ return plotly;
}
const plotlyView = (opts:DocumentOptions) => {
const rtfield = new RichTextField(JSON.stringify(
@@ -280,10 +289,10 @@ export class CurrentUserUtils {
return slide;
}
const mermaidsApi = () => {
- var mermaids = Doc.MyPublishedDocs.find(doc => doc.title === "@mermaids");
+ let mermaids = Doc.MyPublishedDocs.find(doc => doc.title === "@mermaids");
if (!mermaids) {
- const mermaids = Docs.Create.TextDocument(
- `const mdef = (await import("https://cdn.jsdelivr.net/npm/mermaid\@10.8.0/dist/mermaid.esm.min.mjs")).default;
+ mermaids = Docs.Create.TextDocument(
+ `const mdef = (await import("https://cdn.jsdelivr.net/npm/mermaid@10.8.0/dist/mermaid.esm.min.mjs")).default;
window["callb"] = (x) => {
alert(x);
}
@@ -301,6 +310,7 @@ export class CurrentUserUtils {
, {title: "@mermaids", title_custom: true, _layout_showTitle:"title", _width:300,_height:400});
Doc.AddToMyPublished(mermaids);
}
+ return mermaids;
}
const mermaidsView = (opts:DocumentOptions) => {
const rtfield = new RichTextField(JSON.stringify(
@@ -333,7 +343,7 @@ pie title Minerals in my tap water
slide.onPaint = ScriptField.MakeScript(`toggleDetail(documentView, "textPainted")`, {documentView:"any"});
return slide;
}
- const apis = [plotlyApi(), mermaidsApi()]
+ plotlyApi(); mermaidsApi();
const emptyThings:{key:string, // the field name where the empty thing will be stored
opts:DocumentOptions, // the document options that are required for the empty thing
funcs?:{[key:string]: any}, // computed fields that are rquired for the empth thing
@@ -465,83 +475,9 @@ pie title Minerals in my tap water
return DocUtils.AssignDocField(doc, field, (opts, items) => Docs.Create.StackingDocument(items??[], opts), reqdStackOpts, menuBtns, { dropConverter: "convertToButtons(dragData)" });
}
- // Sets up mobile menu if it is undefined creates a new one, otherwise returns existing menu
- static setupActiveMobileMenu(doc: Doc, field="activeMobileMenu") {
- const reqdOpts = { _width: 980, ignoreClick: true, _lockedPosition: false, title: "home", _yMargin: 100, isSystem: true, _chromeHidden: true,};
- DocUtils.AssignDocField(doc, field, (opts, items) => Docs.Create.StackingDocument(this.setupMobileButtons(), opts), reqdOpts);
- }
-
- // Sets up mobile buttons for inside mobile menu
- static setupMobileButtons(doc?: Doc, buttons?: string[]) {
- return [];
- const docProtoData: { title: string, icon: string, drag?: string, ignoreClick?: boolean, click?: string, backgroundColor?: string, info: string, dragFactory?: Doc }[] = [
- { title: "DASHBOARDS", icon: "bars", click: 'switchToMobileLibrary()', backgroundColor: "lightgrey", info: "Access your Dashboards from your mobile, and navigate through all of your documents. " },
- { title: "UPLOAD", icon: "upload", click: 'openMobileUploads()', backgroundColor: "lightgrey", info: "Upload files from your mobile device so they can be accessed on Dash Web." },
- { title: "MOBILE UPLOAD", icon: "mobile", click: 'switchToMobileUploadCollection()', backgroundColor: "lightgrey", info: "Access the collection of your mobile uploads." },
- { title: "RECORD", icon: "microphone", click: 'openMobileAudio()', backgroundColor: "lightgrey", info: "Use your phone to record, dictate and then upload audio onto Dash Web." },
- { title: "PRESENTATION", icon: "desktop", click: 'switchToMobilePresentation()', backgroundColor: "lightgrey", info: "Use your phone as a remote for you presentation." },
- { title: "SETTINGS", icon: "cog", click: 'openMobileSettings()', backgroundColor: "lightgrey", info: "Change your password, log out, or manage your account security." }
- ];
- // returns a list of mobile buttons
- return docProtoData.filter(d => !buttons || !buttons.includes(d.title)).map(data =>
- this.mobileButton({
- title: data.title,
- _lockedPosition: true,
- onClick: data.click ? ScriptField.MakeScript(data.click) : undefined,
- backgroundColor: data.backgroundColor, isSystem: true
- },
- [this.createToolButton({ ignoreClick: true, icon: data.icon, backgroundColor: "rgba(0,0,0,0)", isSystem: true, btnType: ButtonType.ClickButton, }), this.mobileTextContainer({}, [this.mobileButtonText({}, data.title), this.mobileButtonInfo({}, data.info)])])
- );
- }
-
- // sets up the main document for the mobile button
- static mobileButton = (opts: DocumentOptions, docs: Doc[]) => Docs.Create.MulticolumnDocument(docs, {
- ...opts,
- _nativeWidth: 900, _nativeHeight: 250, _width: 900, _height: 250, _yMargin: 15,
- layout_borderRounding: "5px", layout_boxShadow: "0 0", isSystem: true
- }) as any as Doc
-
- // sets up the text container for the information contained within the mobile button
- static mobileTextContainer = (opts: DocumentOptions, docs: Doc[]) => Docs.Create.MultirowDocument(docs, {
- ...opts,
- _nativeWidth: 450, _nativeHeight: 250, _width: 450, _height: 250, _yMargin: 25,
- backgroundColor: "rgba(0,0,0,0)", layout_borderRounding: "0", layout_boxShadow: "0 0", ignoreClick: true, isSystem: true
- }) as any as Doc
-
- // Sets up the title of the button
- static mobileButtonText = (opts: DocumentOptions, buttonTitle: string) => Docs.Create.TextDocument(buttonTitle, {
- ...opts,
- title: buttonTitle, _text_fontSize: "37px", _xMargin: 0, _yMargin: 0, ignoreClick: true, backgroundColor: "rgba(0,0,0,0)", isSystem: true
- }) as any as Doc
-
- // Sets up the description of the button
- static mobileButtonInfo = (opts: DocumentOptions, buttonInfo: string) => Docs.Create.TextDocument(buttonInfo, {
- ...opts,
- title: "info", _text_fontSize: "25px", _xMargin: 0, _yMargin: 0, ignoreClick: true, backgroundColor: "rgba(0,0,0,0)", _dimMagnitude: 2, isSystem: true
- }) as any as Doc
-
-
-
- static setupMobileInkingDoc(userDoc: Doc) {
- return Docs.Create.FreeformDocument([], { title: "Mobile Inking", backgroundColor: "white", isSystem: true });
- }
-
- static setupMobileUploadDoc(userDoc: Doc) {
- // const addButton = Docs.Create.FontIconDocument({ onDragStart: ScriptField.MakeScript('addWebToMobileUpload()'), title: "Add Web Doc to Upload Collection", icon: "plus", backgroundColor: "black" })
- const webDoc = Docs.Create.WebDocument("https://www.britannica.com/biography/Miles-Davis", {
- title: "Upload Images From the Web", _lockedPosition: true, isSystem: true
- });
- const uploadDoc = Docs.Create.StackingDocument([], {
- title: "Mobile Upload Collection", backgroundColor: "white", _lockedPosition: true, isSystem: true, _chromeHidden: true,
- });
- return Docs.Create.StackingDocument([webDoc, uploadDoc], {
- _width: screen.width, _lockedPosition: true, title: "Upload", _layout_autoHeight: true, _yMargin: 80, backgroundColor: "lightgray", isSystem: true, _chromeHidden: true,
- });
- }
-
/// Search option on the left side button panel
static setupSearcher(doc: Doc, field:string) {
- return DocUtils.AssignDocField(doc, field, (opts, items) => Docs.Create.SearchDocument(opts), {
+ return DocUtils.AssignDocField(doc, field, (opts) => Docs.Create.SearchDocument(opts), {
dontRegisterView: true, backgroundColor: "dimgray", ignoreClick: true, title: "Search Panel", isSystem: true, childDragAction: dropActionType.embed,
_lockedPosition: true, _type_collection: CollectionViewType.Schema });
}
@@ -552,7 +488,7 @@ pie title Minerals in my tap water
const creatorBtns = CurrentUserUtils.setupCreatorButtons(doc, allTools?.length ? allTools[0]:undefined);
const userTools = allTools && allTools?.length > 1 ? allTools[1]:undefined;
const userBtns = CurrentUserUtils.setupUserDocumentCreatorButtons(doc, userTools);
- //doc.myUserBtns = new PrefetchProxy(userBtns);
+ // doc.myUserBtns = new PrefetchProxy(userBtns);
const reqdToolOps:DocumentOptions = {
title: "My Tools", isSystem: true, ignoreClick: true, layout_boxShadow: "0 0",
layout_explainer: "This is a palette of documents that can be created.",
@@ -563,7 +499,7 @@ pie title Minerals in my tap water
/// initializes the left sidebar dashboard pane
static setupDashboards(doc: Doc, field:string) {
- var myDashboards = DocCast(doc[field]);
+ let myDashboards = DocCast(doc[field]);
const newDashboard = `createNewDashboard()`;
@@ -572,9 +508,9 @@ pie title Minerals in my tap water
const reqdBtnScript = {onClick: newDashboard,}
const newDashboardButton = DocUtils.AssignScripts(DocUtils.AssignOpts(DocCast(myDashboards?.layout_headerButton), reqdBtnOpts) ?? Docs.Create.FontIconDocument(reqdBtnOpts), reqdBtnScript);
- const contextMenuScripts = [/*newDashboard*/] as string[];
- const contextMenuLabels = [/*"Create New Dashboard"*/] as string[];
- const contextMenuIcons = [/*"plus"*/] as string[];
+ const contextMenuScripts = [/* newDashboard */] as string[];
+ const contextMenuLabels = [/* "Create New Dashboard" */] as string[];
+ const contextMenuIcons = [/* "plus" */] as string[];
const childContextMenuScripts = [`toggleComicMode()`, `snapshotDashboard()`, `shareDashboard(this)`, 'removeDashboard(this)', 'resetDashboard(this)']; // entries must be kept in synch with childContextMenuLabels, childContextMenuIcons, and childContextMenuFilters
const childContextMenuFilters = ['!IsNoviceMode()', '!IsNoviceMode()', undefined as any, undefined as any, '!IsNoviceMode()'];// entries must be kept in synch with childContextMenuLabels, childContextMenuIcons, and childContextMenuScripts
const childContextMenuLabels = ["Toggle Comic Mode", "Snapshot Dashboard", "Share Dashboard", "Remove Dashboard", "Reset Dashboard"];// entries must be kept in synch with childContextMenuScripts, childContextMenuIcons, and childContextMenuFilters
@@ -605,7 +541,7 @@ pie title Minerals in my tap water
/// initializes the left sidebar File system pane
static setupFilesystem(doc: Doc, field:string) {
- var myFilesystem = DocCast(doc[field]);
+ const myFilesystem = DocCast(doc[field]);
const newFolderOpts: DocumentOptions = {
_forceActive: true, _dragOnlyWithinContainer: true, _embedContainer: Doc.MyFilesystem, _width: 30, _height: 30, undoIgnoreFields:new List(['treeView_SortCriterion']),
@@ -650,7 +586,7 @@ pie title Minerals in my tap water
/// initializes the left sidebar panel view of the UserDoc
static setupUserDocView(doc: Doc, field:string) {
const reqdOpts:DocumentOptions = {
- _lockedPosition: true, _gridGap: 5, _forceActive: true, title: Doc.CurrentUserEmail +"-view",
+ _lockedPosition: true, _gridGap: 5, _forceActive: true, title: ClientUtils.CurrentUserEmail +"-view",
layout_boxShadow: "0 0", childDontRegisterViews: true, dropAction: dropActionType.same, ignoreClick: true, isSystem: true,
treeView_HideTitle: true, treeView_TruncateTitleWidth: 350
};
@@ -671,7 +607,7 @@ pie title Minerals in my tap water
static createToolButton = (opts: DocumentOptions) => Docs.Create.FontIconDocument({
btnType: ButtonType.ToolButton, _dropPropertiesToRemove: new List([ "layout_hideContextMenu"]),
- /*_nativeWidth: 40, _nativeHeight: 40, */ _width: 40, _height: 40, isSystem: true, ...opts,
+ /* _nativeWidth: 40, _nativeHeight: 40, */ _width: 40, _height: 40, isSystem: true, ...opts,
})
/// initializes the required buttons in the expanding button menu at the bottom of the Dash window
@@ -694,8 +630,8 @@ pie title Minerals in my tap water
title: "docked buttons", _height: 40, flexGap: 0, layout_boxShadow: "standard", childDragAction: dropActionType.move,
childDontRegisterViews: true, linearView_IsOpen: true, linearView_Expandable: true, ignoreClick: true
};
- reaction(() => UndoManager.redoStack.slice(), () => Doc.GetProto(btns.find(btn => btn.title === "Redo")!).opacity = UndoManager.CanRedo() ? 1 : 0.4, { fireImmediately: true });
- reaction(() => UndoManager.undoStack.slice(), () => Doc.GetProto(btns.find(btn => btn.title === "Undo")!).opacity = UndoManager.CanUndo() ? 1 : 0.4, { fireImmediately: true });
+ reaction(() => UndoManager.redoStack.slice(), () => { Doc.GetProto(btns.find(btn => btn.title === "Redo")!).opacity = UndoManager.CanRedo() ? 1 : 0.4; }, { fireImmediately: true });
+ reaction(() => UndoManager.undoStack.slice(), () => { Doc.GetProto(btns.find(btn => btn.title === "Undo")!).opacity = UndoManager.CanUndo() ? 1 : 0.4; }, { fireImmediately: true });
return DocUtils.AssignDocField(doc, field, (opts, items) => this.linearButtonList(opts, items??[]), dockBtnsReqdOpts, btns);
}
@@ -752,9 +688,9 @@ pie title Minerals in my tap water
{ title: "Pen", toolTip: "Pen (Ctrl+P)", btnType: ButtonType.ToggleButton, icon: "pen-nib", toolType: "pen", scripts: {onClick:'{ return setActiveTool(this.toolType, false, _readOnly_);}' }},
{ title: "Write", toolTip: "Write (Ctrl+Shift+P)", btnType: ButtonType.ToggleButton, icon: "pen", toolType: "write", scripts: {onClick:'{ return setActiveTool(this.toolType, false, _readOnly_);}' }, funcs: {hidden:"IsNoviceMode()" }},
{ title: "Eraser", toolTip: "Eraser (Ctrl+E)", btnType: ButtonType.ToggleButton, icon: "eraser", toolType: "eraser", scripts: {onClick:'{ return setActiveTool(this.toolType, false, _readOnly_);}' }, funcs: {hidden:"IsNoviceMode()" }},
- { title: "Circle", toolTip: "Circle (double tap to lock mode)", btnType: ButtonType.ToggleButton, icon: "circle", toolType:GestureUtils.Gestures.Circle, scripts: {onClick:`{ return setActiveTool(this.toolType, false, _readOnly_);}`, onDoubleClick:`{ return setActiveTool(this.toolType, true, _readOnly_);}`} },
- { title: "Square", toolTip: "Square (double tap to lock mode)", btnType: ButtonType.ToggleButton, icon: "square", toolType:GestureUtils.Gestures.Rectangle, scripts: {onClick:`{ return setActiveTool(this.toolType, false, _readOnly_);}`, onDoubleClick:`{ return setActiveTool(this.toolType, true, _readOnly_);}`} },
- { title: "Line", toolTip: "Line (double tap to lock mode)", btnType: ButtonType.ToggleButton, icon: "minus", toolType:GestureUtils.Gestures.Line, scripts: {onClick:`{ return setActiveTool(this.toolType, false, _readOnly_);}`, onDoubleClick:`{ return setActiveTool(this.toolType, true, _readOnly_);}`} },
+ { title: "Circle", toolTip: "Circle (double tap to lock mode)", btnType: ButtonType.ToggleButton, icon: "circle", toolType: Gestures.Circle, scripts: {onClick:`{ return setActiveTool(this.toolType, false, _readOnly_);}`, onDoubleClick:`{ return setActiveTool(this.toolType, true, _readOnly_);}`} },
+ { title: "Square", toolTip: "Square (double tap to lock mode)", btnType: ButtonType.ToggleButton, icon: "square", toolType: Gestures.Rectangle, scripts: {onClick:`{ return setActiveTool(this.toolType, false, _readOnly_);}`, onDoubleClick:`{ return setActiveTool(this.toolType, true, _readOnly_);}`} },
+ { title: "Line", toolTip: "Line (double tap to lock mode)", btnType: ButtonType.ToggleButton, icon: "minus", toolType: Gestures.Line, scripts: {onClick:`{ return setActiveTool(this.toolType, false, _readOnly_);}`, onDoubleClick:`{ return setActiveTool(this.toolType, true, _readOnly_);}`} },
{ title: "Mask", toolTip: "Mask", btnType: ButtonType.ToggleButton, icon: "user-circle",toolType: "inkMask", scripts: {onClick:'{ return setInkProperty(this.toolType, value, _readOnly_);}'}, funcs: {hidden:"IsNoviceMode()" } },
{ title: "Labels", toolTip: "Lab els", btnType: ButtonType.ToggleButton, icon: "text-width", toolType: "labels", scripts: {onClick:'{ return setInkProperty(this.toolType, value, _readOnly_);}'}, },
{ title: "Width", toolTip: "Stroke width", btnType: ButtonType.NumberSliderButton, toolType: "strokeWidth", ignoreClick: true, scripts: {script: '{ return setInkProperty(this.toolType, value, _readOnly_);}'}, numBtnMin: 1},
@@ -835,13 +771,13 @@ pie title Minerals in my tap water
static setupContextMenuBtn(params:Button, menuDoc:Doc):Doc {
const menuBtnDoc = DocListCast(menuDoc?.data).find( doc => doc.title === params.title);
- const subMenu = params.subMenu;
+ const {subMenu} = params;
if (!subMenu) { // button does not have a sub menu
return this.setupContextMenuButton(params, menuBtnDoc, menuDoc);
}
// linear view
const reqdSubMenuOpts = { ...OmitKeys(params, ["scripts", "funcs", "subMenu"]).omit, undoIgnoreFields: new List(['width', "linearView_IsOpen"]),
- childDontRegisterViews: true, flexGap: 0, _height: 30, ignoreClick: params.scripts?.onClick ? false : true,
+ childDontRegisterViews: true, flexGap: 0, _height: 30, ignoreClick: !params.scripts?.onClick,
linearView_SubMenu: true, linearView_Expandable: true, embedContainer: menuDoc};
const items = (menuBtnDoc?:Doc) => !menuBtnDoc ? [] : subMenu.map(sub => this.setupContextMenuBtn(sub, menuBtnDoc) );
@@ -873,10 +809,10 @@ pie title Minerals in my tap water
{ opts: { title: "Replicate",icon:"camera",toolTip: "Copy dashboard layout",btnType: ButtonType.ClickButton, expertMode: true}, scripts: { onClick: `snapshotDashboard()`}},
{ opts: { title: "Recordings", toolTip: "Workspace Recordings", btnType: ButtonType.DropdownList,expertMode: false, ignoreClick: true, width: 100}, funcs: {hidden: `false`, btnList:`getWorkspaceRecordings()`}, scripts: { script: `{ return replayWorkspace(value, _readOnly_); }`, onDragScript: `{ return startRecordingDrag(value); }`}},
{ opts: { title: "Stop Rec",icon: "stop", toolTip: "Stop recording", btnType: ButtonType.ClickButton, expertMode: false}, funcs: {hidden: `!isWorkspaceRecording()`}, scripts: { onClick: `stopWorkspaceRecording()`}},
- { opts: { title: "Play", icon: "play", toolTip: "Play recording", btnType: ButtonType.ClickButton, expertMode: false}, funcs: {hidden: `isWorkspaceReplaying() !== "${media_state.Paused}"`}, scripts: { onClick: `resumeWorkspaceReplaying(getCurrentRecording())`}},
- { opts: { title: "Pause", icon: "pause",toolTip: "Pause playback", btnType: ButtonType.ClickButton, expertMode: false}, funcs: {hidden: `isWorkspaceReplaying() !== "${media_state.Playing}"`}, scripts: { onClick: `pauseWorkspaceReplaying(getCurrentRecording())`}},
- { opts: { title: "Stop", icon: "stop", toolTip: "Stop playback", btnType: ButtonType.ClickButton, expertMode: false}, funcs: {hidden: `isWorkspaceReplaying() !== "${media_state.Paused}"`}, scripts: { onClick: `stopWorkspaceReplaying(getCurrentRecording())`}},
- { opts: { title: "Delete", icon: "trash",toolTip: "delete selected rec", btnType: ButtonType.ClickButton, expertMode: false}, funcs: {hidden: `isWorkspaceReplaying() !== "${media_state.Paused}"`}, scripts: { onClick: `removeWorkspaceReplaying(getCurrentRecording())`}}
+ { opts: { title: "Play", icon: "play", toolTip: "Play recording", btnType: ButtonType.ClickButton, expertMode: false}, funcs: {hidden: `isWorkspaceReplaying() !== "${mediaState.Paused}"`}, scripts: { onClick: `resumeWorkspaceReplaying(getCurrentRecording())`}},
+ { opts: { title: "Pause", icon: "pause",toolTip: "Pause playback", btnType: ButtonType.ClickButton, expertMode: false}, funcs: {hidden: `isWorkspaceReplaying() !== "${mediaState.Playing}"`}, scripts: { onClick: `pauseWorkspaceReplaying(getCurrentRecording())`}},
+ { opts: { title: "Stop", icon: "stop", toolTip: "Stop playback", btnType: ButtonType.ClickButton, expertMode: false}, funcs: {hidden: `isWorkspaceReplaying() !== "${mediaState.Paused}"`}, scripts: { onClick: `stopWorkspaceReplaying(getCurrentRecording())`}},
+ { opts: { title: "Delete", icon: "trash",toolTip: "delete selected rec", btnType: ButtonType.ClickButton, expertMode: false}, funcs: {hidden: `isWorkspaceReplaying() !== "${mediaState.Paused}"`}, scripts: { onClick: `removeWorkspaceReplaying(getCurrentRecording())`}}
];
const btns = btnDescs.map(desc => dockBtn({_width: desc.opts.width??30, _height: 30, defaultDoubleClick: 'ignore', undoIgnoreFields: new List(['opacity']), _dragOnlyWithinContainer: true, ...desc.opts}, desc.scripts, desc.funcs));
const dockBtnsReqdOpts:DocumentOptions = {
@@ -890,12 +826,14 @@ pie title Minerals in my tap water
return DocUtils.AssignDocField(doc, field, (opts) => Docs.Create.TreeDocument([], opts), { title: "published docs", backgroundColor: "#aca3a6", isSystem: true });
}
- /// The database of all links on all documents
+ static newAccount: boolean = false;
+
+ // The database of all links on all documents
static setupLinkDocs(doc: Doc, linkDatabaseId: string) {
- if (!(Docs.newAccount ? undefined : DocCast(doc.myLinkDatabase))) {
+ if (!(CurrentUserUtils.newAccount ? undefined : DocCast(doc.myLinkDatabase))) {
const linkDocs = new Doc(linkDatabaseId, true);
- linkDocs.title = "LINK DATABASE: " + Doc.CurrentUserEmail;
- linkDocs.author = Doc.CurrentUserEmail;
+ linkDocs.title = "LINK DATABASE: " + ClientUtils.CurrentUserEmail;
+ linkDocs.author = ClientUtils.CurrentUserEmail;
linkDocs.isSystem = true;
linkDocs.data = new List([]);
linkDocs["acl-Guest"] = SharingPermissions.Augment;
@@ -949,16 +887,17 @@ pie title Minerals in my tap water
/// Updates the UserDoc to have all required fields, docs, etc. No changes should need to be
/// written to the server if the code hasn't changed. However, choices need to be made for each Doc/field
/// whether to revert to "default" values, or to leave them as the user/system last set them.
- static updateUserDocument(doc: Doc, sharingDocumentId: string, linkDatabaseId: string) {
+ static updateUserDocument(docIn: Doc, sharingDocumentId: string, linkDatabaseId: string) {
+ const doc = docIn;
DocUtils.AssignDocField(doc, "globalGroupDatabase", () => Docs.Prototypes.MainGroupDocument(), {});
- reaction(() => DateCast(DocCast(doc.globalGroupDatabase)["data_modificationDate"]),
+ reaction(() => DateCast(DocCast(doc.globalGroupDatabase).data_modificationDate),
async () => {
const groups = await DocListCastAsync(DocCast(doc.globalGroupDatabase).data);
- const mygroups = groups?.filter(group => JSON.parse(StrCast(group.members)).includes(Doc.CurrentUserEmail)) || [];
- SetCachedGroups(["Guest", ...mygroups?.map(g => StrCast(g.title))]);
+ const mygroups = groups?.filter(group => JSON.parse(StrCast(group.members)).includes(ClientUtils.CurrentUserEmail)) || [];
+ SetCachedGroups(["Guest", ...(mygroups?.map(g => StrCast(g.title))??[])]);
}, { fireImmediately: true });
doc.isSystem ?? (doc.isSystem = true);
- doc.title ?? (doc.title = Doc.CurrentUserEmail);
+ doc.title ?? (doc.title = ClientUtils.CurrentUserEmail);
Doc.noviceMode ?? (Doc.noviceMode = true);
doc._showLabel ?? (doc._showLabel = true);
doc.textAlign ?? (doc.textAlign = "left");
@@ -971,7 +910,7 @@ pie title Minerals in my tap water
doc.activeFillColor ?? (doc.activeFillColor = "");
doc.activeArrowStart ?? (doc.activeArrowStart = "");
doc.activeArrowEnd ?? (doc.activeArrowEnd = "");
- doc.activeDash ?? (doc.activeDash == "0");
+ doc.activeDash ?? (doc.activeDash === "0");
doc.fontSize ?? (doc.fontSize = "12px");
doc.fontFamily ?? (doc.fontFamily = "Arial");
doc.fontColor ?? (doc.fontColor = "black");
@@ -988,15 +927,14 @@ pie title Minerals in my tap water
this.setupLinkDocs(doc, linkDatabaseId);
this.setupSharedDocs(doc, sharingDocumentId); // sets up the right sidebar collection for mobile upload documents and sharing
this.setupDefaultIconTemplates(doc); // creates a set of icon templates triggered by the document deoration icon
- this.setupActiveMobileMenu(doc); // sets up the current mobile menu for Dash Mobile
this.setupPublished(doc); // sets up the list doc of all docs that have been published (meaning that they can be auto-linked by typing their title into another text box)
this.setupContextMenuButtons(doc); // set up the row of buttons at the top of the dashboard that change depending on what is selected
this.setupTopbarButtons(doc);
this.setupDockedButtons(doc); // the bottom bar of font icons
this.setupLeftSidebarMenu(doc); // the left-side column of buttons that open their contents in a flyout panel on the left
this.setupDocTemplates(doc); // sets up the template menu of templates
- //this.setupFieldInfos(doc); // sets up the collection of field info descriptions for each possible DocumentOption
- DocUtils.AssignDocField(doc, "globalScriptDatabase", (opts) => Docs.Prototypes.MainScriptDocument(), {});
+ // sthis.setupFieldInfos(doc); // sets up the collection of field info descriptions for each possible DocumentOption
+ DocUtils.AssignDocField(doc, "globalScriptDatabase", () => Docs.Prototypes.MainScriptDocument(), {});
DocUtils.AssignDocField(doc, "myHeaderBar", (opts) => Docs.Create.MulticolumnDocument([], opts), { title: "My Header Bar", isSystem: true, _chromeHidden:true, layout_maxShown: 10, childLayoutFitWidth:false, childDocumentsActive:false, dropAction: dropActionType.move}); // drop down panel at top of dashboard for stashing documents
Doc.AddDocToList(Doc.MyFilesystem, undefined, Doc.MyDashboards)
@@ -1005,10 +943,11 @@ pie title Minerals in my tap water
Doc.GetProto(DocCast(Doc.UserDoc().emptyWebpage)).data = new WebField("https://www.wikipedia.org")
+ // eslint-disable-next-line no-new
new LinkManager();
- DocServer.CacheNeedsUpdate && setTimeout(DocServer.UPDATE_SERVER_CACHE, 2500);
- setInterval(DocServer.UPDATE_SERVER_CACHE, 120000);
+ DocServer.CacheNeedsUpdate && setTimeout(UPDATE_SERVER_CACHE, 2500);
+ setInterval(UPDATE_SERVER_CACHE, 120000);
return doc;
}
static setupFieldInfos(doc:Doc, field="fieldInfos") {
@@ -1032,11 +971,11 @@ pie title Minerals in my tap water
@observable public static ServerVersion: string = ';'
public static async loadCurrentUser() {
- return rp.get(Utils.prepend("/getCurrentUser")).then(async response => {
+ return rp.get(ClientUtils.prepend("/getCurrentUser")).then(async response => {
if (response) {
const result: { version: string, userDocumentId: string, sharingDocumentId: string, linkDatabaseId: string, email: string, cacheDocumentIds: string, resolvedPorts: string } = JSON.parse(response);
- runInAction(() => CurrentUserUtils.ServerVersion = result.version);
- Doc.CurrentUserEmail = result.email;
+ runInAction(() => { CurrentUserUtils.ServerVersion = result.version; });
+ ClientUtils.CurrentUserEmail = result.email;
resolvedPorts = result.resolvedPorts as any;
DocServer.init(window.location.protocol, window.location.hostname, resolvedPorts?.socket, result.email);
if (result.cacheDocumentIds)
@@ -1044,13 +983,13 @@ pie title Minerals in my tap water
const ids = result.cacheDocumentIds.split(";");
const batch = 30000;
for (let i = 0; i < ids.length; i += batch) {
+ // eslint-disable-next-line no-await-in-loop
await DocServer.GetRefFields(ids.slice(i, i+batch));
}
}
return result;
- } else {
- throw new Error("There should be a user! Why does Dash think there isn't one?");
- }
+ }
+ throw new Error("There should be a user! Why does Dash think there isn't one?");
});
}
@@ -1060,12 +999,12 @@ pie title Minerals in my tap water
linkDatabaseId: string;
}) {
return DocServer.GetRefField(info.userDocumentId).then(async field => {
- Docs.newAccount = !(field instanceof Doc);
+ CurrentUserUtils.newAccount = !(field instanceof Doc);
await Docs.Prototypes.initialize();
- const userDoc = Docs.newAccount ? new Doc(info.userDocumentId, true) : field as Doc;
+ const userDoc = CurrentUserUtils.newAccount ? new Doc(info.userDocumentId, true) : field as Doc;
this.updateUserDocument(Doc.SetUserDoc(userDoc), info.sharingDocumentId, info.linkDatabaseId);
- if (Docs.newAccount) {
- if (Doc.CurrentUserEmail === "guest") {
+ if (CurrentUserUtils.newAccount) {
+ if (ClientUtils.CurrentUserEmail === "guest") {
DashboardView.createNewDashboard(undefined, "guest dashboard");
} else {
userDoc.activePage = "home";
@@ -1082,14 +1021,10 @@ pie title Minerals in my tap water
input.type = "file";
input.multiple = true;
input.accept = ".zip, application/pdf, video/*, image/*, audio/*";
- input.onchange = async _e => {
+ input.onchange = async () => {
const file = input.files?.[0];
if (file?.type === 'application/zip' || file?.type === 'application/x-zip-compressed') {
const doc = await Doc.importDocument(file);
- // NOT USING SOLR, so need to replace this with something else // if (doc instanceof Doc) {
- // setTimeout(() => SearchUtil.Search(`{!join from=id to=proto_i}id:link*`, true, {}).then(docs =>
- // docs.docs.forEach(d => LinkManager.Instance.addLink(d))), 2000); // need to give solr some time to update so that this query will find any link docs we've added.
- // }
const list = Cast(Doc.MyImports.data, listSpec(Doc), null);
doc instanceof Doc && list?.splice(0, 0, doc);
} else if (input.files && input.files.length !== 0) {
@@ -1109,10 +1044,17 @@ pie title Minerals in my tap water
}
}
-ScriptingGlobals.add(function MySharedDocs() { return Doc.MySharedDocs; }, "document containing all shared Docs");
-ScriptingGlobals.add(function IsExploreMode() { return SnappingManager.ExploreMode; }, "is Dash in exploration mode");
-ScriptingGlobals.add(function IsNoviceMode() { return Doc.noviceMode; }, "is Dash in novice mode");
+// eslint-disable-next-line prefer-arrow-callback
+ScriptingGlobals.add(function MySharedDocs() { return Doc.MySharedDocs; }, "document containing all shared Docs");
+// eslint-disable-next-line prefer-arrow-callback
+ScriptingGlobals.add(function IsExploreMode() { return SnappingManager.ExploreMode; }, "is Dash in exploration mode");
+// eslint-disable-next-line prefer-arrow-callback
+ScriptingGlobals.add(function IsNoviceMode() { return Doc.noviceMode; }, "is Dash in novice mode");
+// eslint-disable-next-line prefer-arrow-callback
ScriptingGlobals.add(function toggleComicMode() { Doc.UserDoc().renderStyle = Doc.UserDoc().renderStyle === "comic" ? undefined : "comic"; }, "switches between comic and normal document rendering");
-ScriptingGlobals.add(function importDocument() { return CurrentUserUtils.importDocument(); }, "imports files from device directly into the import sidebar");
+// eslint-disable-next-line prefer-arrow-callback
+ScriptingGlobals.add(function importDocument() { return CurrentUserUtils.importDocument(); }, "imports files from device directly into the import sidebar");
+// eslint-disable-next-line prefer-arrow-callback
ScriptingGlobals.add(function setInkToolDefaults() { Doc.ActiveTool = InkTool.None; });
-ScriptingGlobals.add(function getSharingDoc() {return Doc.SharingDoc() });
\ No newline at end of file
+// eslint-disable-next-line prefer-arrow-callback
+ScriptingGlobals.add(function getSharingDoc() { return Doc.SharingDoc() });
\ No newline at end of file
diff --git a/src/client/util/DictationManager.ts b/src/client/util/DictationManager.ts
index 82c63695c..207d3ea0b 100644
--- a/src/client/util/DictationManager.ts
+++ b/src/client/util/DictationManager.ts
@@ -1,15 +1,15 @@
import * as interpreter from 'words-to-numbers';
// @ts-ignore bcz: how are you supposed to include these definitions since dom-speech-recognition isn't a module?
import type {} from '@types/dom-speech-recognition';
+import { ClientUtils } from '../../ClientUtils';
import { Doc, Opt } from '../../fields/Doc';
import { List } from '../../fields/List';
import { RichTextField } from '../../fields/RichTextField';
import { listSpec } from '../../fields/Schema';
import { Cast, CastCtor, DocCast } from '../../fields/Types';
import { AudioField, ImageField } from '../../fields/URLField';
-import { Utils } from '../../Utils';
-import { Docs } from '../documents/Documents';
import { DocumentType } from '../documents/DocumentTypes';
+import { Docs } from '../documents/Documents';
import { DictationOverlay } from '../views/DictationOverlay';
import { DocumentView, OpenWhere } from '../views/nodes/DocumentView';
import { SelectionManager } from './SelectionManager';
@@ -103,7 +103,7 @@ export namespace DictationManager {
results = await (pendingListen = listenImpl(options));
pendingListen = undefined;
if (results) {
- Utils.CopyText(results);
+ ClientUtils.CopyText(results);
if (overlay) {
DictationOverlay.Instance.isListening = false;
const execute = options?.tryExecute;
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 40d28c690..67a61f17e 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -8,10 +8,9 @@ import { Cast, DocCast, NumCast, StrCast } from '../../fields/Types';
import { AudioField } from '../../fields/URLField';
import { GetEffectiveAcl } from '../../fields/util';
import { CollectionViewType } from '../documents/DocumentTypes';
-import { CollectionDockingView } from '../views/collections/CollectionDockingView';
import { TabDocView } from '../views/collections/TabDocView';
import { LightboxView } from '../views/LightboxView';
-import { DocumentView, DocumentViewInternal, OpenWhere, OpenWhereMod } from '../views/nodes/DocumentView';
+import { DocumentView, DocumentViewInternal, OpenWhere } from '../views/nodes/DocumentView';
import { FocusViewOptions } from '../views/nodes/FieldView';
import { KeyValueBox } from '../views/nodes/KeyValueBox';
import { LinkAnchorBox } from '../views/nodes/LinkAnchorBox';
@@ -20,12 +19,14 @@ import { ScriptingGlobals } from './ScriptingGlobals';
import { SelectionManager } from './SelectionManager';
export class DocumentManager {
+ // eslint-disable-next-line no-use-before-define
private static _instance: DocumentManager;
public static get Instance(): DocumentManager {
+ // eslint-disable-next-line no-return-assign
return this._instance || (this._instance = new this());
}
- //global holds all of the nodes (regardless of which collection they're in)
+ // global holds all of the nodes (regardless of which collection they're in)
@observable _documentViews = new Set();
@observable.shallow public CurrentlyLoading: Doc[] = [];
@computed public get DocumentViews() {
@@ -38,7 +39,7 @@ export class DocumentManager {
this._documentViews.delete(dv);
}
- //private constructor so no other class can create a nodemanager
+ // private constructor so no other class can create a nodemanager
private constructor() {
makeObservable(this);
observe(this.CurrentlyLoading, change => {
@@ -52,6 +53,7 @@ export class DocumentManager {
case 'splice':
(change as any).removed.forEach((doc: Doc) => DocumentManager.Instance.getAllDocumentViews(doc).forEach(dv => StrCast(dv.Document.layout_fieldKey) === 'layout_icon' && dv.iconify(() => dv.iconify())));
break;
+ default:
}
});
}
@@ -101,7 +103,7 @@ export class DocumentManager {
SelectionManager.DeselectView(view);
});
- //gets all views
+ // gets all views
public getDocumentViewsById(id: string) {
const toReturn: DocumentView[] = [];
DocumentManager.Instance.DocumentViews.forEach(view => {
@@ -135,25 +137,17 @@ export class DocumentManager {
);
}
- public getLightboxDocumentView = (toFind: Doc, originatingDoc: Opt = undefined): DocumentView | undefined => {
+ public getLightboxDocumentView = (toFind: Doc): DocumentView | undefined => {
const views: DocumentView[] = [];
DocumentManager.Instance.DocumentViews.forEach(view => LightboxView.Contains(view) && Doc.AreProtosEqual(view.Document, toFind) && views.push(view));
- return views?.find(view => view.ContentDiv?.getBoundingClientRect().width /*&& view._props.focus !== returnFalse) || views?.find(view => view._props.focus !== returnFalse*/) || (views.length ? views[0] : undefined);
+ return views?.find(view => view.ContentDiv?.getBoundingClientRect().width /* && view._props.focus !== returnFalse) || views?.find(view => view._props.focus !== returnFalse */) || (views.length ? views[0] : undefined);
};
- public getFirstDocumentView = (toFind: Doc, originatingDoc: Opt = undefined): DocumentView | undefined => {
- if (LightboxView.LightboxDoc) return DocumentManager.Instance.getLightboxDocumentView(toFind, originatingDoc);
- const views = this.getDocumentViews(toFind); //.filter(view => view.Document !== originatingDoc);
- return views?.find(view => view.ContentDiv?.getBoundingClientRect().width /*&& view._props.focus !== returnFalse) || views?.find(view => view._props.focus !== returnFalse*/) || (views.length ? views[0] : undefined);
+ public getFirstDocumentView = (toFind: Doc): DocumentView | undefined => {
+ if (LightboxView.LightboxDoc) return DocumentManager.Instance.getLightboxDocumentView(toFind);
+ const views = this.getDocumentViews(toFind); // .filter(view => view.Document !== originatingDoc);
+ return views?.find(view => view.ContentDiv?.getBoundingClientRect().width /* && view._props.focus !== returnFalse) || views?.find(view => view._props.focus !== returnFalse */) || (views.length ? views[0] : undefined);
};
- public getDocumentViews(toFindIn: Doc): DocumentView[] {
- const toFind =
- // Array.from(DocumentManager.Instance.DocumentViews).find(
- // dv =>
- // ((dv.Document.data as any)?.url?.href && (dv.Document.data as any)?.url?.href === (toFindIn.data as any)?.url?.href) ||
- // ((DocCast(dv.Document.annotationOn)?.data as any)?.url?.href && (DocCast(dv.Document.annotationOn)?.data as any)?.url?.href === (DocCast(toFindIn.annotationOn)?.data as any)?.url?.href)
- // )?.Document ??
- toFindIn;
-
+ public getDocumentViews(toFind: Doc): DocumentView[] {
const toReturn: DocumentView[] = [];
const docViews = DocumentManager.Instance.DocumentViews.filter(view => !LightboxView.Contains(view));
const lightViews = DocumentManager.Instance.DocumentViews.filter(view => LightboxView.Contains(view));
@@ -172,7 +166,7 @@ export class DocumentManager {
static GetContextPath(doc: Opt, includeExistingViews?: boolean) {
if (!doc) return [];
const srcContext = DocCast(doc.annotationOn, DocCast(doc.embedContainer));
- var containerDocContext = srcContext ? [srcContext, doc] : [doc];
+ let containerDocContext = srcContext ? [srcContext, doc] : [doc];
while (
containerDocContext.length &&
DocCast(containerDocContext[0]?.embedContainer) &&
@@ -204,10 +198,6 @@ export class DocumentManager {
DocumentManager._overlayViews?.clear();
}
static _overlayViews = new ObservableSet();
- static addView = (doc: Doc, finished?: () => void) => {
- CollectionDockingView.AddSplit(doc, OpenWhereMod.right);
- finished?.();
- };
public static LinkCommonAncestor(linkDoc: Doc) {
const anchor = (which: number) => {
@@ -228,7 +218,7 @@ export class DocumentManager {
// focusing on each context
public showDocumentView = async (targetDocView: DocumentView, options: FocusViewOptions) => {
const docViewPath = [...(targetDocView.containerViewPath?.() ?? []), targetDocView];
- let rootContextView = docViewPath.shift();
+ const rootContextView = docViewPath.shift();
await (rootContextView && this.focusViewsInPath(rootContextView, options, async () => ({ childDocView: docViewPath.shift(), viewSpec: undefined, focused: false })));
if (options.toggleTarget && (!options.didMove || targetDocView.Document.hidden)) targetDocView.Document.hidden = !targetDocView.Document.hidden;
else if (options.openLocation?.startsWith(OpenWhere.toggle) && !options.didMove && rootContextView) DocumentViewInternal.addDocTabFunc(rootContextView.Document, options.openLocation);
@@ -242,9 +232,10 @@ export class DocumentManager {
// and finally restoring the targetDoc to the viewSpec specified by the last document which may either be the targetDoc, or a viewSpec that describes the targetDoc configuration
public showDocument = async (
targetDoc: Doc, // document to display
- options: FocusViewOptions, // options for how to navigate to target
+ optionsIn: FocusViewOptions, // options for how to navigate to target
finished?: (changed: boolean) => void // func called after focusing on target with flag indicating whether anything needed to be done.
) => {
+ const options = optionsIn;
Doc.RemoveDocFromList(Doc.MyRecentlyClosed, undefined, targetDoc);
const docContextPath = DocumentManager.GetContextPath(targetDoc, true);
if (docContextPath.some(doc => doc.hidden)) options.toggleTarget = false;
@@ -253,13 +244,14 @@ export class DocumentManager {
options.toggleTarget = false;
TabDocView.Activate(tabView?._document);
}
- let rootContextView =
+ const rootContextView =
docContextPath.length &&
(await new Promise(res => {
const viewIndex = docContextPath.findIndex(doc => this.getDocumentView(doc));
if (viewIndex !== -1) {
viewIndex && docContextPath.splice(0, viewIndex);
- return res(this.getDocumentView(docContextPath[0])!);
+ res(this.getDocumentView(docContextPath[0])!);
+ return;
}
options.didMove = true;
(!LightboxView.LightboxDoc && docContextPath.some(doc => TabDocView.Activate(doc))) || DocumentViewInternal.addDocTabFunc(docContextPath[0], options.openLocation ?? OpenWhere.addRight);
@@ -270,7 +262,9 @@ export class DocumentManager {
const target = DocCast(targetDoc.annotationOn, targetDoc);
const contextView = this.getDocumentView(DocCast(target.embedContainer));
if (contextView?.ComponentView?.addDocTab?.(target, OpenWhere.lightbox)) {
- await new Promise(waitres => setTimeout(() => waitres()));
+ await new Promise(waitres => {
+ setTimeout(() => waitres());
+ });
}
}
docContextPath.shift();
@@ -287,19 +281,25 @@ export class DocumentManager {
};
focusViewsInPath = async (
- docView: DocumentView, //
- options: FocusViewOptions,
+ docViewIn: DocumentView, //
+ optionsIn: FocusViewOptions,
iterator: (docView: DocumentView) => Promise<{ viewSpec: Opt; childDocView: Opt; focused: boolean }>
) => {
let contextView: DocumentView | undefined; // view containing context that contains target
let focused = false;
+ let docView = docViewIn;
+ const options = optionsIn;
while (true) {
if (docView.Document.layout_fieldKey === 'layout_icon') {
- await new Promise(res => docView.iconify(res));
+ // eslint-disable-next-line no-await-in-loop
+ await new Promise(res => {
+ docView.iconify(res);
+ });
options.didMove = true;
}
const nextFocus = docView._props.focus(docView.Document, options); // focus the view within its container
- focused = focused || (nextFocus === undefined ? false : true); // keep track of whether focusing on a view needed to actually change anything
+ focused = focused || nextFocus !== undefined; // keep track of whether focusing on a view needed to actually change anything
+ // eslint-disable-next-line no-await-in-loop
const { childDocView, viewSpec } = await iterator(docView);
if (!childDocView) return { viewSpec: options.anchorDoc ?? viewSpec ?? docView.Document, docView, contextView, focused };
contextView = options.anchorDoc?.layout_unrendered && !childDocView.Document.layout_unrendered ? childDocView : docView;
@@ -308,10 +308,11 @@ export class DocumentManager {
};
@action
- restoreDocView(viewSpec: Opt, docView: DocumentView, options: FocusViewOptions, contextView: Opt, targetDoc: Doc) {
+ restoreDocView(viewSpec: Opt, docViewIn: DocumentView, options: FocusViewOptions, contextView: Opt, targetDoc: Doc) {
+ const docView = docViewIn;
if (viewSpec && docView) {
- //if (docView.ComponentView instanceof FormattedTextBox)
- //viewSpec !== docView.Document &&
+ // if (docView.ComponentView instanceof FormattedTextBox)
+ // viewSpec !== docView.Document &&
docView.ComponentView?.focus?.(viewSpec, options);
PresBox.restoreTargetDocView(docView, viewSpec, options.zoomTime ?? 500);
Doc.linkFollowHighlight(viewSpec ? [docView.Document, viewSpec] : docView.Document, undefined, options.effect);
@@ -332,7 +333,10 @@ export class DocumentManager {
}
}
}
-export function DocFocusOrOpen(doc: Doc, options: FocusViewOptions = { willZoomCentered: true, zoomScale: 0, openLocation: OpenWhere.toggleRight }, containingDoc?: Doc) {
+// eslint-disable-next-line default-param-last
+export function DocFocusOrOpen(docIn: Doc, optionsIn: FocusViewOptions = { willZoomCentered: true, zoomScale: 0, openLocation: OpenWhere.toggleRight }, containingDoc?: Doc) {
+ let doc = docIn;
+ const options = optionsIn;
const func = () => {
const cv = DocumentManager.Instance.getDocumentView(containingDoc);
const dv = DocumentManager.Instance.getDocumentView(doc, cv);
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 9627c5df2..62f055f1a 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -1,3 +1,5 @@
+/* eslint-disable import/no-mutable-exports */
+/* eslint-disable no-use-before-define */
/**
* The DragManager handles all dragging interactions that occur entirely within Dash (as opposed to external drag operations from the file system, etc)
*
@@ -13,32 +15,25 @@
*/
import { action, observable, runInAction } from 'mobx';
+import { ClientUtils } from '../../ClientUtils';
+import { emptyFunction } from '../../Utils';
import { DateField } from '../../fields/DateField';
-import { Doc, Field, Opt, StrListCast } from '../../fields/Doc';
+import { Doc, FieldType, Opt, StrListCast } from '../../fields/Doc';
+import { DocData } from '../../fields/DocSymbols';
import { List } from '../../fields/List';
import { PrefetchProxy } from '../../fields/Proxy';
import { ScriptField } from '../../fields/ScriptField';
import { ScriptCast } from '../../fields/Types';
-import { emptyFunction, Utils } from '../../Utils';
-import { Docs, DocUtils } from '../documents/Documents';
+import { DocUtils, Docs } from '../documents/Documents';
import { CollectionFreeFormDocumentView } from '../views/nodes/CollectionFreeFormDocumentView';
import { DocumentView } from '../views/nodes/DocumentView';
+import { dropActionType } from './DropActionTypes';
import { ScriptingGlobals } from './ScriptingGlobals';
import { SelectionManager } from './SelectionManager';
import { SnappingManager } from './SnappingManager';
import { UndoManager } from './UndoManager';
-import { DocData } from '../../fields/DocSymbols';
-const { contextMenuZindex } = require('../views/global/globalCssVariables.module.scss'); // prettier-ignore
-export enum dropActionType {
- embed = 'embed', // create a new embedding of the dragged document for the new location
- copy = 'copy', // copy the dragged document
- move = 'move', // move the dragged document to the drop location after removing it from where it was
- add = 'add', // add the dragged document to the drop location without removing it from where it was
- same = 'same', // only allow drop within same collection (or same hierarchical tree collection)
- inPlace = 'inSame', // keep document in place (unless overridden by a drag modifier)
- proto = 'proto',
-} // undefined = move, same = move but doesn't call dropPropertiesToRemove
+const { contextMenuZindex } = require('../views/global/globalCssVariables.module.scss'); // prettier-ignore
/**
* Initialize drag
@@ -83,6 +78,9 @@ export namespace DragManager {
let dragLabel: HTMLDivElement;
export let StartWindowDrag: Opt<(e: { pageX: number; pageY: number }, dragDocs: Doc[], finishDrag?: (aborted: boolean) => void) => boolean>;
export let CompleteWindowDrag: Opt<(aborted: boolean) => void>;
+ export let AbortDrag: () => void = emptyFunction;
+ export const docsBeingDragged: Doc[] = observable([]);
+ export let DocDragData: DocumentDragData | undefined;
export function Root() {
const root = document.getElementById('root');
@@ -91,7 +89,6 @@ export namespace DragManager {
}
return root;
}
- export let AbortDrag: () => void = emptyFunction;
export type MoveFunction = (document: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => boolean;
export type RemoveFunction = (document: Doc | Doc[]) => boolean;
@@ -106,6 +103,7 @@ export namespace DragManager {
// event called when the drag operation results in a drop action
export class DropEvent {
+ // eslint-disable-next-line no-useless-constructor
constructor(
readonly x: number,
readonly y: number,
@@ -115,7 +113,9 @@ export namespace DragManager {
readonly metaKey: boolean,
readonly ctrlKey: boolean,
readonly embedKey: boolean
- ) {}
+ ) {
+ /* empty */
+ }
}
// event called when the drag operation has completed (aborted or completed a drop) -- this will be after any drop event has been generated
@@ -194,7 +194,7 @@ export namespace DragManager {
userDropAction?: dropActionType;
}
- let defaultPreDropFunc = (e: Event, de: DragManager.DropEvent, targetAction: dropActionType) => {
+ const defaultPreDropFunc = (e: Event, de: DragManager.DropEvent, targetAction: dropActionType) => {
if (de.complete.docDragData) {
targetAction && (de.complete.docDragData.dropAction = targetAction);
e.stopPropagation();
@@ -228,7 +228,7 @@ export namespace DragManager {
return dropDoc;
};
const finishDrag = async (e: DragCompleteEvent) => {
- const docDragData = e.docDragData;
+ const { docDragData } = e;
setTimeout(() => dragData.draggedViews.forEach(view => view.props.dragEnding?.()));
onDropCompleted?.(e); // glr: optional additional function to be called - in this case with presentation trails
if (docDragData && !docDragData.droppedDocuments.length) {
@@ -256,7 +256,9 @@ export namespace DragManager {
.forEach((drop: Doc, i: number) => {
const dragProps = StrListCast(dragData.draggedDocuments[i].dropPropertiesToRemove);
const remProps = (dragData?.dropPropertiesToRemove || []).concat(Array.from(dragProps));
- [...remProps, 'dropPropertiesToRemove'].map(prop => (drop[prop] = undefined));
+ [...remProps, 'dropPropertiesToRemove'].forEach(prop => {
+ drop[prop] = undefined;
+ });
});
}
return e;
@@ -268,15 +270,18 @@ export namespace DragManager {
}
// drag a button template and drop a new button
- export function StartButtonDrag(eles: HTMLElement[], script: string, title: string, vars: { [name: string]: Field }, params: string[], initialize: (button: Doc) => void, downX: number, downY: number, options?: DragOptions) {
+ export function StartButtonDrag(eles: HTMLElement[], script: string, title: string, vars: { [name: string]: FieldType }, params: string[], initialize: (button: Doc) => void, downX: number, downY: number, options?: DragOptions) {
const finishDrag = (e: DragCompleteEvent) => {
const bd = Docs.Create.ButtonDocument({ toolTip: title, z: 1, _width: 150, _height: 50, title, onClick: ScriptField.MakeScript(script) });
- params.map(p => Object.keys(vars).indexOf(p) !== -1 && (bd[DocData][p] = new PrefetchProxy(vars[p] as Doc))); // copy all "captured" arguments into document parameterfields
+ params.forEach(p => {
+ Object.keys(vars).indexOf(p) !== -1 && (bd[DocData][p] = new PrefetchProxy(vars[p] as Doc));
+ }); // copy all "captured" arguments into document parameterfields
initialize?.(bd);
bd[DocData]['onClick-paramFieldKeys'] = new List(params);
e.docDragData && (e.docDragData.droppedDocuments = [bd]);
return e;
};
+ // eslint-disable-next-line no-param-reassign
options = options ?? {};
options.noAutoscroll = true; // these buttons are being dragged on the overlay layer, so scrollin the underlay is not appropriate
StartDrag(eles, new DragManager.DocumentDragData([]), downX, downY, options, finishDrag);
@@ -298,7 +303,7 @@ export namespace DragManager {
}
export function snapDragAspect(dragPt: number[], snapAspect: number) {
- let closest = Utils.SNAP_THRESHOLD;
+ let closest = ClientUtils.SNAP_THRESHOLD;
let near = dragPt;
const intersect = (x1: number, y1: number, x2: number, y2: number, x3: number, y3: number, x4: number, y4: number, dragx: number, dragy: number) => {
if ((x1 === x2 && y1 === y2) || (x3 === x4 && y3 === y4)) return undefined; // Check if none of the lines are of length 0
@@ -307,7 +312,7 @@ export namespace DragManager {
const ua = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / denominator;
// let ub = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)) / denominator;
- //if (ua < 0 || ua > 1 || ub < 0 || ub > 1) return undefined; // is the intersection along the segments
+ // if (ua < 0 || ua > 1 || ub < 0 || ub > 1) return undefined; // is the intersection along the segments
// Return a object with the x and y coordinates of the intersection
const x = x1 + ua * (x2 - x1);
@@ -315,14 +320,14 @@ export namespace DragManager {
const dist = Math.sqrt((dragx - x) * (dragx - x) + (dragy - y) * (dragy - y));
return { pt: [x, y], dist };
};
- SnappingManager.VertSnapLines.forEach((xCoord, i) => {
+ SnappingManager.VertSnapLines.forEach(xCoord => {
const pt = intersect(dragPt[0], dragPt[1], dragPt[0] + snapAspect, dragPt[1] + 1, xCoord, -1, xCoord, 1, dragPt[0], dragPt[1]);
if (pt && pt.dist < closest) {
closest = pt.dist;
near = pt.pt;
}
});
- SnappingManager.HorizSnapLines.forEach((yCoord, i) => {
+ SnappingManager.HorizSnapLines.forEach(yCoord => {
const pt = intersect(dragPt[0], dragPt[1], dragPt[0] + snapAspect, dragPt[1] + 1, -1, yCoord, 1, yCoord, dragPt[0], dragPt[1]);
if (pt && pt.dist < closest) {
closest = pt.dist;
@@ -333,7 +338,7 @@ export namespace DragManager {
}
// snap to the active snap lines - if oneAxis is set (eg, for maintaining aspect ratios), then it only snaps to the nearest horizontal/vertical line
export function snapDrag(e: PointerEvent, xFromLeft: number, yFromTop: number, xFromRight: number, yFromBottom: number) {
- const snapThreshold = Utils.SNAP_THRESHOLD;
+ const snapThreshold = ClientUtils.SNAP_THRESHOLD;
const snapVal = (pts: number[], drag: number, snapLines: number[]) => {
if (snapLines.length) {
const offs = [pts[0], (pts[0] - pts[1]) / 2, -pts[1]]; // offsets from drag pt
@@ -350,14 +355,33 @@ export namespace DragManager {
y: snapVal([yFromTop, yFromBottom], e.pageY, SnappingManager.HorizSnapLines),
};
}
- export let docsBeingDragged: Doc[] = observable([]);
- export let CanEmbed = false;
- export let DocDragData: DocumentDragData | undefined;
- export function StartDrag(eles: HTMLElement[], dragData: { [id: string]: any }, downX: number, downY: number, options?: DragOptions, finishDrag?: (dropData: DragCompleteEvent) => void, dragUndoName?: string) {
+
+ async function dispatchDrag(target: Element, e: PointerEvent, complete: DragCompleteEvent, pos: { x: number; y: number }, finishDrag?: (e: DragCompleteEvent) => void, options?: DragOptions, endDrag?: () => void) {
+ const dropArgs = {
+ cancelable: true, // allows preventDefault() to be called to cancel the drop
+ bubbles: true,
+ detail: {
+ ...pos,
+ complete,
+ shiftKey: e.shiftKey,
+ altKey: e.altKey,
+ metaKey: e.metaKey,
+ ctrlKey: e.ctrlKey,
+ embedKey: SnappingManager.CanEmbed,
+ },
+ };
+ target.dispatchEvent(new CustomEvent('dashPreDrop', dropArgs));
+ UndoManager.StartTempBatch(); // run drag/drop in temp batch in case drop is not allowed (so we can undo any intermediate changes)
+ await finishDrag?.(complete);
+ UndoManager.EndTempBatch(target.dispatchEvent(new CustomEvent('dashOnDrop', dropArgs))); // event return val is true unless the event preventDefault() is called
+ options?.dragComplete?.(complete);
+ endDrag?.();
+ }
+ export function StartDrag(elesIn: HTMLElement[], dragData: { [id: string]: any }, downX: number, downY: number, options?: DragOptions, finishDrag?: (dropData: DragCompleteEvent) => void, dragUndoName?: string) {
if (dragData.dropAction === 'none' || SnappingManager.ExploreMode) return;
DocDragData = dragData as DocumentDragData;
const batch = UndoManager.StartBatch(dragUndoName ?? 'document drag');
- eles = eles.filter(e => e);
+ const eles = elesIn.filter(e => e);
SnappingManager.SetCanEmbed(dragData.canEmbed || false);
if (!dragDiv) {
dragDiv = document.createElement('div');
@@ -375,9 +399,9 @@ export namespace DragManager {
}
Object.assign(dragDiv.style, { width: '', height: '', overflow: '' });
dragDiv.hidden = false;
- const scalings: number[] = [],
- xs: number[] = [],
- ys: number[] = [];
+ const scalings: number[] = [];
+ const xs: number[] = [];
+ const ys: number[] = [];
const elesCont = {
left: Number.MAX_SAFE_INTEGER,
@@ -385,7 +409,7 @@ export namespace DragManager {
top: Number.MAX_SAFE_INTEGER,
bottom: Number.MIN_SAFE_INTEGER,
};
- let rot: number[] = [];
+ const rot: number[] = [];
const docsToDrag = dragData instanceof DocumentDragData ? dragData.draggedDocuments : dragData instanceof AnchorAnnoDragData ? [dragData.dragDocument] : [];
const dragElements = eles.map(ele => {
// bcz: very hacky -- if dragged element is a freeForm view with a rotation, then extract the rotation in order to apply it to the dragged element
@@ -471,7 +495,9 @@ export namespace DragManager {
.filter(pb => pb.width && pb.height)
.map((pb, i) => pb.getContext('2d')!.drawImage(pdfBoxSrc[i], 0, 0));
}
- [dragElement, ...Array.from(dragElement.getElementsByTagName('*'))].forEach(ele => (ele as any).style && ((ele as any).style.pointerEvents = 'none'));
+ [dragElement, ...Array.from(dragElement.getElementsByTagName('*'))].forEach(ele => {
+ (ele as any).style && ((ele as any).style.pointerEvents = 'none');
+ });
dragDiv.appendChild(dragElement);
if (dragElement !== ele) {
@@ -493,7 +519,11 @@ export namespace DragManager {
const hideDragShowOriginalElements = (hide: boolean) => {
dragLabel.style.display = hide && !SnappingManager.CanEmbed ? '' : 'none';
!hide && dragElements.map(dragElement => dragElement.parentNode === dragDiv && dragDiv.removeChild(dragElement));
- setTimeout(() => eles.forEach(ele => (ele.hidden = hide)));
+ setTimeout(() =>
+ eles.forEach(ele => {
+ ele.hidden = hide;
+ })
+ );
};
options?.hideSource && hideDragShowOriginalElements(true);
@@ -505,22 +535,7 @@ export namespace DragManager {
const yFromBottom = elesCont.bottom - downY;
let scrollAwaiter: Opt;
- AbortDrag = () => {
- options?.dragComplete?.(new DragCompleteEvent(true, dragData));
- cleanupDrag(true);
- };
-
- const cleanupDrag = action((undo: boolean) => {
- (dragData as DocumentDragData).draggedViews?.forEach(view => view.props.dragEnding?.());
- hideDragShowOriginalElements(false);
- document.removeEventListener('pointermove', moveHandler, true);
- document.removeEventListener('pointerup', upHandler, true);
- SnappingManager.SetIsDragging(false);
- if (batch.end() && undo) UndoManager.Undo();
- docsBeingDragged.length = 0;
- SnappingManager.SetCanEmbed(false);
- });
- var startWindowDragTimer: any;
+ let startWindowDragTimer: any;
const moveHandler = (e: PointerEvent) => {
e.preventDefault(); // required or dragging text menu link item ends up dragging the link button as native drag/drop
if (dragData instanceof DocumentDragData) {
@@ -580,10 +595,10 @@ export namespace DragManager {
defaultPrevented: true,
eventPhase: e.eventPhase,
isTrusted: true,
- preventDefault: () => ('not implemented for this event' ? false : false),
- isDefaultPrevented: () => ('not implemented for this event' ? false : false),
- stopPropagation: () => ('not implemented for this event' ? false : false),
- isPropagationStopped: () => ('not implemented for this event' ? false : false),
+ preventDefault: () => 'not implemented for this event' && false,
+ isDefaultPrevented: () => 'not implemented for this event' && false,
+ stopPropagation: () => 'not implemented for this event' && false,
+ isPropagationStopped: () => 'not implemented for this event' && false,
persist: emptyFunction,
timeStamp: e.timeStamp,
type: 'dashDragMovePause',
@@ -602,7 +617,9 @@ export namespace DragManager {
const moveVec = { x: x - lastPt.x, y: y - lastPt.y };
lastPt = { x, y };
- dragElements.map((dragElement, i) => (dragElement.style.transform = `translate(${(xs[i] += moveVec.x)}px, ${(ys[i] += moveVec.y)}px) rotate(${rot[i]}deg) scale(${scalings[i]})`));
+ dragElements.forEach((dragElement, i) => {
+ dragElement.style.transform = `translate(${(xs[i] += moveVec.x)}px, ${(ys[i] += moveVec.y)}px) rotate(${rot[i]}deg) scale(${scalings[i]})`;
+ });
dragLabel.style.transform = `translate(${xs[0]}px, ${ys[0] - 20}px)`;
};
const upHandler = (e: PointerEvent) => {
@@ -610,36 +627,32 @@ export namespace DragManager {
startWindowDragTimer = undefined;
dispatchDrag(document.elementFromPoint(e.x, e.y) || document.body, e, new DragCompleteEvent(false, dragData), snapDrag(e, xFromLeft, yFromTop, xFromRight, yFromBottom), finishDrag, options, () => cleanupDrag(false));
};
+ const cleanupDrag = action((undo: boolean) => {
+ (dragData as DocumentDragData).draggedViews?.forEach(view => view.props.dragEnding?.());
+ hideDragShowOriginalElements(false);
+ document.removeEventListener('pointermove', moveHandler, true);
+ document.removeEventListener('pointerup', upHandler, true);
+ SnappingManager.SetIsDragging(false);
+ if (batch.end() && undo) UndoManager.Undo();
+ docsBeingDragged.length = 0;
+ SnappingManager.SetCanEmbed(false);
+ });
+ AbortDrag = () => {
+ options?.dragComplete?.(new DragCompleteEvent(true, dragData));
+ cleanupDrag(true);
+ };
document.addEventListener('pointermove', moveHandler, true);
document.addEventListener('pointerup', upHandler, true);
}
-
- async function dispatchDrag(target: Element, e: PointerEvent, complete: DragCompleteEvent, pos: { x: number; y: number }, finishDrag?: (e: DragCompleteEvent) => void, options?: DragOptions, endDrag?: () => void) {
- const dropArgs = {
- cancelable: true, // allows preventDefault() to be called to cancel the drop
- bubbles: true,
- detail: {
- ...pos,
- complete,
- shiftKey: e.shiftKey,
- altKey: e.altKey,
- metaKey: e.metaKey,
- ctrlKey: e.ctrlKey,
- embedKey: SnappingManager.CanEmbed,
- },
- };
- target.dispatchEvent(new CustomEvent('dashPreDrop', dropArgs));
- UndoManager.StartTempBatch(); // run drag/drop in temp batch in case drop is not allowed (so we can undo any intermediate changes)
- await finishDrag?.(complete);
- UndoManager.EndTempBatch(target.dispatchEvent(new CustomEvent('dashOnDrop', dropArgs))); // event return val is true unless the event preventDefault() is called
- options?.dragComplete?.(complete);
- endDrag?.();
- }
}
+// eslint-disable-next-line prefer-arrow-callback
ScriptingGlobals.add(function toggleRaiseOnDrag(readOnly?: boolean) {
if (readOnly) {
return SelectionManager.Views.some(dv => dv.Document.keepZWhenDragged);
}
- SelectionManager.Views.map(dv => (dv.Document.keepZWhenDragged = !dv.Document.keepZWhenDragged));
+ SelectionManager.Views.forEach(dv => {
+ dv.Document.keepZWhenDragged = !dv.Document.keepZWhenDragged;
+ });
+ return undefined;
});
diff --git a/src/client/util/DropActionTypes.ts b/src/client/util/DropActionTypes.ts
new file mode 100644
index 000000000..45b294d97
--- /dev/null
+++ b/src/client/util/DropActionTypes.ts
@@ -0,0 +1,9 @@
+export enum dropActionType {
+ embed = 'embed', // create a new embedding of the dragged document for the new location
+ copy = 'copy', // copy the dragged document
+ move = 'move', // move the dragged document to the drop location after removing it from where it was
+ add = 'add', // add the dragged document to the drop location without removing it from where it was
+ same = 'same', // only allow drop within same collection (or same hierarchical tree collection)
+ inPlace = 'inSame', // keep document in place (unless overridden by a drag modifier)
+ proto = 'proto',
+} // undefined = move, same = move but doesn't call dropPropertiesToRemove
diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx
index f4f879208..6b20b885b 100644
--- a/src/client/util/GroupManager.tsx
+++ b/src/client/util/GroupManager.tsx
@@ -5,19 +5,20 @@ import { observer } from 'mobx-react';
import * as React from 'react';
import Select from 'react-select';
import * as RequestPromise from 'request-promise';
+import { ClientUtils } from '../../ClientUtils';
+import { Utils } from '../../Utils';
import { DateField } from '../../fields/DateField';
import { Doc, DocListCast, Opt } from '../../fields/Doc';
import { Id } from '../../fields/FieldSymbols';
import { listSpec } from '../../fields/Schema';
import { Cast, StrCast } from '../../fields/Types';
-import { Utils } from '../../Utils';
import { MainViewModal } from '../views/MainViewModal';
+import { ObservableReactComponent } from '../views/ObservableReactComponent';
import { TaskCompletionBox } from '../views/nodes/TaskCompletedBox';
import './GroupManager.scss';
import { GroupMemberView } from './GroupMemberView';
import { SettingsManager } from './SettingsManager';
import { SharingManager, User } from './SharingManager';
-import { ObservableReactComponent } from '../views/ObservableReactComponent';
/**
* Interface for options for the react-select component
@@ -55,7 +56,7 @@ export class GroupManager extends ObservableReactComponent<{}> {
*/
populateUsers = async () => {
if (Doc.UserDoc()[Id] !== Utils.GuestID()) {
- const userList = await RequestPromise.get(Utils.prepend('/getUsers'));
+ const userList = await RequestPromise.get(ClientUtils.prepend('/getUsers'));
const raw = JSON.parse(userList) as User[];
raw.map(action(user => !this.users.some(umail => umail === user.email) && this.users.push(user.email)));
}
@@ -135,7 +136,7 @@ export class GroupManager extends ObservableReactComponent<{}> {
hasEditAccess(groupDoc: Doc): boolean {
if (!groupDoc) return false;
const accessList: string[] = JSON.parse(StrCast(groupDoc.owners));
- return accessList.includes(Doc.CurrentUserEmail) || this.adminGroupMembers?.includes(Doc.CurrentUserEmail);
+ return accessList.includes(ClientUtils.CurrentUserEmail) || this.adminGroupMembers?.includes(ClientUtils.CurrentUserEmail);
}
/**
@@ -147,7 +148,7 @@ export class GroupManager extends ObservableReactComponent<{}> {
const name = groupName.toLowerCase() === 'admin' ? 'Admin' : groupName;
const groupDoc = new Doc('GROUP:' + name, true);
groupDoc.title = name;
- groupDoc.owners = JSON.stringify([Doc.CurrentUserEmail]);
+ groupDoc.owners = JSON.stringify([ClientUtils.CurrentUserEmail]);
groupDoc.members = JSON.stringify(memberEmails);
this.addGroup(groupDoc);
}
@@ -176,11 +177,11 @@ export class GroupManager extends ObservableReactComponent<{}> {
Doc.RemoveDocFromList(this.GroupManagerDoc, 'data', group);
SharingManager.Instance.removeGroup(group);
const members = JSON.parse(StrCast(group.members));
- if (members.includes(Doc.CurrentUserEmail)) {
+ if (members.includes(ClientUtils.CurrentUserEmail)) {
const index = DocListCast(this.GroupManagerDoc.data).findIndex(grp => grp === group);
index !== -1 && Cast(this.GroupManagerDoc.data, listSpec(Doc), [])?.splice(index, 1);
}
- this.GroupManagerDoc['data_modificationDate'] = new DateField();
+ this.GroupManagerDoc.data_modificationDate = new DateField();
if (group === this.currentGroup) {
this.currentGroup = undefined;
}
@@ -260,7 +261,10 @@ export class GroupManager extends ObservableReactComponent<{}> {
alert('Please select a unique group name');
return;
}
- this.createGroupDoc(value, this.selectedUsers?.map(user => user.value));
+ this.createGroupDoc(
+ value,
+ this.selectedUsers?.map(user => user.value)
+ );
this.selectedUsers = null;
this.inputRef.current!.value = '';
this.buttonColour = '#979797';
diff --git a/src/client/util/History.ts b/src/client/util/History.ts
index 2f1a336cc..b500a5af9 100644
--- a/src/client/util/History.ts
+++ b/src/client/util/History.ts
@@ -1,6 +1,6 @@
import * as qs from 'query-string';
import { Doc } from '../../fields/Doc';
-import { OmitKeys, Utils } from '../../Utils';
+import { OmitKeys, ClientUtils } from '../../ClientUtils';
import { DocServer } from '../DocServer';
import { DashboardView } from '../views/DashboardView';
@@ -136,7 +136,7 @@ export namespace HistoryUtil {
function addStringifier(type: string, keys: string[], customStringifier?: (state: ParsedUrl, current: string) => string) {
stringifiers[type] = state => {
- let path = Utils.prepend(`/${type}`);
+ let path = ClientUtils.prepend(`/${type}`);
if (customStringifier) {
path = customStringifier(state, path);
}
diff --git a/src/client/util/HypothesisUtils.ts b/src/client/util/HypothesisUtils.ts
index c5f307f44..d1600468a 100644
--- a/src/client/util/HypothesisUtils.ts
+++ b/src/client/util/HypothesisUtils.ts
@@ -1,5 +1,5 @@
import { action, runInAction } from 'mobx';
-import { simulateMouseClick } from '../../Utils';
+import { simulateMouseClick } from '../../ClientUtils';
import { Doc, Opt } from '../../fields/Doc';
import { Cast, StrCast } from '../../fields/Types';
import { WebField } from '../../fields/URLField';
@@ -33,7 +33,7 @@ export namespace Hypothesis {
// await SearchUtil.Search('web', true).then(
// action(async (res: SearchUtil.DocSearchResult) => {
// const docs = res.docs;
- // const filteredDocs = docs.filter(doc => doc.author === Doc.CurrentUserEmail && doc.type === DocumentType.WEB && doc.data);
+ // const filteredDocs = docs.filter(doc => doc.author === ClientUtils.CurrentUserEmail && doc.type === DocumentType.WEB && doc.data);
// filteredDocs.forEach(doc => {
// uri === Cast(doc.data, WebField)?.url.href && results.push(doc); // TODO check visited sites history?
// });
diff --git a/src/client/util/Import & Export/ImageUtils.ts b/src/client/util/Import & Export/ImageUtils.ts
index d99828956..dfad9755c 100644
--- a/src/client/util/Import & Export/ImageUtils.ts
+++ b/src/client/util/Import & Export/ImageUtils.ts
@@ -1,10 +1,10 @@
+import { ClientUtils } from '../../../ClientUtils';
import { Doc } from '../../../fields/Doc';
+import { DocData } from '../../../fields/DocSymbols';
+import { Id } from '../../../fields/FieldSymbols';
+import { Cast, NumCast, StrCast } from '../../../fields/Types';
import { ImageField } from '../../../fields/URLField';
-import { Cast, StrCast, NumCast } from '../../../fields/Types';
import { Networking } from '../../Network';
-import { Id } from '../../../fields/FieldSymbols';
-import { Utils } from '../../../Utils';
-import { DocData } from '../../../fields/DocSymbols';
export namespace ImageUtils {
export type imgInfo = {
@@ -35,7 +35,7 @@ export namespace ImageUtils {
export const ExportHierarchyToFileSystem = async (collection: Doc): Promise => {
const a = document.createElement('a');
- a.href = Utils.prepend(`/imageHierarchyExport/${collection[Id]}`);
+ a.href = ClientUtils.prepend(`/imageHierarchyExport/${collection[Id]}`);
a.download = `Dash Export [${StrCast(collection.title)}].zip`;
a.click();
};
diff --git a/src/client/util/InteractionUtils.tsx b/src/client/util/InteractionUtils.tsx
index a2f5826fe..a07550e09 100644
--- a/src/client/util/InteractionUtils.tsx
+++ b/src/client/util/InteractionUtils.tsx
@@ -1,6 +1,6 @@
import * as React from 'react';
-import { GestureUtils } from '../../pen-gestures/GestureUtils';
import { Utils } from '../../Utils';
+import { Gestures } from '../../pen-gestures/GestureTypes';
import './InteractionUtils.scss';
export namespace InteractionUtils {
@@ -9,81 +9,86 @@ export namespace InteractionUtils {
export const PENTYPE = 'pen';
export const ERASERTYPE = 'eraser';
- const POINTER_PEN_BUTTON = -1;
- const REACT_POINTER_PEN_BUTTON = 0;
const ERASER_BUTTON = 5;
- export class MultiTouchEvent {
- constructor(
- readonly fingers: number,
- readonly targetTouches: T extends React.TouchEvent ? React.Touch[] : Touch[],
- readonly touches: T extends React.TouchEvent ? React.Touch[] : Touch[],
- readonly changedTouches: T extends React.TouchEvent ? React.Touch[] : Touch[],
- readonly touchEvent: T extends React.TouchEvent ? React.TouchEvent : TouchEvent
- ) {}
- }
-
- export interface MultiTouchEventDisposer {
- (): void;
- }
-
- /**
- *
- * @param element - element to turn into a touch target
- * @param startFunc - event handler, typically Touchable.onTouchStart (classes that inherit touchable can pass in this.onTouchStart)
- */
- export function MakeMultiTouchTarget(element: HTMLElement, startFunc: (e: Event, me: MultiTouchEvent) => void): MultiTouchEventDisposer {
- const onMultiTouchStartHandler = (e: Event) => startFunc(e, (e as CustomEvent>).detail);
- // const onMultiTouchMoveHandler = moveFunc ? (e: Event) => moveFunc(e, (e as CustomEvent>).detail) : undefined;
- // const onMultiTouchEndHandler = endFunc ? (e: Event) => endFunc(e, (e as CustomEvent>).detail) : undefined;
- element.addEventListener('dashOnTouchStart', onMultiTouchStartHandler);
- // if (onMultiTouchMoveHandler) {
- // element.addEventListener("dashOnTouchMove", onMultiTouchMoveHandler);
- // }
- // if (onMultiTouchEndHandler) {
- // element.addEventListener("dashOnTouchEnd", onMultiTouchEndHandler);
- // }
- return () => {
- element.removeEventListener('dashOnTouchStart', onMultiTouchStartHandler);
- // if (onMultiTouchMoveHandler) {
- // element.removeEventListener("dashOnTouchMove", onMultiTouchMoveHandler);
- // }
- // if (onMultiTouchEndHandler) {
- // element.removeEventListener("dashOnTouchend", onMultiTouchEndHandler);
- // }
- };
- }
-
- /**
- * Turns an element onto a target for touch hold handling.
- * @param element - element to add events to
- * @param func - function to add to the event
- */
- export function MakeHoldTouchTarget(element: HTMLElement, func: (e: Event, me: MultiTouchEvent) => void): MultiTouchEventDisposer {
- const handler = (e: Event) => func(e, (e as CustomEvent>).detail);
- element.addEventListener('dashOnTouchHoldStart', handler);
- return () => {
- element.removeEventListener('dashOnTouchHoldStart', handler);
- };
- }
-
- export function GetMyTargetTouches(mte: InteractionUtils.MultiTouchEvent, prevPoints: Map, ignorePen: boolean): React.Touch[] {
- const myTouches = new Array();
- for (const pt of mte.touches) {
- if (!ignorePen || ((pt as any).radiusX > 1 && (pt as any).radiusY > 1)) {
- for (const tPt of mte.targetTouches) {
- if (tPt?.screenX === pt?.screenX && tPt?.screenY === pt?.screenY) {
- if (pt && prevPoints.has(pt.identifier)) {
- myTouches.push(pt);
- }
- }
+ export function makePolygon(shape: string, points: { X: number; Y: number }[]) {
+ // if arrow/line/circle, the two end points should be the starting and the ending point
+ let left = points[0].X;
+ let top = points[0].Y;
+ let right = points[1].X;
+ let bottom = points[1].Y;
+ if (points.length > 1 && points[points.length - 1].X === points[0].X && points[points.length - 1].Y + 1 === points[0].Y) {
+ // pointer is up (first and last points are the same)
+ if (![Gestures.Arrow, Gestures.Line, Gestures.Circle].includes(shape as any as Gestures)) {
+ // otherwise take max and min
+ const xs = points.map(p => p.X);
+ const ys = points.map(p => p.Y);
+ right = Math.max(...xs);
+ left = Math.min(...xs);
+ bottom = Math.max(...ys);
+ top = Math.min(...ys);
+ }
+ } else {
+ // if in the middle of drawing
+ // take first and last points
+ right = points[points.length - 1].X;
+ left = points[0].X;
+ bottom = points[points.length - 1].Y;
+ top = points[0].Y;
+ if (shape !== Gestures.Arrow && shape !== Gestures.Line && shape !== Gestures.Circle) {
+ // switch left/right and top/bottom if needed
+ if (left > right) {
+ const temp = right;
+ right = left;
+ left = temp;
+ }
+ if (top > bottom) {
+ const temp = top;
+ top = bottom;
+ bottom = temp;
}
}
}
- // if (mte.touches.length !== myTouches.length) {
- // throw Error("opo")
- // }
- return myTouches;
+ const polyPts = [];
+ switch (shape) {
+ case Gestures.Rectangle:
+ polyPts.push({ X: left, Y: top });
+ polyPts.push({ X: right, Y: top });
+ polyPts.push({ X: right, Y: bottom });
+ polyPts.push({ X: left, Y: bottom });
+ polyPts.push({ X: left, Y: top });
+ break;
+ case Gestures.Triangle:
+ polyPts.push({ X: left, Y: bottom });
+ polyPts.push({ X: right, Y: bottom });
+ polyPts.push({ X: (right + left) / 2, Y: top });
+ polyPts.push({ X: left, Y: bottom });
+ break;
+ case Gestures.Circle:
+ {
+ const centerX = (Math.max(left, right) + Math.min(left, right)) / 2;
+ const centerY = (Math.max(top, bottom) + Math.min(top, bottom)) / 2;
+ const radius = Math.max(centerX - Math.min(left, right), centerY - Math.min(top, bottom));
+ for (let x = centerX - radius; x < centerX + radius; x++) {
+ const y = Math.sqrt(radius ** 2 - (x - centerX) ** 2) + centerY;
+ polyPts.push({ X: x, Y: y });
+ }
+ for (let x = centerX + radius; x > centerX - radius; x--) {
+ const y = Math.sqrt(radius ** 2 - (x - centerX) ** 2) + centerY;
+ const newY = centerY - (y - centerY);
+ polyPts.push({ X: x, Y: newY });
+ }
+ polyPts.push({ X: centerX - radius, Y: Math.sqrt(radius ** 2 - (-radius) ** 2) + centerY });
+ }
+ break;
+
+ case Gestures.Line:
+ default:
+ polyPts.push({ X: left, Y: top });
+ polyPts.push({ X: right, Y: bottom });
+ break;
+ }
+ return polyPts;
}
export function CreatePolyline(
@@ -101,20 +106,20 @@ export namespace InteractionUtils {
arrowEnd: string,
markerScale: number,
dash: string | undefined,
- scalex: number,
- scaley: number,
+ scalexIn: number,
+ scaleyIn: number,
shape: string,
pevents: string,
opacity: number,
nodefs: boolean,
downHdlr?: (e: React.PointerEvent) => void,
- mask?: boolean,
- dropshadow?: string
+ mask?: boolean
+ // dropshadow?: string
) {
const pts = shape ? makePolygon(shape, points) : points;
- if (isNaN(scalex)) scalex = 1;
- if (isNaN(scaley)) scaley = 1;
+ const scalex = isNaN(scalexIn) ? 1 : scalexIn;
+ const scaley = isNaN(scaleyIn) ? 1 : scaleyIn;
const toScr = (p: { X: number; Y: number }) => ` ${!p ? 0 : (p.X - left - width / 2) * scalex + width / 2}, ${!p ? 0 : (p.Y - top - width / 2) * scaley + width / 2} `;
const strpts = bezier
@@ -137,7 +142,7 @@ export namespace InteractionUtils {
{!mask ? null : (
-
+
)}
{arrowStart !== 'dot' && arrowEnd !== 'dot' ? null : (
@@ -172,7 +177,7 @@ export namespace InteractionUtils {
1 && points[points.length - 1].X === points[0].X && points[points.length - 1].Y + 1 === points[0].Y) {
- //pointer is up (first and last points are the same)
- if (shape === GestureUtils.Gestures.Arrow || shape === GestureUtils.Gestures.Line || shape === GestureUtils.Gestures.Circle) {
- //if arrow or line, the two end points should be the starting and the ending point
- var left = points[0].X;
- var top = points[0].Y;
- var right = points[1].X;
- var bottom = points[1].Y;
- } else {
- //otherwise take max and min
- const xs = points.map(p => p.X);
- const ys = points.map(p => p.Y);
- right = Math.max(...xs);
- left = Math.min(...xs);
- bottom = Math.max(...ys);
- top = Math.min(...ys);
- }
- } else {
- //if in the middle of drawing
- //take first and last points
- right = points[points.length - 1].X;
- left = points[0].X;
- bottom = points[points.length - 1].Y;
- top = points[0].Y;
- if (shape !== GestureUtils.Gestures.Arrow && shape !== GestureUtils.Gestures.Line && shape !== GestureUtils.Gestures.Circle) {
- //switch left/right and top/bottom if needed
- if (left > right) {
- const temp = right;
- right = left;
- left = temp;
- }
- if (top > bottom) {
- const temp = top;
- top = bottom;
- bottom = temp;
- }
- }
- }
- points = [];
- switch (shape) {
- case GestureUtils.Gestures.Rectangle:
- points.push({ X: left, Y: top });
- points.push({ X: right, Y: top });
- points.push({ X: right, Y: bottom });
- points.push({ X: left, Y: bottom });
- points.push({ X: left, Y: top });
- break;
- case GestureUtils.Gestures.Triangle:
- points.push({ X: left, Y: bottom });
- points.push({ X: right, Y: bottom });
- points.push({ X: (right + left) / 2, Y: top });
- points.push({ X: left, Y: bottom });
- break;
- case GestureUtils.Gestures.Circle:
- const centerX = (Math.max(left, right) + Math.min(left, right)) / 2;
- const centerY = (Math.max(top, bottom) + Math.min(top, bottom)) / 2;
- const radius = Math.max(centerX - Math.min(left, right), centerY - Math.min(top, bottom));
- for (var x = centerX - radius; x < centerX + radius; x++) {
- const y = Math.sqrt(Math.pow(radius, 2) - Math.pow(x - centerX, 2)) + centerY;
- points.push({ X: x, Y: y });
- }
- for (var x = centerX + radius; x > centerX - radius; x--) {
- const y = Math.sqrt(Math.pow(radius, 2) - Math.pow(x - centerX, 2)) + centerY;
- const newY = centerY - (y - centerY);
- points.push({ X: x, Y: newY });
- }
- points.push({ X: centerX - radius, Y: Math.sqrt(Math.pow(radius, 2) - Math.pow(-radius, 2)) + centerY });
- break;
-
- case GestureUtils.Gestures.Line:
- points.push({ X: left, Y: top });
- points.push({ X: right, Y: bottom });
- break;
- }
- return points;
- }
/**
* Returns whether or not the pointer event passed in is of the type passed in
* @param e - pointer event. this event could be from a mouse, a pen, or a finger
@@ -279,10 +207,11 @@ export namespace InteractionUtils {
// prettier-ignore
switch (type) {
// pen and eraser are both pointer type 'pen', but pen is button 0 and eraser is button 5. -syip2
- case PENTYPE: return e.pointerType === PENTYPE && (e.button === -1 || e.button === 0);
+ case PENTYPE: return e.pointerType === PENTYPE && (e.button === -1 || e.button === 0);
case ERASERTYPE: return e.pointerType === PENTYPE && e.button === (e instanceof PointerEvent ? ERASER_BUTTON : ERASER_BUTTON);
- case TOUCHTYPE: return e.pointerType === TOUCHTYPE;
- }
+ case TOUCHTYPE: return e.pointerType === TOUCHTYPE;
+ default:
+ } // prettier-ignore
return e.pointerType === type;
}
@@ -292,7 +221,7 @@ export namespace InteractionUtils {
* @param pt2
*/
export function TwoPointEuclidist(pt1: React.Touch, pt2: React.Touch): number {
- return Math.sqrt(Math.pow(pt1.clientX - pt2.clientX, 2) + Math.pow(pt1.clientY - pt2.clientY, 2));
+ return Math.sqrt((pt1.clientX - pt2.clientX) ** 2 + (pt1.clientY - pt2.clientY) ** 2);
}
/**
@@ -349,72 +278,4 @@ export namespace InteractionUtils {
}
return 0;
}
-
- export function IsDragging(oldTouches: Map, newTouches: React.Touch[], leniency: number): boolean {
- for (const touch of newTouches) {
- if (touch) {
- const oldTouch = oldTouches.get(touch.identifier);
- if (oldTouch) {
- if (TwoPointEuclidist(touch, oldTouch) >= leniency) {
- return true;
- }
- }
- }
- }
- return false;
- }
-
- // These might not be very useful anymore, but I'll leave them here for now -syip2
- {
- /**
- * Returns the type of Touch Interaction from a list of points.
- * Also returns any data that is associated with a Touch Interaction
- * @param pts - List of points
- */
- // export function InterpretPointers(pts: React.Touch[]): { type: Opt, data?: any } {
- // const leniency = 200;
- // switch (pts.length) {
- // case 1:
- // return { type: OneFinger };
- // case 2:
- // return { type: TwoSeperateFingers };
- // case 3:
- // let pt1 = pts[0];
- // let pt2 = pts[1];
- // let pt3 = pts[2];
- // if (pt1 && pt2 && pt3) {
- // let dist12 = TwoPointEuclidist(pt1, pt2);
- // let dist23 = TwoPointEuclidist(pt2, pt3);
- // let dist13 = TwoPointEuclidist(pt1, pt3);
- // let dist12close = dist12 < leniency;
- // let dist23close = dist23 < leniency;
- // let dist13close = dist13 < leniency;
- // let xor2313 = dist23close ? !dist13close : dist13close;
- // let xor = dist12close ? !xor2313 : xor2313;
- // // three input xor because javascript doesn't have logical xor's
- // if (xor) {
- // let points: number[] = [];
- // let min = Math.min(dist12, dist23, dist13);
- // switch (min) {
- // case dist12:
- // points = [0, 1, 2];
- // break;
- // case dist23:
- // points = [1, 2, 0];
- // break;
- // case dist13:
- // points = [0, 2, 1];
- // break;
- // }
- // return { type: TwoToOneFingers, data: points };
- // }
- // else {
- // return { type: ThreeSeperateFingers, data: null };
- // }
- // }
- // default:
- // return { type: undefined };
- // }
- // }
- }
}
diff --git a/src/client/util/LinkFollower.ts b/src/client/util/LinkFollower.ts
index 6c0bf3242..ba715aa1f 100644
--- a/src/client/util/LinkFollower.ts
+++ b/src/client/util/LinkFollower.ts
@@ -1,5 +1,5 @@
import { action, runInAction } from 'mobx';
-import { Doc, DocListCast, Field, FieldResult, Opt } from '../../fields/Doc';
+import { Doc, DocListCast, FieldType, FieldResult, Opt } from '../../fields/Doc';
import { ScriptField } from '../../fields/ScriptField';
import { BoolCast, Cast, DocCast, NumCast, ScriptCast, StrCast } from '../../fields/Types';
import { DocumentType } from '../documents/DocumentTypes';
@@ -138,6 +138,6 @@ ScriptingGlobals.add(function followLink(doc: Doc, altKey: boolean) {
export function FollowLinkScript() {
return ScriptField.MakeScript('return followLink(this,altKey)', { altKey: 'boolean' });
}
-export function IsFollowLinkScript(field: FieldResult) {
+export function IsFollowLinkScript(field: FieldResult) {
return ScriptCast(field)?.script.originalScript.includes('return followLink(');
}
diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts
index 8972bf705..82cd791cc 100644
--- a/src/client/util/LinkManager.ts
+++ b/src/client/util/LinkManager.ts
@@ -1,12 +1,16 @@
-import { action, makeObservable, observable, observe, runInAction } from 'mobx';
+import { action, makeObservable, observable, observe } from 'mobx';
import { computedFn } from 'mobx-utils';
-import { Doc, DocListCast, DocListCastAsync, Field, Opt } from '../../fields/Doc';
+import * as rp from 'request-promise';
+import { ClientUtils } from '../../ClientUtils';
+import { Doc, DocListCast, DocListCastAsync, FieldType, Opt } from '../../fields/Doc';
import { DirectLinks, DocData } from '../../fields/DocSymbols';
import { FieldLoader } from '../../fields/FieldLoader';
+import { Id } from '../../fields/FieldSymbols';
import { List } from '../../fields/List';
import { ProxyField } from '../../fields/Proxy';
import { Cast, DocCast, PromiseValue, StrCast } from '../../fields/Types';
import { DocServer } from '../DocServer';
+import { DocumentType } from '../documents/DocumentTypes';
import { ScriptingGlobals } from './ScriptingGlobals';
/*
* link doc:
@@ -45,6 +49,7 @@ export class LinkManager {
constructor() {
makeObservable(this);
LinkManager._instance = this;
+ Doc.AddLink = this.addLink;
this.createlink_relationshipLists();
// since this is an action, not a reaction, we get only one shot to add this link to the Anchor docs
// Thus make sure all promised values are resolved from link -> link.proto -> link.link_anchor_[1,2] -> link.link_anchor_[1,2].proto
@@ -84,7 +89,7 @@ export class LinkManager {
);
const watchUserLinkDB = (userLinkDBDoc: Doc) => {
- const toRealField = (field: Field) => (field instanceof ProxyField ? field.value : field); // see List.ts. data structure is not a simple list of Docs, but a list of ProxyField/Fields
+ const toRealField = (field: FieldType) => (field instanceof ProxyField ? field.value : field); // see List.ts. data structure is not a simple list of Docs, but a list of ProxyField/Fields
if (userLinkDBDoc.data) {
observe(
userLinkDBDoc.data,
@@ -148,7 +153,7 @@ export class LinkManager {
Doc.AddDocToList(Doc.UserDoc(), 'links', linkDoc);
if (!checkExists || !DocListCast(Doc.LinkDBDoc().data).includes(linkDoc)) {
Doc.AddDocToList(Doc.LinkDBDoc(), 'data', linkDoc);
- setTimeout(DocServer.UPDATE_SERVER_CACHE, 100);
+ setTimeout(UPDATE_SERVER_CACHE, 100);
}
}
public deleteLink(linkDoc: Doc) {
@@ -240,6 +245,42 @@ export class LinkManager {
}
}
+let cacheDocumentIds = ''; // ; separate string of all documents ids in the user's working set (cached on the server)
+export function UPDATE_SERVER_CACHE() {
+ const prototypes = Object.values(DocumentType)
+ .filter(type => type !== DocumentType.NONE)
+ .map(type => DocServer._cache[type + 'Proto'])
+ .filter(doc => doc instanceof Doc)
+ .map(doc => doc as Doc);
+ const references = new Set(prototypes);
+ Doc.FindReferences(Doc.UserDoc(), references, undefined);
+ DocListCast(DocCast(Doc.UserDoc().myLinkDatabase).data).forEach(link => {
+ if (!references.has(DocCast(link.link_anchor_1)) && !references.has(DocCast(link.link_anchor_2))) {
+ Doc.RemoveDocFromList(DocCast(Doc.UserDoc().myLinkDatabase), 'data', link);
+ Doc.AddDocToList(Doc.MyRecentlyClosed, undefined, link);
+ }
+ });
+ LinkManager.Instance.userLinkDBs.forEach(linkDb => Doc.FindReferences(linkDb, references, undefined));
+ const filtered = Array.from(references);
+
+ const newCacheUpdate = filtered.map(doc => doc[Id]).join(';');
+ if (newCacheUpdate === cacheDocumentIds) return;
+ cacheDocumentIds = newCacheUpdate;
+
+ // print out cached docs
+ Doc.MyDockedBtns.linearView_IsOpen && console.log('Set cached docs = ');
+ const isFiltered = filtered.filter(doc => !Doc.IsSystem(doc));
+ const strings = isFiltered.map(doc => StrCast(doc.title) + ' ' + (Doc.IsDataProto(doc) ? '(data)' : '(embedding)'));
+ Doc.MyDockedBtns.linearView_IsOpen && strings.sort().forEach((str, i) => console.log(i.toString() + ' ' + str));
+
+ rp.post(ClientUtils.prepend('/setCacheDocumentIds'), {
+ body: {
+ cacheDocumentIds,
+ },
+ json: true,
+ });
+}
+
ScriptingGlobals.add(
function links(doc: any) {
return new List(LinkManager.Links(doc));
diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts
index 422e708bc..de5e8b92e 100644
--- a/src/client/util/Scripting.ts
+++ b/src/client/util/Scripting.ts
@@ -4,12 +4,14 @@
// // @ts-ignore
// import * as typescriptes5 from '!!raw-loader!../../../node_modules/typescript/lib/lib.es5.d.ts'
// @ts-ignore
-import * as typescriptlib from '!!raw-loader!./type_decls.d';
+// eslint-disable-next-line node/no-unpublished-import
import * as ts from 'typescript';
-import { Doc, Field } from '../../fields/Doc';
+import * as typescriptlib from '!!raw-loader!./type_decls.d';
+import { Doc, FieldType } from '../../fields/Doc';
import { RefField } from '../../fields/RefField';
import { ScriptField } from '../../fields/ScriptField';
-import { scriptingGlobals, ScriptingGlobals } from './ScriptingGlobals';
+import { ScriptingGlobals, scriptingGlobals } from './ScriptingGlobals';
+
export { ts };
export interface ScriptSuccess {
@@ -30,6 +32,7 @@ export type ScriptParam = { [name: string]: string };
export interface CompiledScript {
readonly compiled: true;
readonly originalScript: string;
+ // eslint-disable-next-line no-use-before-define
readonly options: Readonly;
run(args?: { [name: string]: any }, onError?: (res: any) => void, errorVal?: any): ScriptResult;
}
@@ -47,6 +50,7 @@ export function isCompileError(toBeDetermined: CompileResult): toBeDetermined is
return false;
}
+// eslint-disable-next-line no-use-before-define
function Run(script: string | undefined, customParams: string[], diagnostics: any[], originalScript: string, options: ScriptOptions): CompileResult {
const errors = diagnostics.filter(diag => diag.category === ts.DiagnosticCategory.Error);
if ((options.typecheck !== false && errors.length) || !script) {
@@ -60,6 +64,7 @@ function Run(script: string | undefined, customParams: string[], diagnostics: an
// let params: any[] = [Docs, ...fieldTypes];
const compiledFunction = (() => {
try {
+ // eslint-disable-next-line no-new-func
return new Function(...paramNames, `return ${script}`);
} catch (e) {
console.log(e);
@@ -68,20 +73,17 @@ function Run(script: string | undefined, customParams: string[], diagnostics: an
})();
if (!compiledFunction) return { compiled: false, errors };
const { capturedVariables = {} } = options;
+ // eslint-disable-next-line default-param-last
const run = (args: { [name: string]: any } = {}, onError?: (e: any) => void, errorVal?: any): ScriptResult => {
const argsArray: any[] = [];
+ // eslint-disable-next-line no-restricted-syntax
for (const name of customParams) {
- if (name === 'this') {
- continue;
- }
- if (name in args) {
- argsArray.push(args[name]);
- } else {
- argsArray.push(capturedVariables[name]);
+ if (name !== 'this') {
+ argsArray.push(name in args ? args[name] : capturedVariables[name]);
}
}
const thisParam = args.this || capturedVariables.this;
- let batch: { end(): void } | undefined = undefined;
+ let batch: { end(): void } | undefined;
try {
if (!options.editable) {
batch = Doc.MakeReadOnly();
@@ -109,7 +111,7 @@ class ScriptingCompilerHost {
files: File[] = [];
// getSourceFile(fileName: string, languageVersion: ts.ScriptTarget, onError?: ((message: string) => void) | undefined, shouldCreateNewSourceFile?: boolean | undefined): ts.SourceFile | undefined {
- getSourceFile(fileName: string, languageVersion: any, onError?: ((message: string) => void) | undefined, shouldCreateNewSourceFile?: boolean | undefined): any | undefined {
+ getSourceFile(fileName: string, languageVersion: any /* , onError?: ((message: string) => void) | undefined, shouldCreateNewSourceFile?: boolean | undefined */): any | undefined {
const contents = this.readFile(fileName);
if (contents !== undefined) {
return ts.createSourceFile(fileName, contents, languageVersion, true);
@@ -118,7 +120,7 @@ class ScriptingCompilerHost {
}
// getDefaultLibFileName(options: ts.CompilerOptions): string {
- getDefaultLibFileName(options: any): string {
+ getDefaultLibFileName(/* options: any */): string {
return 'node_modules/typescript/lib/lib.d.ts'; // No idea what this means...
}
writeFile(fileName: string, content: string) {
@@ -157,7 +159,7 @@ export type Traverser = (node: ts.Node, indentation: string) => boolean | void;
export type TraverserParam = Traverser | { onEnter: Traverser; onLeave: Traverser };
export type Transformer = {
transformer: ts.TransformerFactory;
- getVars?: () => { [name: string]: Field };
+ getVars?: () => { [name: string]: FieldType };
};
export interface ScriptOptions {
requiredType?: string; // does function required a typed return value
@@ -210,12 +212,13 @@ export function CompileScript(script: string, options: ScriptOptions = {}): Comp
const newCaptures = options.transformer.getVars?.();
if (Object.keys(newCaptures ?? {}).length) {
// tslint:disable-next-line: prefer-object-spread
- //options.capturedVariables = Object.assign(capturedVariables, newCaptures!) as any;
+ // options.capturedVariables = Object.assign(capturedVariables, newCaptures!) as any;
- const transformed = result.transformed;
+ const { transformed } = result;
const printer = ts.createPrinter({
newLine: ts.NewLineKind.LineFeed,
});
+ // eslint-disable-next-line no-param-reassign
script = printer.printFile(transformed[0].getSourceFile());
}
result.dispose();
@@ -224,19 +227,23 @@ export function CompileScript(script: string, options: ScriptOptions = {}): Comp
if ('this' in params || 'this' in capturedVariables) {
paramNames.push('this');
}
+ // eslint-disable-next-line no-restricted-syntax
for (const key in params) {
- if (key === 'this') continue;
- paramNames.push(key);
+ if (key !== 'this') {
+ paramNames.push(key);
+ }
}
const paramList = paramNames.map(key => {
const val = params[key];
return `${key}: ${val}`;
});
+ // eslint-disable-next-line no-restricted-syntax
for (const key in capturedVariables) {
- if (key === 'this') continue;
- const val = capturedVariables[key];
- paramNames.push(key);
- paramList.push(`${key}: ${typeof val === 'object' ? Object.getPrototypeOf(val).constructor.name : typeof val}`);
+ if (key !== 'this') {
+ const val = capturedVariables[key];
+ paramNames.push(key);
+ paramList.push(`${key}: ${typeof val === 'object' ? Object.getPrototypeOf(val).constructor.name : typeof val}`);
+ }
}
const paramString = paramList.join(', ');
const body = addReturn && !script.startsWith('{ return') ? `return ${script};` : script;
@@ -261,6 +268,7 @@ export function CompileScript(script: string, options: ScriptOptions = {}): Comp
}
ScriptingGlobals.add(CompileScript);
+// eslint-disable-next-line prefer-arrow-callback
ScriptingGlobals.add(function runScript(doc: Doc, script: ScriptField) {
return script?.script.run({ this: doc }).result;
});
diff --git a/src/client/util/ScriptingGlobals.ts b/src/client/util/ScriptingGlobals.ts
index f151acd81..bc159ed65 100644
--- a/src/client/util/ScriptingGlobals.ts
+++ b/src/client/util/ScriptingGlobals.ts
@@ -1,8 +1,18 @@
+import ts from 'typescript';
-import * as ts from "typescript";
export { ts };
+const _scriptingGlobals: { [name: string]: any } = {};
+const _scriptingDescriptions: { [name: string]: any } = {};
+const _scriptingParams: { [name: string]: any } = {};
+// eslint-disable-next-line import/no-mutable-exports
+export let scriptingGlobals: { [name: string]: any } = _scriptingGlobals;
export namespace ScriptingGlobals {
+ export function getGlobals() { return Object.keys(_scriptingGlobals); } // prettier-ignore
+ export function getGlobalObj() { return _scriptingGlobals; } // prettier-ignore
+ export function getDescriptions() { return _scriptingDescriptions; } // prettier-ignore
+ export function getParameters() { return _scriptingParams; } // prettier-ignore
+
export function add(global: { name: string }): void;
export function add(name: string, global: any): void;
export function add(global: { name: string }, decription?: string, params?: string): void;
@@ -11,7 +21,7 @@ export namespace ScriptingGlobals {
let obj: any;
if (second !== undefined) {
- if (typeof first === "string") {
+ if (typeof first === 'string') {
n = first;
obj = second;
} else {
@@ -22,18 +32,20 @@ export namespace ScriptingGlobals {
_scriptingParams[n] = third;
}
}
- } else if (first && typeof first.name === "string") {
+ } else if (first && typeof first.name === 'string') {
n = first.name;
obj = first;
} else {
- throw new Error("Must either register an object with a name, or give a name and an object");
+ throw new Error('Must either register an object with a name, or give a name and an object');
}
- if (n === undefined || n === "undefined") {
+ if (n === undefined || n === 'undefined') {
return false;
- } else if (_scriptingGlobals.hasOwnProperty(n)) {
+ }
+ if (_scriptingGlobals.hasOwnProperty(n)) {
throw new Error(`Global with name ${n} is already registered, choose another name`);
}
_scriptingGlobals[n] = obj;
+ return true;
}
export function makeMutableGlobalsCopy(globals?: { [name: string]: any }) {
return { ..._scriptingGlobals, ...(globals || {}) };
@@ -57,25 +69,16 @@ export namespace ScriptingGlobals {
return false;
}
- export function resetScriptingGlobals() { scriptingGlobals = _scriptingGlobals; }
+ export function resetScriptingGlobals() {
+ scriptingGlobals = _scriptingGlobals;
+ }
// const types = Object.keys(ts.SyntaxKind).map(kind => ts.SyntaxKind[kind]);
- export function printNodeType(node: any, indentation = "") { console.log(indentation + ts.SyntaxKind[node.kind]); }
-
- export function getGlobals() { return Object.keys(_scriptingGlobals); }
-
- export function getGlobalObj() { return _scriptingGlobals; }
-
- export function getDescriptions() { return _scriptingDescriptions; }
-
- export function getParameters() { return _scriptingParams; }
+ export function printNodeType(node: any, indentation = '') {
+ console.log(indentation + ts.SyntaxKind[node.kind]);
+ }
}
-export function scriptingGlobal(constructor: { new(...args: any[]): any }) {
+export function scriptingGlobal(constructor: { new (...args: any[]): any }) {
ScriptingGlobals.add(constructor);
}
-
-const _scriptingGlobals: { [name: string]: any } = {};
-export let scriptingGlobals: { [name: string]: any } = _scriptingGlobals;
-const _scriptingDescriptions: { [name: string]: any } = {};
-const _scriptingParams: { [name: string]: any } = {};
\ No newline at end of file
diff --git a/src/client/util/SearchUtil.ts b/src/client/util/SearchUtil.ts
index fff2737b6..65b9a977d 100644
--- a/src/client/util/SearchUtil.ts
+++ b/src/client/util/SearchUtil.ts
@@ -1,5 +1,5 @@
import { ObservableMap } from 'mobx';
-import { Doc, DocListCast, Field, Opt } from '../../fields/Doc';
+import { Doc, DocListCast, Field, FieldType, Opt } from '../../fields/Doc';
import { Id } from '../../fields/FieldSymbols';
import { StrCast } from '../../fields/Types';
import { DocumentType } from '../documents/DocumentTypes';
@@ -30,7 +30,7 @@ export namespace SearchUtil {
(onlyKeys ?? SearchUtil.documentKeys(doc)).forEach(
key =>
(val => (exact ? val === query.toLowerCase() : val.includes(query.toLowerCase())))(
- matchKeyNames ? key : Field.toString(doc[key] as Field))
+ matchKeyNames ? key : Field.toString(doc[key] as FieldType))
&& hlights.add(key)
); // prettier-ignore
blockedKeys.forEach(key => hlights.delete(key));
@@ -71,7 +71,7 @@ export namespace SearchUtil {
docs.filter(d => d && !visited.includes(d)).forEach(d => {
visited.push(d);
const fieldKey = Doc.LayoutFieldKey(d);
- const annos = !Field.toString(Doc.LayoutField(d) as Field).includes('CollectionView');
+ const annos = !Field.toString(Doc.LayoutField(d) as FieldType).includes('CollectionView');
const data = d[annos ? fieldKey + '_annotations' : fieldKey];
data && newarray.push(...DocListCast(data));
const sidebar = d[fieldKey + '_sidebar'];
diff --git a/src/client/util/SerializationHelper.ts b/src/client/util/SerializationHelper.ts
index 8daa69890..fa1a911f7 100644
--- a/src/client/util/SerializationHelper.ts
+++ b/src/client/util/SerializationHelper.ts
@@ -1,5 +1,5 @@
import { PropSchema, serialize, deserialize, custom, setDefaultModelSchema, getDefaultModelSchema } from 'serializr';
-import { Field } from '../../fields/Doc';
+// import { Field } from '../../fields/Doc';
let serializing = 0;
export function afterDocDeserialize(cb: (err: any, val: any) => void, err: any, newValue: any) {
@@ -7,12 +7,16 @@ export function afterDocDeserialize(cb: (err: any, val: any) => void, err: any,
cb(err, newValue);
serializing--;
}
+
+const serializationTypes: { [name: string]: { ctor: { new (): any }; afterDeserialize?: (obj: any) => void | Promise } } = {};
+const reverseMap: { [ctor: string]: string } = {};
+
export namespace SerializationHelper {
export function IsSerializing() {
return serializing > 0;
}
- export function Serialize(obj: Field): any {
+ export function Serialize(obj: any /* Field */): any {
if (obj === undefined || obj === null) {
return null;
}
@@ -24,7 +28,7 @@ export namespace SerializationHelper {
serializing++;
if (!(obj.constructor.name in reverseMap)) {
serializing--;
- throw Error('Error: ' + `type '${obj.constructor.name}' not registered. Make sure you register it using a @Deserializable decorator`);
+ throw Error(`Error: type '${obj.constructor.name}' not registered. Make sure you register it using a @Deserializable decorator`);
}
const json = serialize(obj);
@@ -52,25 +56,24 @@ export namespace SerializationHelper {
}
const type = serializationTypes[obj.__type];
- const value = await new Promise(res => deserialize(type.ctor, obj, (err, result) => res(result)));
+ const value = await new Promise(res => {
+ deserialize(type.ctor, obj, (err, result) => res(result));
+ });
type.afterDeserialize?.(value);
return value;
}
}
-const serializationTypes: { [name: string]: { ctor: { new (): any }; afterDeserialize?: (obj: any) => void | Promise } } = {};
-const reverseMap: { [ctor: string]: string } = {};
-
export function Deserializable(className: string, afterDeserialize?: (obj: any) => void | Promise, constructorArgs?: [string]): (constructor: { new (...args: any[]): any }) => void {
- function addToMap(className: string, ctor: { new (...args: any[]): any }) {
- const schema = getDefaultModelSchema(ctor) as any;
- if (schema.targetClass !== ctor || constructorArgs) {
- setDefaultModelSchema(ctor, { ...schema, factory: (context: any) => new ctor(...(constructorArgs ?? [])?.map(arg => context.json[arg])) });
+ function addToMap(className: string, Ctor: { new (...args: any[]): any }) {
+ const schema = getDefaultModelSchema(Ctor) as any;
+ if (schema.targetClass !== Ctor || constructorArgs) {
+ setDefaultModelSchema(Ctor, { ...schema, factory: (context: any) => new Ctor(...(constructorArgs ?? []).map(arg => context.json[arg])) });
}
if (!(className in serializationTypes)) {
- serializationTypes[className] = { ctor, afterDeserialize };
- reverseMap[ctor.name] = className;
+ serializationTypes[className] = { ctor: Ctor, afterDeserialize };
+ reverseMap[Ctor.name] = className;
} else {
throw new Error(`Name ${className} has already been registered as deserializable`);
}
diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx
index 8594a1c92..e2971895a 100644
--- a/src/client/util/SettingsManager.tsx
+++ b/src/client/util/SettingsManager.tsx
@@ -1,11 +1,11 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Button, ColorPicker, Dropdown, DropdownType, EditableText, Group, NumberDropdown, Size, Toggle, ToggleType, Type } from 'browndash-components';
-import { action, computed, makeObservable, observable, runInAction } from 'mobx';
+import { action, computed, makeObservable, observable, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { BsGoogle } from 'react-icons/bs';
import { FaFillDrip, FaPalette } from 'react-icons/fa';
-import { Utils, addStyleSheet, addStyleSheetRule } from '../../Utils';
+import { ClientUtils, addStyleSheet, addStyleSheetRule } from '../../ClientUtils';
import { Doc, Opt } from '../../fields/Doc';
import { DashVersion } from '../../fields/DocSymbols';
import { BoolCast, Cast, NumCast, StrCast } from '../../fields/Types';
@@ -18,6 +18,7 @@ import { FontIconBox } from '../views/nodes/FontIconBox/FontIconBox';
import { GroupManager } from './GroupManager';
import './SettingsManager.scss';
import { undoBatch } from './UndoManager';
+import { SnappingManager } from './SnappingManager';
export enum ColorScheme {
Dark = 'Dark',
@@ -34,6 +35,7 @@ export enum freeformScrollMode {
@observer
export class SettingsManager extends React.Component<{}> {
+ // eslint-disable-next-line no-use-before-define
public static Instance: SettingsManager;
static _settingsStyle = addStyleSheet();
@observable public isOpen = false;
@@ -60,6 +62,16 @@ export class SettingsManager extends React.Component<{}> {
}
// undefined means ColorScheme.Light until all CSS is updated with values for each color scheme (e.g., see MainView.scss, DocumentDecorations.scss)
});
+ reaction(
+ () => [SettingsManager.userBackgroundColor, SettingsManager.userColor, SettingsManager.userVariantColor],
+ ([back, user, variant]) => {
+ SnappingManager.userBackgroundColor = back;
+ SnappingManager.userVariantColor = variant;
+ SnappingManager.userColor = user;
+ },
+ { fireImmediately: true }
+ );
+ SnappingManager.SettingsStyle = SettingsManager._settingsStyle;
}
matchSystem = () => {
if (Doc.UserDoc().userThemeSystem) {
@@ -116,7 +128,7 @@ export class SettingsManager extends React.Component<{}> {
if (this.playgroundMode) {
DocServer.Control.makeReadOnly();
addStyleSheetRule(SettingsManager._settingsStyle, 'topbar-inner-container', { background: 'red !important' });
- } else Doc.CurrentUserEmail !== 'guest' && DocServer.Control.makeEditable();
+ } else ClientUtils.CurrentUserEmail !== 'guest' && DocServer.Control.makeEditable();
});
@undoBatch
@@ -176,7 +188,7 @@ export class SettingsManager extends React.Component<{}> {
{userTheme === ColorScheme.Custom && (
}
@@ -185,7 +197,7 @@ export class SettingsManager extends React.Component<{}> {
setFinalColor={this.switchUserColor}
/>
}
@@ -194,7 +206,7 @@ export class SettingsManager extends React.Component<{}> {
setFinalColor={this.switchUserBackgroundColor}
/>
}
@@ -212,8 +224,8 @@ export class SettingsManager extends React.Component<{}> {
return (
(Doc.UserDoc().layout_showTitle = Doc.UserDoc().layout_showTitle ? undefined : 'author_date')}
toggleStatus={Doc.UserDoc().layout_showTitle !== undefined}
@@ -221,8 +233,8 @@ export class SettingsManager extends React.Component<{}> {
color={SettingsManager.userColor}
/>
(Doc.UserDoc()['documentLinksButton-fullMenu'] = !Doc.UserDoc()['documentLinksButton-fullMenu'])}
toggleStatus={BoolCast(Doc.UserDoc()['documentLinksButton-fullMenu'])}
@@ -230,8 +242,8 @@ export class SettingsManager extends React.Component<{}> {
color={SettingsManager.userColor}
/>
(FontIconBox.ShowIconLabels = !FontIconBox.ShowIconLabels)}
toggleStatus={FontIconBox.ShowIconLabels}
@@ -239,8 +251,8 @@ export class SettingsManager extends React.Component<{}> {
color={SettingsManager.userColor}
/>
(GestureOverlay.RecognizeGestures = !GestureOverlay.RecognizeGestures)}
toggleStatus={GestureOverlay.RecognizeGestures}
@@ -248,8 +260,8 @@ export class SettingsManager extends React.Component<{}> {
color={SettingsManager.userColor}
/>
(Doc.UserDoc().activeInkHideTextLabels = !Doc.UserDoc().activeInkHideTextLabels)}
toggleStatus={BoolCast(Doc.UserDoc().activeInkHideTextLabels)}
@@ -257,8 +269,8 @@ export class SettingsManager extends React.Component<{}> {
color={SettingsManager.userColor}
/>
(Doc.UserDoc().openInkInLightbox = !Doc.UserDoc().openInkInLightbox)}
toggleStatus={BoolCast(Doc.UserDoc().openInkInLightbox)}
@@ -266,8 +278,8 @@ export class SettingsManager extends React.Component<{}> {
color={SettingsManager.userColor}
/>
(Doc.UserDoc().showLinkLines = !Doc.UserDoc().showLinkLines)}
toggleStatus={BoolCast(Doc.UserDoc().showLinkLines)}
@@ -278,12 +290,12 @@ export class SettingsManager extends React.Component<{}> {