From 7e4d793eaa7e5b6b564355a11fa02a5611645f20 Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 21 Mar 2025 18:58:21 -0400 Subject: trying to improve how data / layout / root and templtae docs are accessed. --- src/client/views/PropertiesView.tsx | 42 ++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 21 deletions(-) (limited to 'src/client/views/PropertiesView.tsx') diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index 11adf7435..c72f958fc 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -282,7 +282,7 @@ export class PropertiesView extends ObservableReactComponent(tags) : undefined; + doc.$tags = tags.length ? new List(tags) : undefined; } return true; } @@ -498,7 +498,7 @@ export class PropertiesView extends ObservableReactComponent { let userOnDoc = true; @@ -777,7 +777,7 @@ export class PropertiesView extends ObservableReactComponent doc.layout_isSvg); + return this.containsInkDoc ? DocListCast(this.selectedDoc.$data) : DocumentView.SelectedSchemaDoc() ? [DocumentView.SelectedSchemaDoc()!] : DocumentView.SelectedDocs().filter(doc => doc.layout_isSvg); } @computed get shapeXps() { return NumCast(this.selectedDoc?.x); } // prettier-ignore set shapeXps(value) { this.selectedDoc && (this.selectedDoc.x = Math.round(value * 100) / 100); } // prettier-ignore @@ -839,10 +839,10 @@ export class PropertiesView extends ObservableReactComponent { - doc[DocData].stroke_width = Math.round(value * 100) / 100; + doc.$stroke_width = Math.round(value * 100) / 100; }); } @@ -881,20 +881,20 @@ export class PropertiesView extends ObservableReactComponent { const inkStroke = DocumentView.getDocumentView(doc)?.ComponentView as InkingStroke; const { inkData } = inkStroke.inkScaledData(); if (InkingStroke.IsClosed(inkData)) { - doc[DocData].fillColor = value || undefined; + doc.$fillColor = value || undefined; } }); } - @computed get colorStk() { return StrCast(this.selectedStrokes.lastElement()?.[DocData].color); } // prettier-ignore + @computed get colorStk() { return StrCast(this.selectedStrokes.lastElement()?.$olor); } // prettier-ignore set colorStk(value) { this.selectedStrokes.forEach(doc => { - doc[DocData].color = value || undefined; + doc.$color = value || undefined; }); } @computed get borderColor() { @@ -902,7 +902,7 @@ export class PropertiesView extends ObservableReactComponent void) { return ( @@ -1034,41 +1034,41 @@ export class PropertiesView extends ObservableReactComponent { - doc[DocData].stroke_dash = value ? this._lastDash : undefined; + doc.$stroke_dash = value ? this._lastDash : undefined; }); } @computed get widthStk() { return this.getField('stroke_width') || '1'; } // prettier-ignore set widthStk(value) { this.selectedStrokes.forEach(doc => { - doc[DocData].stroke_width = Number(value); + doc.$stroke_width = Number(value); }); } @computed get markScal() { return Number(this.getField('stroke_markerScale') || '1'); } // prettier-ignore set markScal(value) { this.selectedStrokes.forEach(doc => { - doc[DocData].stroke_markerScale = Number(value); + doc.$stroke_markerScale = Number(value); }); } @computed get refStrength() { return Number(this.getField('drawing_refStrength') || '50'); } // prettier-ignore set refStrength(value) { - this.selectedDoc[DocData].drawing_refStrength = Number(value); + this.selectedDoc.$drawing_refStrength = Number(value); } @computed get smoothAmt() { return Number(this.getField('stroke_smoothAmount') || '5'); } // prettier-ignore set smoothAmt(value) { this.selectedStrokes.forEach(doc => { - doc[DocData].stroke_smoothAmount = Number(value); + doc.$stroke_smoothAmount = Number(value); }); } @computed get markHead() { return this.getField('stroke_startMarker') || ''; } // prettier-ignore set markHead(value) { this.selectedStrokes.forEach(doc => { - doc[DocData].stroke_startMarker = value; + doc.$stroke_startMarker = value; }); } @computed get markTail() { return this.getField('stroke_endMarker') || ''; } // prettier-ignore set markTail(value) { this.selectedStrokes.forEach(doc => { - doc[DocData].stroke_endMarker = value; + doc.$stroke_endMarker = value; }); } @@ -1356,7 +1356,7 @@ export class PropertiesView extends ObservableReactComponent { - const childDocs: Doc[] = DocListCast(selectedDoc[DocData].data); + const childDocs: Doc[] = DocListCast(selectedDoc.$data); for (let i = 0; i < childDocs.length; i++) { if (DocumentView.getDocumentView(childDocs[i])?.layoutDoc?.layout_isSvg) { return true; @@ -1454,7 +1454,7 @@ export class PropertiesView extends ObservableReactComponent { if (this.selectedLink) { - this.selectedLink[DocData].link_description = value; + this.selectedLink.$link_description = value; } }); -- cgit v1.2.3-70-g09d2 From bc308b888f41e8789f1b9f522ced46e68e726862 Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 21 Mar 2025 19:24:06 -0400 Subject: from last --- src/client/documents/DocUtils.ts | 4 ++-- src/client/util/CalendarManager.tsx | 5 +++-- src/client/util/CurrentUserUtils.ts | 4 ++-- src/client/views/DocComponent.tsx | 2 +- src/client/views/PinFuncs.ts | 4 ++-- src/client/views/PropertiesButtons.tsx | 2 +- src/client/views/PropertiesView.tsx | 2 +- src/client/views/nodes/DocumentView.tsx | 8 ++++---- src/fields/Doc.ts | 4 +--- 9 files changed, 17 insertions(+), 18 deletions(-) (limited to 'src/client/views/PropertiesView.tsx') diff --git a/src/client/documents/DocUtils.ts b/src/client/documents/DocUtils.ts index 13f6f6920..b0170a192 100644 --- a/src/client/documents/DocUtils.ts +++ b/src/client/documents/DocUtils.ts @@ -383,8 +383,8 @@ export namespace DocUtils { const newDoc = DocUtils.copyDragFactory(dragDoc); if (newDoc) { newDoc._author = ClientUtils.CurrentUserEmail(); - newDoc._x = x; - newDoc._y = y; + newDoc.x = x; + newDoc.y = y; newDoc.$backgroundColor = Doc.UserDoc().textBackgroundColor; DocumentView.SetSelectOnLoad(newDoc); if (pivotField) { diff --git a/src/client/util/CalendarManager.tsx b/src/client/util/CalendarManager.tsx index a0db4e32e..3357e5d59 100644 --- a/src/client/util/CalendarManager.tsx +++ b/src/client/util/CalendarManager.tsx @@ -17,6 +17,7 @@ import { TaskCompletionBox } from '../views/nodes/TaskCompletedBox'; import './CalendarManager.scss'; import { SnappingManager } from './SnappingManager'; import { CalendarDate, DateValue } from '@internationalized/date'; +import { DocData } from '../../fields/DocSymbols'; // import 'react-date-range/dist/styles.css'; // import 'react-date-range/dist/theme/default.css'; @@ -118,7 +119,7 @@ export class CalendarManager extends ObservableReactComponent { // TODO: Make undoable private addToCalendar = () => { const docs = DocumentView.Selected().length < 2 ? [this.targetDoc] : DocumentView.Selected().map(docView => docView.Document); - const targetDoc = docs[0]; // doc to add to calendar + const targetDoc = docs[0]?.[DocData]; if (targetDoc) { let calendar: Doc; @@ -229,7 +230,7 @@ export class CalendarManager extends ObservableReactComponent { @computed get calendarInterface() { const docs = DocumentView.Selected().length < 2 ? [this.targetDoc] : DocumentView.Selected().map(docView => docView.Document); - const targetDoc = docs[0]; + const targetDoc = docs[0]?.[DocData]; return (
`; slide.$type_collection = CollectionViewType.Freeform; - slide._onPaint = ScriptField.MakeScript(`toggleDetail(documentView, "textPainted")`, {documentView:"any"}); + slide.onPaint = ScriptField.MakeScript(`toggleDetail(documentView, "textPainted")`, {documentView:"any"}); return slide; } const mermaidsApi = () => { @@ -376,7 +376,7 @@ pie title Minerals in my tap water slide.$text = rtfield; slide.$layout_textPainted = ``; slide.$_type_collection = CollectionViewType.Freeform; - slide._onPaint = ScriptField.MakeScript(`toggleDetail(documentView, "textPainted")`, {documentView:"any"}); + slide.onPaint = ScriptField.MakeScript(`toggleDetail(documentView, "textPainted")`, {documentView:"any"}); return slide; } plotlyApi(); mermaidsApi(); diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index 064906530..79a0cc602 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -42,7 +42,7 @@ export function DocComponent

() { * This is the document being rendered. It may be a template so it may or may no inherit from the data doc. */ @computed get layoutDoc() { - return this._props.LayoutTemplateString ? this.Document : Doc.Layout(this._props.Document, this._props.LayoutTemplate?.()); + return this._props.LayoutTemplateString ? this._props.Document : Doc.Layout(this._props.Document, this._props.LayoutTemplate?.()); } /** diff --git a/src/client/views/PinFuncs.ts b/src/client/views/PinFuncs.ts index 1ab8575a8..d756830da 100644 --- a/src/client/views/PinFuncs.ts +++ b/src/client/views/PinFuncs.ts @@ -76,8 +76,8 @@ export function PinDocView(pinDocIn: Doc, pinProps: PinProps, targetDoc: Doc) { pinDoc.config_data = Field.Copy(targetDoc[fkey]); } if (pinProps.pinData.dataannos) { - const fieldKey = Doc.LayoutFieldKey(targetDoc); - pinDoc.config_annotations = new List(DocListCast(targetDoc['$' + fieldKey + '_annotations']).filter(doc => !doc.layout_unrendered)); + const fieldKey = '$' + Doc.LayoutFieldKey(targetDoc) + +'_annotations'; + pinDoc.config_annotations = new List(DocListCast(targetDoc[fieldKey]).filter(doc => !doc.layout_unrendered)); } if (pinProps.pinData.inkable) { pinDoc.config_fillColor = targetDoc.fillColor; diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx index 2b4fe478e..a1e8fe7ba 100644 --- a/src/client/views/PropertiesButtons.tsx +++ b/src/client/views/PropertiesButtons.tsx @@ -131,7 +131,7 @@ export class PropertiesButtons extends React.Component { on => `${on ? 'Flashcard enabled' : 'Flashcard disabled'} `, () => , (dv, doc) => { - const on = !!doc.onPaint; + const on = !!doc.$onPaint; doc.$onPaint = on ? undefined : ScriptField.MakeScript(`toggleDetail(documentView, "textPainted")`, { documentView: 'any' }); doc.$layout_textPainted = on ? undefined : ``; } diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index c72f958fc..b95553c4e 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -891,7 +891,7 @@ export class PropertiesView extends ObservableReactComponent { doc.$color = value || undefined; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index fdaf13733..46bd076d4 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -143,10 +143,10 @@ export class DocumentViewInternal extends DocComponent Date: Sun, 23 Mar 2025 10:27:28 -0400 Subject: updated more [DocData] to .$ things --- src/client/util/CalendarManager.tsx | 5 +-- src/client/util/LinkManager.ts | 6 +-- src/client/views/DocComponent.tsx | 9 ++-- src/client/views/PropertiesDocContextSelector.tsx | 2 +- src/client/views/PropertiesView.tsx | 8 +--- src/client/views/SidebarAnnos.tsx | 9 ++-- src/client/views/StyleProviderQuiz.tsx | 8 ++-- src/client/views/TagsView.tsx | 33 +++++++-------- .../views/collections/CollectionDockingView.tsx | 6 +-- .../collections/CollectionMasonryViewFieldRow.tsx | 3 +- src/client/views/collections/CollectionMenu.tsx | 3 +- .../views/collections/CollectionNoteTakingView.tsx | 2 +- .../collections/CollectionNoteTakingViewColumn.tsx | 2 +- .../collections/CollectionStackedTimeline.tsx | 8 ++-- .../views/collections/CollectionStackingView.tsx | 5 +-- src/client/views/collections/CollectionSubView.tsx | 12 +++++- src/client/views/collections/TabDocView.tsx | 2 +- src/client/views/collections/TreeView.tsx | 5 +-- .../CollectionFreeFormInfoUI.tsx | 11 +++-- .../collectionFreeForm/CollectionFreeFormView.tsx | 25 ++++++----- .../collectionFreeForm/FaceCollectionBox.tsx | 4 +- .../collectionFreeForm/ImageLabelBox.tsx | 19 ++++----- .../collections/collectionFreeForm/MarqueeView.tsx | 19 ++++----- src/client/views/global/globalScripts.ts | 49 +++++++++------------- src/client/views/nodes/AudioBox.tsx | 2 +- src/client/views/nodes/ComparisonBox.tsx | 10 ++--- .../views/nodes/DataVizBox/components/TableBox.tsx | 4 +- src/client/views/nodes/DiagramBox.tsx | 5 +-- src/client/views/nodes/DocumentView.tsx | 14 +++---- src/client/views/nodes/ImageBox.tsx | 42 +++++++++---------- src/client/views/nodes/KeyValueBox.tsx | 2 +- src/client/views/nodes/LinkBox.tsx | 6 +-- src/client/views/nodes/LinkDescriptionPopup.tsx | 3 +- src/client/views/nodes/PDFBox.tsx | 17 ++++---- .../views/nodes/RecordingBox/RecordingBox.tsx | 3 +- src/client/views/nodes/VideoBox.tsx | 48 ++++++++++----------- src/client/views/nodes/calendarBox/CalendarBox.tsx | 2 +- .../nodes/chatbot/chatboxcomponents/ChatBox.tsx | 6 +-- .../views/nodes/formattedText/EquationView.tsx | 5 +-- src/client/views/nodes/imageEditor/ImageEditor.tsx | 9 ++-- src/client/views/nodes/trails/PresBox.tsx | 9 ++-- src/client/views/smartdraw/SmartDrawHandler.tsx | 21 +++++----- src/client/views/smartdraw/StickerPalette.tsx | 28 ++++++------- src/fields/Doc.ts | 25 ++++++----- src/fields/ScriptField.ts | 3 +- 45 files changed, 243 insertions(+), 276 deletions(-) (limited to 'src/client/views/PropertiesView.tsx') diff --git a/src/client/util/CalendarManager.tsx b/src/client/util/CalendarManager.tsx index 3357e5d59..1a5a5f889 100644 --- a/src/client/util/CalendarManager.tsx +++ b/src/client/util/CalendarManager.tsx @@ -17,7 +17,6 @@ import { TaskCompletionBox } from '../views/nodes/TaskCompletedBox'; import './CalendarManager.scss'; import { SnappingManager } from './SnappingManager'; import { CalendarDate, DateValue } from '@internationalized/date'; -import { DocData } from '../../fields/DocSymbols'; // import 'react-date-range/dist/styles.css'; // import 'react-date-range/dist/theme/default.css'; @@ -119,7 +118,7 @@ export class CalendarManager extends ObservableReactComponent { // TODO: Make undoable private addToCalendar = () => { const docs = DocumentView.Selected().length < 2 ? [this.targetDoc] : DocumentView.Selected().map(docView => docView.Document); - const targetDoc = docs[0]?.[DocData]; + const targetDoc = docs[0]; if (targetDoc) { let calendar: Doc; @@ -230,7 +229,7 @@ export class CalendarManager extends ObservableReactComponent { @computed get calendarInterface() { const docs = DocumentView.Selected().length < 2 ? [this.targetDoc] : DocumentView.Selected().map(docView => docView.Document); - const targetDoc = docs[0]?.[DocData]; + const targetDoc = docs[0]; return (
!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'), { diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index 79a0cc602..a311f6436 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -49,7 +49,7 @@ export function DocComponent

() { * This is the unique data repository for a dcoument that stores the intrinsic document data */ @computed get dataDoc() { - return this.Document[DocData]; + return this._props.Document[DocData]; } } return Component; @@ -93,7 +93,7 @@ export function ViewBoxBaseComponent

() { * This is the unique data repository for a dcoument that stores the intrinsic document data */ @computed get dataDoc() { - return this.Document.isTemplateForField || this.Document.isTemplateDoc ? (this._props.TemplateDataDocument ?? this.Document[DocData]) : this.Document[DocData]; + return this._props.Document.isTemplateForField || this._props.Document.isTemplateDoc ? (this._props.TemplateDataDocument ?? this._props.Document[DocData]) : this._props.Document[DocData]; } /** @@ -151,7 +151,7 @@ export function ViewBoxAnnotatableComponent

() { * This is the unique data repository for a dcoument that stores the intrinsic document data */ @computed get dataDoc() { - return this.Document.isTemplateForField || this.Document.isTemplateDoc ? (this._props.TemplateDataDocument ?? this.Document[DocData]) : this.Document[DocData]; + return this._props.Document.isTemplateForField || this._props.Document.isTemplateDoc ? (this._props.TemplateDataDocument ?? this._props.Document[DocData]) : this._props.Document[DocData]; } /** @@ -225,8 +225,7 @@ export function ViewBoxAnnotatableComponent

() { if ([AclAugment, AclEdit, AclAdmin].includes(effectiveAcl)) { added.forEach(adoc => { adoc._dragOnlyWithinContainer = undefined; - if (annotationKey ?? this._annotationKeySuffix()) adoc[DocData].annotationOn = this.Document; - else adoc[DocData].annotationOn = undefined; + adoc.$annotationOn = (annotationKey ?? this._annotationKeySuffix()) ? this.Document : undefined; Doc.SetContainer(adoc, this.Document); inheritParentAcls(targetDataDoc, adoc, true); }); diff --git a/src/client/views/PropertiesDocContextSelector.tsx b/src/client/views/PropertiesDocContextSelector.tsx index f494ff16a..ee6486a9c 100644 --- a/src/client/views/PropertiesDocContextSelector.tsx +++ b/src/client/views/PropertiesDocContextSelector.tsx @@ -26,7 +26,7 @@ export class PropertiesDocContextSelector extends ObservableReactComponent embedding.embedContainer && embedding.embedContainer instanceof Doc).reduce((set, embedding) => set.add(Cast(embedding.embedContainer, Doc, null)), new Set()); diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index b95553c4e..7e9cd002b 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -181,7 +181,7 @@ export class PropertiesView extends ObservableReactComponent { const layoutDoc = this.selectedLayoutDoc; - if (layoutDoc && this.dataDoc) { + if (layoutDoc) { return Math.max( 70, Math.min( @@ -308,11 +308,7 @@ export class PropertiesView extends ObservableReactComponent data.split(':')[0]) .filter(data => !filterExlusions?.includes(data.split(':')[0])) .map(data => { - const key = data.split(':')[0]; + const key = '$'+data.split(':')[0]; const val = Field.Copy(this.allMetadata.get(key)); - target[DocData][key] = val; + target[key] = val; return { type: 'dashField', attrs: { fieldKey: key, docId: '', hideKey: false, hideValue: false, editable: true }, @@ -98,7 +97,7 @@ export class SidebarAnnos extends ObservableReactComponent { - const input = StrCast(doc[DocData].title); + const input = StrCast(doc.$title); if (imgQuizMode(img) == quizMode.SMART && input) { const questionText = 'Question: What was labeled in this image?'; const rubricText = ' Rubric: ' + StrCast(doc.quiz); @@ -275,8 +275,8 @@ export namespace styleProviderQuiz { function redo(img: ImageBox) { imgQuizBoxes(img).forEach(doc => { - doc[DocData].title = ''; - doc.backgroundColor = '#e4e4e4'; + doc.$title = ''; + doc.$backgroundColor = '#e4e4e4'; }); } diff --git a/src/client/views/TagsView.tsx b/src/client/views/TagsView.tsx index 93d6fb684..7d2a6be5e 100644 --- a/src/client/views/TagsView.tsx +++ b/src/client/views/TagsView.tsx @@ -72,7 +72,7 @@ export class TagItem extends ObservableReactComponent { const newTagCol = new Doc(); newTagCol.title = tag; newTagCol.collections = new List(); - newTagCol[DocData].docs = new List(); + newTagCol.$docs = new List(); Doc.ActiveDashboard && Doc.AddDocToList(Doc.ActiveDashboard, 'myTagCollections', newTagCol); return newTagCol; @@ -82,7 +82,7 @@ export class TagItem extends ObservableReactComponent { * @param tag tag string * @returns An array of documents that contain the tag. */ - public static allDocsWithTag = (tag: string) => DocListCast(TagItem.findTagCollectionDoc(tag)?.[DocData].docs); + public static allDocsWithTag = (tag: string) => DocListCast(TagItem.findTagCollectionDoc(tag)?.$docs); public static docHasTag = (doc: Doc, tag: string) => StrListCast(doc?.tags).includes(tag); /** @@ -95,11 +95,11 @@ export class TagItem extends ObservableReactComponent { // If the document is of type COLLECTION, make it a smart collection, otherwise, add the tag to the document. if (doc.type === DocumentType.COL && !doc.annotationOn) { - Doc.AddDocToList(tagCollection[DocData], 'collections', doc); + Doc.AddDocToList(tagCollection, 'collections', doc); // Iterate through the tag Doc collections and add a copy of the document to each collection - for (const cdoc of DocListCast(tagCollection[DocData].docs)) { - if (!DocListCast(doc[DocData].data).find(d => Doc.AreProtosEqual(d, cdoc))) { + for (const cdoc of DocListCast(tagCollection.$docs)) { + if (!DocListCast(doc.$data).find(d => Doc.AreProtosEqual(d, cdoc))) { const newEmbedding = Doc.MakeEmbedding(cdoc); Doc.AddDocToList(doc[DocData], 'data', newEmbedding); Doc.SetContainer(newEmbedding, doc); @@ -111,7 +111,7 @@ export class TagItem extends ObservableReactComponent { // Iterate through the tag document's collections and add a copy of the document to each collection for (const collection of DocListCast(tagCollection.collections)) { - if (!DocListCast(collection[DocData].data).find(d => Doc.AreProtosEqual(d, doc))) { + if (!DocListCast(collection.$data).find(d => Doc.AreProtosEqual(d, doc))) { const newEmbedding = Doc.MakeEmbedding(doc); Doc.AddDocToList(collection[DocData], 'data', newEmbedding); Doc.SetContainer(newEmbedding, collection); @@ -119,8 +119,8 @@ export class TagItem extends ObservableReactComponent { } } - if (!doc[DocData].tags) doc[DocData].tags = new List(); - const tagList = doc[DocData].tags as List; + if (!doc.$tags) doc.$tags = new List(); + const tagList = doc.$tags as List; if (!tagList.includes(tag)) tagList.push(tag); }; @@ -131,22 +131,22 @@ export class TagItem extends ObservableReactComponent { * @param tagDoc doc that collections the Docs with the tag */ public static removeTagFromDoc = (doc: Doc, tag: string, tagDoc?: Doc) => { - if (doc[DocData].tags) { + if (doc.$tags) { if (doc.type === DocumentType.COL) { tagDoc && Doc.RemoveDocFromList(tagDoc[DocData], 'collections', doc); for (const cur_doc of TagItem.allDocsWithTag(tag)) { - doc[DocData].data = new List(DocListCast(doc[DocData].data).filter(d => !Doc.AreProtosEqual(cur_doc, d))); + doc.$data = new List(DocListCast(doc.$data).filter(d => !Doc.AreProtosEqual(cur_doc, d))); } } else { tagDoc && Doc.RemoveDocFromList(tagDoc[DocData], 'docs', doc); for (const collection of DocListCast(tagDoc?.collections)) { - collection[DocData].data = new List(DocListCast(collection[DocData].data).filter(d => !Doc.AreProtosEqual(doc, d))); + collection.$data = new List(DocListCast(collection.$data).filter(d => !Doc.AreProtosEqual(doc, d))); } } } - doc[DocData].tags = new List(StrListCast(doc[DocData].tags).filter(label => label !== tag)); + doc.$tags = new List(StrListCast(doc.$tags).filter(label => label !== tag)); }; private _ref: React.RefObject; @@ -171,11 +171,10 @@ export class TagItem extends ObservableReactComponent { // Create a new collection and set up configurations. const newCollection = ((doc: Doc) => { - const docData = doc[DocData]; - docData.data = new List(newEmbeddings); - docData.title = this._props.tag; - docData.tags = new List([this._props.tag]); - docData.freeform_fitContentsToBox = true; + doc.$data = new List(newEmbeddings); + doc.$title = this._props.tag; + doc.$tags = new List([this._props.tag]); + doc.$freeform_fitContentsToBox = true; doc._freeform_panX = doc._freeform_panY = 0; doc._width = 900; doc._height = 900; diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index a45b37f43..12a131deb 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -5,7 +5,7 @@ import * as ReactDOM from 'react-dom/client'; import ResizeObserver from 'resize-observer-polyfill'; import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, DivHeight, DivWidth, incrementTitleCopy, returnTrue, UpdateIcon } from '../../../ClientUtils'; import { Doc, DocListCast, Field, Opt } from '../../../fields/Doc'; -import { AclAdmin, AclEdit, DocData } from '../../../fields/DocSymbols'; +import { AclAdmin, AclEdit } from '../../../fields/DocSymbols'; import { Id } from '../../../fields/FieldSymbols'; import { InkTool } from '../../../fields/InkField'; import { List } from '../../../fields/List'; @@ -481,7 +481,7 @@ export class CollectionDockingView extends CollectionSubView() { Array.from(cloned.map.entries()).forEach(entry => { json = json.replace(entry[0], entry[1][Id]); }); - cloned.clone[DocData].dockingConfig = json; + cloned.clone.$dockingConfig = json; return DashboardView.openDashboard(cloned.clone); } const matches = json.match(/"documentId":"[a-z0-9-]+"/g); @@ -495,7 +495,7 @@ export class CollectionDockingView extends CollectionSubView() { const newtab = origtabdocs.length ? Doc.MakeCopy(origtab, true, undefined, true) : Doc.MakeEmbedding(origtab); const newtabdocs = origtabdocs.map(origtabdoc => Doc.MakeEmbedding(origtabdoc)); if (newtabdocs.length) { - newtab[DocData].data = new List(newtabdocs); + newtab.$data = new List(newtabdocs); newtabdocs.forEach(ntab => Doc.SetContainer(ntab, newtab)); } json = json.replace(origtab[Id], newtab[Id]); diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx index 996626575..4fe73895e 100644 --- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx +++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx @@ -5,7 +5,6 @@ import * as React from 'react'; import { returnEmptyString, returnFalse, setupMoveUpEvents } from '../../../ClientUtils'; import { emptyFunction, numberRange } from '../../../Utils'; import { Doc } from '../../../fields/Doc'; -import { DocData } from '../../../fields/DocSymbols'; import { PastelSchemaPalette, SchemaHeaderField } from '../../../fields/SchemaHeaderField'; import { ScriptField } from '../../../fields/ScriptField'; import { Docs } from '../../documents/Documents'; @@ -164,7 +163,7 @@ export class CollectionMasonryViewFieldRow extends ObservableReactComponent { - this.target[DocData].data = new List(source); + this.target.$data = new List(source); }, ''), initialize: emptyFunction, }; diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx index c499bd288..4650727eb 100644 --- a/src/client/views/collections/CollectionNoteTakingView.tsx +++ b/src/client/views/collections/CollectionNoteTakingView.tsx @@ -441,7 +441,7 @@ export class CollectionNoteTakingView extends CollectionSubView() { if ((e.ctrlKey || fieldProps.Document._createDocOnCR) && ['Enter'].includes(e.key)) { e.stopPropagation?.(); const newDoc = Doc.MakeCopy(fieldProps.Document, true); - newDoc[DocData].text = undefined; + newDoc.$text = undefined; DocumentView.SetSelectOnLoad(newDoc); return this.addDocument?.(newDoc); } diff --git a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx index 40b3f9ef2..df9b5a1eb 100644 --- a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx +++ b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx @@ -157,7 +157,7 @@ export class CollectionNoteTakingViewColumn extends ObservableReactComponent { const colHdrData = Array.from(Cast(this._props.Document[this._props.fieldKey + '_columnHeaders'], listSpec(SchemaHeaderField), null)); if (this._props.headingObject) { - // this._props.docList.forEach(d => (d[DocData][this._props.pivotField] = undefined)); + // this._props.docList.forEach(d => (d['$'+this._props.pivotField] = undefined)); colHdrData.splice(colHdrData.indexOf(this._props.headingObject), 1); this._props.resizeColumns(colHdrData); } diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx index a7a9f2114..0836cce38 100644 --- a/src/client/views/collections/CollectionStackedTimeline.tsx +++ b/src/client/views/collections/CollectionStackedTimeline.tsx @@ -432,8 +432,8 @@ export class CollectionStackedTimeline extends CollectionSubView { - 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}"]` - ); + this._props.mark.$title = ComputedField.MakeFunction(`["${this._props.endTag}"] ? "#" + formatToTime(this["${this._props.startTag}"]) + "-" + formatToTime(this["${this._props.endTag}"]) : "#" + formatToTime(this["${this._props.startTag}"]`); }; // context menu contextMenuItems = () => { diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 392d85af3..9972fe03d 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -5,7 +5,6 @@ import { observer } from 'mobx-react'; import * as React from 'react'; import { ClientUtils, DivHeight, returnNone, returnZero, setupMoveUpEvents, smoothScroll } from '../../../ClientUtils'; import { Doc, Opt } from '../../../fields/Doc'; -import { DocData } from '../../../fields/DocSymbols'; import { Id } from '../../../fields/FieldSymbols'; import { List } from '../../../fields/List'; import { listSpec } from '../../../fields/Schema'; @@ -316,11 +315,11 @@ export class CollectionStackingView extends CollectionSubView([]) : undefined; + newDoc['$' + Doc.LayoutFieldKey(newDoc)] = dataField === undefined || Cast(dataField, listSpec(Doc), null)?.length !== undefined ? new List([]) : undefined; if (layoutFieldKey !== 'layout' && fieldProps.Document[layoutFieldKey] instanceof Doc) { newDoc[layoutFieldKey] = fieldProps.Document[layoutFieldKey]; } - newDoc[DocData].text = undefined; + newDoc.$text = undefined; DocumentView.SetSelectOnLoad(newDoc); return this.addDocument?.(newDoc); } diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 655894e40..29caead5b 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -9,7 +9,7 @@ import { Id } from '../../../fields/FieldSymbols'; import { List } from '../../../fields/List'; import { listSpec } from '../../../fields/Schema'; import { ScriptField } from '../../../fields/ScriptField'; -import { BoolCast, Cast, DateCast, NumCast, ScriptCast, StrCast, toList } from '../../../fields/Types'; +import { BoolCast, Cast, DateCast, DocCast, NumCast, ScriptCast, StrCast, toList } from '../../../fields/Types'; import { WebField } from '../../../fields/URLField'; import { GetEffectiveAcl, TraceMobx } from '../../../fields/util'; import { GestureUtils } from '../../../pen-gestures/GestureUtils'; @@ -85,6 +85,9 @@ export function CollectionSubView() { constructor(props: X & SubCollectionViewProps) { super(props); makeObservable(this); + console.log(`propsTitle: ${this._props.Document.title} DocTitle: ${this.Document.title} LayoutTitle:${this.layoutDoc.title} DataTitle:${this.dataDoc.title}`); + console.log(`tempTitle: ${this._props.TemplateDataDocument?.title} LayouTResolve: ${DocCast(this.layoutDoc.resolvedDataDoc)?.title} propDocResolve: ${DocCast(this._props.Document.resolvedDataDoc)?.title}`); + console.log('Children:', this.childDocs, this.childLayoutPairs); } @observable _focusFilters: Opt = undefined; // childFilters that are overridden when previewing a link to an anchor which has childFilters set on it @@ -108,7 +111,12 @@ export function CollectionSubView() { } get dataDoc() { - return this._props.TemplateDataDocument instanceof Doc && this.Document.isTemplateForField ? Doc.GetProto(this._props.TemplateDataDocument) : this.Document.resolvedDataDoc ? this.Document : this.Document[DocData]; // if the layout document has a resolvedDataDoc, then we don't want to get its parent which would be the unexpanded template + console.log(this._props.Document.title + ' isTemplate: ' + this.layoutDoc.isTemplateForField); + return this._props.TemplateDataDocument instanceof Doc && this.layoutDoc.isTemplateForField // + ? this._props.TemplateDataDocument[DocData] + : this.layoutDoc.resolvedDataDoc + ? this._props.Document + : this.Document[DocData]; // if the layout document has a resolvedDataDoc, then we don't want to get its parent which would be the unexpanded template } get childContainerViewPath() { diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index cc56a8ff9..2d2c0fce5 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -347,7 +347,7 @@ export class TabDocView extends ObservableReactComponent { undoable(() => { const target = e.currentTarget as unknown as { value: string }; titleEle.size = target?.value.length + 3; - doc[DocData].title = target?.value ?? ''; + doc.$title = target?.value ?? ''; }, 'edit tab title')(); }; diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index 6208905fe..d6a19e88e 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -372,9 +372,8 @@ export class TreeView extends ObservableReactComponent { _width: 1000, _height: 10, }); - const bulletData = bullet[DocData]; - bulletData.title = ComputedField.MakeFunction('this.text?.Text'); - bulletData.data = new List([]); + bullet.$title = ComputedField.MakeFunction('this.text?.Text'); + bullet.$data = new List([]); DocumentView.addViewRenderedCb(bullet, dv => dv.ComponentView?.setFocus?.()); return bullet; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx index 8b9a3e0ec..35c6d30fe 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx @@ -1,16 +1,15 @@ import { makeObservable, observable, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; -import { Doc, DocListCast, FieldType, FieldResult } from '../../../../fields/Doc'; +import { Doc, DocListCast, FieldResult, FieldType } from '../../../../fields/Doc'; import { InkTool } from '../../../../fields/InkField'; import { StrCast } from '../../../../fields/Types'; import { ObservableReactComponent } from '../../ObservableReactComponent'; import { DocButtonState, DocumentLinksButton } from '../../nodes/DocumentLinksButton'; import { TopBar } from '../../topbar/TopBar'; import { CollectionFreeFormInfoState, InfoState, StateEntryFunc, infoState } from './CollectionFreeFormInfoState'; -import './CollectionFreeFormView.scss'; -import { DocData } from '../../../../fields/DocSymbols'; import { CollectionFreeFormView } from './CollectionFreeFormView'; +import './CollectionFreeFormView.scss'; export interface CollectionFreeFormInfoUIProps { Document: Doc; @@ -41,7 +40,7 @@ export class CollectionFreeFormInfoUI extends ObservableReactComponent {this._currState = val;}); } // prettier-ignore componentWillUnmount(): void { - this._props.Document[DocData].backgroundColor = this._originalbackground; + this._props.Document.$backgroundColor = this._originalbackground; } setCurrState = (state: infoState) => { @@ -52,9 +51,9 @@ export class CollectionFreeFormInfoUI extends ObservableReactComponent { - this._originalbackground = StrCast(this._props.Document[DocData].backgroundColor); + this._originalbackground = StrCast(this._props.Document.$backgroundColor); // state entry functions - // const setBackground = (colour: string) => () => {this._props.Document[DocData].backgroundColor = colour;} // prettier-ignore + // const setBackground = (colour: string) => () => {this._props.Document.$backgroundColor = colour;} // prettier-ignore // const setOpacity = (opacity: number) => () => {this._props.LayoutDoc.opacity = opacity;} // prettier-ignore // arc transition trigger conditions const firstDoc = () => (this._props.childDocs().length ? this._props.childDocs()[0] : undefined); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 89aa53c35..6030e146c 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -561,7 +561,7 @@ export class CollectionFreeFormView extends CollectionSubView { - inkDoc[DocData][field] = stroke.dataDoc[field]; + inkDoc['$' + field] = stroke.dataDoc[field]; }); this.addDocument(inkDoc); }); @@ -1260,15 +1260,14 @@ export class CollectionFreeFormView extends CollectionSubView { - const docData = doc[DocData]; - docData.title = opts.text; - docData._width = opts.size; - docData.ai_drawing_input = opts.text; - docData.ai_drawing_complexity = opts.complexity; - docData.ai_drawing_colored = opts.autoColor; - docData.ai_drawing_size = opts.size; - docData.ai_drawing_data = gptRes; - docData.ai = 'gpt'; + doc.$title = opts.text; + doc.$width = opts.size; + doc.$ai_drawing_input = opts.text; + doc.$ai_drawing_complexity = opts.complexity; + doc.$ai_drawing_colored = opts.autoColor; + doc.$ai_drawing_size = opts.size; + doc.$ai_drawing_data = gptRes; + doc.$ai = 'gpt'; this._drawingContainer = doc; if (x !== undefined && y !== undefined) { [doc.x, doc.y] = this.screenToFreeformContentsXf.transformPoint(x, y); @@ -1493,7 +1492,7 @@ export class CollectionFreeFormView extends CollectionSubView { const textDoc = DocCast(fieldProps.Document.rootDocument, fieldProps.Document); const newDoc = Doc.MakeCopy(textDoc, true); - newDoc[DocData][Doc.LayoutFieldKey(newDoc, fieldProps.LayoutTemplateString)] = undefined; // the copy should not copy the text contents of it source, just the render style + newDoc['$' + Doc.LayoutFieldKey(newDoc, fieldProps.LayoutTemplateString)] = undefined; // the copy should not copy the text contents of it source, just the render style newDoc.x = NumCast(textDoc.x) + (below ? 0 : NumCast(textDoc._width) + 10); newDoc.y = NumCast(textDoc.y) + (below ? NumCast(textDoc._height) + 10 : 0); DocumentView.SetSelectOnLoad(newDoc); @@ -1686,7 +1685,7 @@ export class CollectionFreeFormView extends CollectionSubView (this.Document.isTemplateDoc || this.Document.isTemplateForField ? false : !this._renderCutoffData.get(doc[Id] + ''))); + renderCutoffProvider = computedFn((doc: Doc) => (this._props.Document.isTemplateDoc || this._props.Document.isTemplateForField ? false : !this._renderCutoffData.get(doc[Id] + ''))); doEngineLayout( poolData: Map, diff --git a/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx b/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx index b9f8b13a7..b40189d76 100644 --- a/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx +++ b/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx @@ -108,7 +108,7 @@ export class UniqueFaceBox extends ViewBoxBaseComponent() { if (faceAnno) { faceAnno.face && FaceRecognitionHandler.UniqueFaceRemoveFaceImage(faceAnno, DocCast(faceAnno.face)); FaceRecognitionHandler.UniqueFaceAddFaceImage(faceAnno, this.Document); - faceAnno[DocData].face = this.Document[DocData]; + faceAnno.$face = this.Document[DocData]; } } }); @@ -118,7 +118,7 @@ export class UniqueFaceBox extends ViewBoxBaseComponent() { const imgDoc = faceAnno; faceAnno.face && FaceRecognitionHandler.UniqueFaceRemoveFaceImage(imgDoc, DocCast(faceAnno.face)); FaceRecognitionHandler.UniqueFaceAddFaceImage(faceAnno, this.Document); - faceAnno[DocData].face = this.Document[DocData]; + faceAnno.$face = this.Document[DocData]; }); e.stopPropagation(); return true; diff --git a/src/client/views/collections/collectionFreeForm/ImageLabelBox.tsx b/src/client/views/collections/collectionFreeForm/ImageLabelBox.tsx index a3d9641da..c983d7c26 100644 --- a/src/client/views/collections/collectionFreeForm/ImageLabelBox.tsx +++ b/src/client/views/collections/collectionFreeForm/ImageLabelBox.tsx @@ -9,7 +9,6 @@ import React from 'react'; import { imageUrlToBase64 } from '../../../../ClientUtils'; import { Utils, numberRange } from '../../../../Utils'; import { Doc, NumListCast, Opt } from '../../../../fields/Doc'; -import { DocData } from '../../../../fields/DocSymbols'; import { List } from '../../../../fields/List'; import { ImageCast } from '../../../../fields/Types'; import { gptGetEmbedding, gptImageLabel } from '../../../apis/gpt/GPT'; @@ -165,7 +164,7 @@ export class ImageLabelBox extends ViewBoxBaseComponent() { // Converts the images into a Base64 format, afterwhich the information is sent to GPT to label them. const imageInfos = this._selectedImages.map(async doc => { - if (!doc[DocData].tags_chat) { + if (!doc.$tags_chat) { const [name, type] = ImageCast(doc[Doc.LayoutFieldKey(doc)]).url.href.split('.'); return imageUrlToBase64(`${name}_o.${type}`).then(hrefBase64 => !hrefBase64 ? undefined : @@ -176,7 +175,7 @@ export class ImageLabelBox extends ViewBoxBaseComponent() { (await Promise.all(imageInfos)).forEach(imageInfo => { if (imageInfo) { - imageInfo.doc[DocData].tags_chat = (imageInfo.doc[DocData].tags_chat as List) ?? new List(); + imageInfo.doc.$tags_chat = (imageInfo.doc.$tags_chat as List) ?? new List(); const labels = imageInfo.labels.split('\n'); labels.forEach(label => { @@ -186,7 +185,7 @@ export class ImageLabelBox extends ViewBoxBaseComponent() { .replace(/^\d+\.\s*|-|f\*/, '') .replace(/^#/, '') .trim(); - (imageInfo.doc[DocData].tags_chat as List).push(hashLabel); + (imageInfo.doc.$tags_chat as List).push(hashLabel); }); } }); @@ -201,10 +200,10 @@ export class ImageLabelBox extends ViewBoxBaseComponent() { this.startLoading(); for (const doc of this._selectedImages) { - for (let index = 0; index < (doc[DocData].tags_chat as List).length; index++) { - const label = (doc[DocData].tags_chat as List)[index]; + for (let index = 0; index < (doc.$tags_chat as List).length; index++) { + const label = (doc.$tags_chat as List)[index]; const embedding = await gptGetEmbedding(label); - doc[DocData][`tags_embedding_${index + 1}`] = new List(embedding); + doc[`$tags_embedding_${index + 1}`] = new List(embedding); } } @@ -215,13 +214,13 @@ export class ImageLabelBox extends ViewBoxBaseComponent() { // For each image, loop through the labels, and calculate similarity. Associate it with the // most similar one. this._selectedImages.forEach(doc => { - const embedLists = numberRange((doc[DocData].tags_chat as List).length).map(n => Array.from(NumListCast(doc[DocData][`tags_embedding_${n + 1}`]))); + const embedLists = numberRange((doc.$tags_chat as List).length).map(n => Array.from(NumListCast(doc[`$tags_embedding_${n + 1}`]))); const bestEmbedScore = (embedding: Opt) => Math.max(...embedLists.map(l => (embedding && similarity(Array.from(embedding), l)!) || 0)); const {label: mostSimilarLabelCollect} = this._labelGroups.map(label => ({ label, similarityScore: bestEmbedScore(labelToEmbedding.get(label)) })) .reduce((prev, cur) => cur.similarityScore < 0.3 || cur.similarityScore <= prev.similarityScore ? prev: cur, { label: '', similarityScore: 0, }); // prettier-ignore - doc[DocData].data_label = mostSimilarLabelCollect; // The label most similar to the image's contents. + doc.$data_label = mostSimilarLabelCollect; // The label most similar to the image's contents. }); this.endLoading(); @@ -322,7 +321,7 @@ export class ImageLabelBox extends ViewBoxBaseComponent() { await DocumentView.showDocument(doc, { willZoomCentered: true }); }}>

this._props.addDocTab(doc, OpenWhere.addRightKeyvalue)}> - {(doc[DocData].tags_chat as List).map(label => { + {(doc.$tags_chat as List).map(label => { return (
{label} diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index ad52db496..e3f4c6605 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -165,7 +165,7 @@ export class MarqueeView extends ObservableReactComponent { - this._props.Document[DocData].icon = new ImageField(returnedfilename); + this._props.Document.$icon = new ImageField(returnedfilename); }); }) ); @@ -371,10 +371,9 @@ export class MarqueeView extends ObservableReactComponent { - const docData = doc[DocData]; - docData.data = new List(selected); - docData.isGroup = makeGroup; - docData.title = makeGroup ? 'grouping' : 'nested freeform'; + doc.$data = new List(selected); + doc.$isGroup = makeGroup; + doc.$title = makeGroup ? 'grouping' : 'nested freeform'; doc._freeform_panX = doc._freeform_panY = 0; return doc; })(Doc.MakeCopy(Doc.UserDoc().emptyCollection as Doc, true)); @@ -462,9 +461,9 @@ export class MarqueeView extends ObservableReactComponent { - doc[DocData].borderColor = color; - }); + else selected.forEach(doc => (doc.$borderColor = color)); } return ''; }); @@ -114,7 +108,7 @@ ScriptingGlobals.add(function setBorderColor(color?: string, checkResult?: boole ScriptingGlobals.add(function setBackgroundColor(color?: string, checkResult?: boolean) { const selectedViews = DocumentView.Selected(); const selectedDoc = selectedViews.lastElement()?.Document; - const defaultFill = selectedDoc?._layout_isSvg ? () => StrCast(selectedDoc[DocData].fillColor) : !Doc.ActiveTool || Doc.ActiveTool === InkTool.None ? () => StrCast(Doc.UserDoc().textBackgroundColor, 'transparent') : () => ActiveInkFillColor(); + const defaultFill = selectedDoc?._layout_isSvg ? () => StrCast(selectedDoc.$fillColor) : !Doc.ActiveTool || Doc.ActiveTool === InkTool.None ? () => StrCast(Doc.UserDoc().textBackgroundColor, 'transparent') : () => ActiveInkFillColor(); const setDefaultFill = !Doc.ActiveTool || Doc.ActiveTool === InkTool.None ? (c: string) => { Doc.UserDoc().textBackgroundColor = c; }: SetActiveInkFillColor; // prettier-ignore if (Doc.ActiveTool !== InkTool.None && !selectedViews.lastElement()?.Document._layout_isSvg) { if (checkResult) return defaultFill(); @@ -149,10 +143,7 @@ ScriptingGlobals.add(function setBackgroundColor(color?: string, checkResult?: b return selected.lastElement()?._backgroundColor ?? defaultFill(); } if (!selected.length) setDefaultFill(color ?? 'transparent'); - else - selected.forEach(doc => { - doc[DocData][doc._layout_isSvg ? 'fillColor' : 'backgroundColor'] = color; - }); + else selected.forEach(doc => (doc[doc._layout_isSvg ? '$fillColor' : '$backgroundColor'] = color)); } return ''; }); @@ -170,7 +161,7 @@ ScriptingGlobals.add(function setHeaderColor(color?: string, checkResult?: boole } if (DocumentView.Selected().length) { DocumentView.SelectedDocs().forEach(doc => { - doc[DocData].layout_headingColor = color === 'transparent' ? undefined : color; + doc.$layout_headingColor = color === 'transparent' ? undefined : color; doc.layout_showTitle = color === 'transparent' ? undefined : StrCast(doc.layout_showTitle, 'title'); }); } else { @@ -470,23 +461,23 @@ ScriptingGlobals.add(function setInkProperty(option: InkProperty, value: string // prettier-ignore const map: Map number|boolean|string|undefined; setInk: (doc: Doc) => void; setMode: () => void }> = new Map([ [InkProperty.Mask, { - checkResult: () => ((selected?._layout_isSvg ? BoolCast(selected[DocData].stroke_isInkMask) : ActiveIsInkMask())), - setInk: (doc: Doc) => { doc[DocData].stroke_isInkMask = !doc.stroke_isInkMask; }, + checkResult: () => ((selected?._layout_isSvg ? BoolCast(selected.$stroke_isInkMask) : ActiveIsInkMask())), + setInk: (doc: Doc) => { doc.$stroke_isInkMask = !doc.stroke_isInkMask; }, setMode: () => SetActiveIsInkMask(value ? true : false) }], [InkProperty.Labels, { - checkResult: () => ((selected?._layout_isSvg ? BoolCast(selected[DocData].stroke_showLabel) : !ActiveHideTextLabels())), - setInk: (doc: Doc) => { doc[DocData].stroke_showLabel = value; }, + checkResult: () => ((selected?._layout_isSvg ? BoolCast(selected.$stroke_showLabel) : !ActiveHideTextLabels())), + setInk: (doc: Doc) => { doc.$stroke_showLabel = value; }, setMode: () => SetactiveHideTextLabels(value? false : true), }], [ InkProperty.StrokeWidth, { - checkResult: () => (selected?._layout_isSvg ? NumCast(selected[DocData].stroke_width, 1) : ActiveInkWidth()), - setInk: (doc: Doc) => { doc[DocData].stroke_width = NumCast(value); }, + checkResult: () => (selected?._layout_isSvg ? NumCast(selected.$stroke_width, 1) : ActiveInkWidth()), + setInk: (doc: Doc) => { doc.$stroke_width = NumCast(value); }, setMode: () => SetActiveInkWidth(value.toString()), }], [InkProperty.StrokeColor, { - checkResult: () => (selected?._layout_isSvg? StrCast(selected[DocData].color) : ActiveInkColor()), - setInk: (doc: Doc) => { doc[DocData].color = String(value); }, + checkResult: () => (selected?._layout_isSvg? StrCast(selected.$color) : ActiveInkColor()), + setInk: (doc: Doc) => { doc.$color = String(value); }, setMode: () => SetActiveInkColor(StrCast(value)) }], [ InkProperty.EraserWidth, { diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx index 25e76e2a6..e0d59cc9d 100644 --- a/src/client/views/nodes/AudioBox.tsx +++ b/src/client/views/nodes/AudioBox.tsx @@ -296,7 +296,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent() { 'Content-Type': 'application/json', }, }); - this.Document[DocData].phoneticTranscription = response.data['transcription']; + this.Document.$phoneticTranscription = response.data['transcription']; }; // context menu diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx index c0c6db4d3..992fbba66 100644 --- a/src/client/views/nodes/ComparisonBox.tsx +++ b/src/client/views/nodes/ComparisonBox.tsx @@ -80,8 +80,8 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent() const front = Docs.Create.CenteredTextCreator('question', question, {}, img); const back = Docs.Create.CenteredTextCreator('answer', answer, {}); if (useDoc) { - useDoc[DocData][frontKey] = front; - useDoc[DocData][backKey] = back; + useDoc['$' + frontKey] = front; + useDoc['$' + backKey] = back; return useDoc; } return Docs.Create.FlashcardDocument(title, front, back, { _width: 300, _height: 300 }); @@ -475,7 +475,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent() axios .post( 'http://localhost:105/recognize/', // - { file: DocCast(this.Document.audio)[DocData].url }, + { file: DocCast(this.Document.audio).$url }, { headers: { 'Content-Type': 'application/json' } } ) .then(response => { @@ -520,7 +520,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent() action(resp => { switch (resp && callType) { case GPTCallType.CHATCARD: - DocCast(this.dataDoc[this.backKey])[DocData].text = resp; + DocCast(this.dataDoc[this.backKey]).$text = resp; break; case GPTCallType.QUIZDOC: this._renderSide = this.backKey; @@ -618,7 +618,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent() const hrefBase64 = await imageUrlToBase64(u); const response = await gptImageLabel(hrefBase64, 'Answer the following question as a short flashcard response. Do not include a label.' + (this.dataDoc.text as RichTextField)?.Text); - DocCast(this.dataDoc[this.backKey])[DocData].text = response; + DocCast(this.dataDoc[this.backKey]).$text = response; } catch (error) { console.log('Error', error); } diff --git a/src/client/views/nodes/DataVizBox/components/TableBox.tsx b/src/client/views/nodes/DataVizBox/components/TableBox.tsx index 7ef4bca6b..b31a0ec1e 100644 --- a/src/client/views/nodes/DataVizBox/components/TableBox.tsx +++ b/src/client/views/nodes/DataVizBox/components/TableBox.tsx @@ -160,8 +160,8 @@ export class TableBox extends ObservableReactComponent { DragManager.StartAnchorAnnoDrag(moveEv.target instanceof HTMLElement ? [moveEv.target] : [], new DragManager.AnchorAnnoDragData(this._props.docView()!, sourceAnchorCreator, targetCreator), downX, downY, { dragComplete: completeEv => { if (!completeEv.aborted && completeEv.annoDragData && completeEv.annoDragData.linkSourceDoc && completeEv.annoDragData.dropDocument && completeEv.linkDocument) { - completeEv.linkDocument[DocData].link_matchEmbeddings = true; - completeEv.linkDocument[DocData].stroke_startMarker = true; + completeEv.linkDocument.$link_matchEmbeddings = true; + completeEv.linkDocument.$stroke_startMarker = true; this._props.docView?.()?._props.addDocument?.(completeEv.linkDocument); } }, diff --git a/src/client/views/nodes/DiagramBox.tsx b/src/client/views/nodes/DiagramBox.tsx index a49c69be3..3b666bad5 100644 --- a/src/client/views/nodes/DiagramBox.tsx +++ b/src/client/views/nodes/DiagramBox.tsx @@ -3,7 +3,6 @@ import { action, computed, makeObservable, observable, reaction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { Doc, DocListCast } from '../../../fields/Doc'; -import { DocData } from '../../../fields/DocSymbols'; import { RichTextField } from '../../../fields/RichTextField'; import { Cast, DocCast, NumCast, RTFCast, StrCast } from '../../../fields/Types'; import { Gestures } from '../../../pen-gestures/GestureTypes'; @@ -46,7 +45,7 @@ export class DiagramBox extends ViewBoxAnnotatableComponent() { @observable _errorMessage = ''; @computed get mermaidcode() { - return StrCast(this.Document[DocData].text, RTFCast(this.Document[DocData].text)?.Text); + return StrCast(this.Document.$text, RTFCast(this.Document.$text)?.Text); } componentDidMount() { @@ -89,7 +88,7 @@ export class DiagramBox extends ViewBoxAnnotatableComponent() { }; setMermaidCode = undoable((res: string) => { - this.Document[DocData].text = new RichTextField( + this.Document.$text = new RichTextField( JSON.stringify({ doc: { type: 'doc', diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 46bd076d4..e9353b001 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -9,7 +9,7 @@ import { Fade, JackInTheBox } from 'react-awesome-reveal'; import { ClientUtils, DivWidth, isTargetChildOf as isParentOf, lightOrDark, returnFalse, returnVal, simMouseEvent, simulateMouseClick } from '../../../ClientUtils'; import { Utils, emptyFunction } from '../../../Utils'; import { Doc, DocListCast, Field, FieldType, Opt, StrListCast } from '../../../fields/Doc'; -import { AclAdmin, AclEdit, AclPrivate, Animation, AudioPlay, DocData, DocViews } from '../../../fields/DocSymbols'; +import { AclAdmin, AclEdit, AclPrivate, Animation, AudioPlay, DocViews } from '../../../fields/DocSymbols'; import { Id } from '../../../fields/FieldSymbols'; import { InkTool } from '../../../fields/InkField'; import { List } from '../../../fields/List'; @@ -40,6 +40,7 @@ import { FieldsDropdown } from '../FieldsDropdown'; import { ObserverJsxParser } from '../ObservableReactComponent'; import { PinProps } from '../PinFuncs'; import { StyleProp } from '../StyleProp'; +import { TagsView } from '../TagsView'; import { ViewBoxInterface } from '../ViewBoxInterface'; import { GroupActive } from './CollectionFreeFormDocumentView'; import { DocumentContentsView } from './DocumentContentsView'; @@ -52,7 +53,6 @@ import { FormattedTextBox } from './formattedText/FormattedTextBox'; import { PresEffect, PresEffectDirection } from './trails/PresEnums'; import SpringAnimation from './trails/SlideEffect'; import { SpringType, springMappings } from './trails/SpringUtils'; -import { TagsView } from '../TagsView'; export interface DocumentViewProps extends FieldViewSharedProps { hideDecorations?: boolean; // whether to suppress all DocumentDecorations when doc is selected @@ -426,7 +426,7 @@ export class DocumentViewInternal extends DocComponent { this.Document.ignoreClick = false; - this.Document.onClick = this.Document[DocData].onClick = undefined; + this.Document.onClick = this.Document.$onClick = undefined; }, 'default on click'); deleteClicked = undoable(() => this._props.removeDocument?.(this.Document), 'delete doc'); @@ -758,7 +758,7 @@ export class DocumentViewInternal extends DocComponent ')[1]; const matchedTags = Array.from(SearchUtil.SearchCollection(Doc.MyFilesystem, tag, false, ['tags']).keys()); - const collectionDocs = DocListCast(collection[DocData].data).concat(collection); + const collectionDocs = DocListCast(collection.$data).concat(collection); let wid = 100; let created = false; const matchedDocs = matchedTags @@ -1749,6 +1749,6 @@ ScriptingGlobals.add(function updateTagsCollection(collection: Doc) { return aset; }, new Set()); - created && (collection[DocData].data = new List(Array.from(matchedDocs))); + created && (collection.$data = new List(Array.from(matchedDocs))); return true; }); diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 5b06e9fc5..5d9718760 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -284,10 +284,9 @@ export class ImageBox extends ViewBoxAnnotatableComponent() { crop = (region: Doc | undefined, addCrop?: boolean) => { if (!region) return undefined; const cropping = Doc.MakeCopy(region, true); - const regionData = region[DocData]; - regionData.lockedPosition = true; - regionData.title = 'region:' + this.Document.title; - regionData.followLinkToggle = true; + region.$lockedPosition = true; + region.$title = 'region:' + this.Document.title; + region.$followLinkToggle = true; this.addDocument(region); const anchx = NumCast(cropping.x); const anchy = NumCast(cropping.y); @@ -300,24 +299,23 @@ export class ImageBox extends ViewBoxAnnotatableComponent() { cropping._width = anchw * (this._props.NativeDimScaling?.() || 1); cropping._height = anchh * (this._props.NativeDimScaling?.() || 1); cropping.onClick = undefined; - const croppingProto = cropping[DocData]; - croppingProto.annotationOn = undefined; - croppingProto.isDataDoc = true; - croppingProto.backgroundColor = undefined; - croppingProto.proto = Cast(this.Document.proto, Doc, null)?.proto; // set proto of cropping's data doc to be IMAGE_PROTO - croppingProto.type = DocumentType.IMG; - croppingProto.layout = ImageBox.LayoutString('data'); - croppingProto.data = ObjectField.MakeCopy(this.dataDoc[this.fieldKey] as ObjectField); - croppingProto.data_nativeWidth = anchw; - croppingProto.data_nativeHeight = anchh; - croppingProto.freeform_scale = viewScale; - croppingProto.freeform_panX = anchx / viewScale; - croppingProto.freeform_panY = anchy / viewScale; - croppingProto.freeform_scale_min = viewScale; - croppingProto.freeform_panX_min = anchx / viewScale; - croppingProto.freeform_panX_max = anchw / viewScale; - croppingProto.freeform_panY_min = anchy / viewScale; - croppingProto.freeform_panY_max = anchh / viewScale; + cropping.$annotationOn = undefined; + cropping.$isDataDoc = true; + cropping.$backgroundColor = undefined; + cropping.$proto = Cast(this.Document.proto, Doc, null)?.proto; // set proto of cropping's data doc to be IMAGE_PROTO + cropping.$type = DocumentType.IMG; + cropping.$layout = ImageBox.LayoutString('data'); + cropping.$data = ObjectField.MakeCopy(this.dataDoc[this.fieldKey] as ObjectField); + cropping.$data_nativeWidth = anchw; + cropping.$data_nativeHeight = anchh; + cropping.$freeform_scale = viewScale; + cropping.$reeform_panX = anchx / viewScale; + cropping.$freeform_panY = anchy / viewScale; + cropping.$freeform_scale_min = viewScale; + cropping.$freeform_panX_min = anchx / viewScale; + cropping.$freeform_panX_max = anchw / viewScale; + cropping.$freeform_panY_min = anchy / viewScale; + cropping.$freeform_panY_max = anchh / viewScale; if (addCrop) { DocUtils.MakeLink(region, cropping, { link_relationship: 'cropped image' }); cropping.x = NumCast(this.Document.x) + NumCast(this.layoutDoc._width); diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx index 9795febbe..32c9efbd9 100644 --- a/src/client/views/nodes/KeyValueBox.tsx +++ b/src/client/views/nodes/KeyValueBox.tsx @@ -105,7 +105,7 @@ export class KeyValueBox extends ViewBoxBaseComponent() { if (setResult) setResult?.(value); else target[key] = field; }; - const res = script.run({ this: Doc.Layout(doc), _setCacheResult_ }, console.log); + const res = script.run({ this: doc, _setCacheResult_ }, console.log); if (!res.success) { if (key) target[key] = script.originalScript; return false; diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx index d5dc256d9..8bf65b637 100644 --- a/src/client/views/nodes/LinkBox.tsx +++ b/src/client/views/nodes/LinkBox.tsx @@ -252,7 +252,7 @@ export class LinkBox extends ViewBoxBaseComponent() { fontSize={fontSize} GetValue={() => linkDesc} SetValue={action(val => { - this.Document[DocData].link_description = val; + this.Document.$link_description = val; return true; })} /> @@ -262,8 +262,8 @@ export class LinkBox extends ViewBoxBaseComponent() { background={color} color={fontColor || lightOrDark(DashColor(color).fade(0.5).toString())} type={Type.PRIM} - val={StrCast(this.Document[DocData].link_description)} - setVal={action(val => (this.Document[DocData].link_description = val))} + val={StrCast(this.Document.$link_description)} + setVal={action(val => (this.Document.$link_description = val))} fillWidth /> */}
diff --git a/src/client/views/nodes/LinkDescriptionPopup.tsx b/src/client/views/nodes/LinkDescriptionPopup.tsx index ff95f8547..6bec9c53d 100644 --- a/src/client/views/nodes/LinkDescriptionPopup.tsx +++ b/src/client/views/nodes/LinkDescriptionPopup.tsx @@ -1,7 +1,6 @@ import { action, makeObservable, observable, reaction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; -import { DocData } from '../../../fields/DocSymbols'; import { StrCast } from '../../../fields/Types'; import { LinkManager } from '../../util/LinkManager'; import './LinkDescriptionPopup.scss'; @@ -48,7 +47,7 @@ export class LinkDescriptionPopup extends React.Component<{}> { onDismiss = (add: boolean) => { this.display = false; if (add) { - LinkManager.Instance.currentLink && (LinkManager.Instance.currentLink[DocData].link_description = this.description); + LinkManager.Instance.currentLink && (LinkManager.Instance.currentLink.$link_description = this.description); } this.description = ''; }; diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 06b75e243..1f72206fc 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -129,15 +129,14 @@ export class PDFBox extends ViewBoxAnnotatableComponent() { cropping._width = anchw; cropping._height = anchh; cropping.onClick = undefined; - const croppingProto = cropping[DocData]; - croppingProto.annotationOn = undefined; - croppingProto.isDataDoc = true; - croppingProto.proto = Cast(this.Document.proto, Doc, null)?.proto; // set proto of cropping's data doc to be IMAGE_PROTO - croppingProto.type = DocumentType.IMG; - croppingProto.layout = ImageBox.LayoutString('data'); - croppingProto.data = new ImageField(ClientUtils.CorsProxy('http://www.cs.brown.edu/~bcz/noImage.png')); - croppingProto.data_nativeWidth = anchw; - croppingProto.data_nativeHeight = anchh; + cropping.$annotationOn = undefined; + cropping.$isDataDoc = true; + cropping.$proto = Cast(this.Document.proto, Doc, null)?.proto; // set proto of cropping's data doc to be IMAGE_PROTO + cropping.$type = DocumentType.IMG; + cropping.$layout = ImageBox.LayoutString('data'); + cropping.$data = new ImageField(ClientUtils.CorsProxy('http://www.cs.brown.edu/~bcz/noImage.png')); + cropping.$data_nativeWidth = anchw; + cropping.$data_nativeHeight = anchh; if (addCrop) { DocUtils.MakeLink(region, cropping, { link_relationship: 'cropped image' }); } diff --git a/src/client/views/nodes/RecordingBox/RecordingBox.tsx b/src/client/views/nodes/RecordingBox/RecordingBox.tsx index 7ba313e92..53783e8a3 100644 --- a/src/client/views/nodes/RecordingBox/RecordingBox.tsx +++ b/src/client/views/nodes/RecordingBox/RecordingBox.tsx @@ -3,7 +3,6 @@ import { observer } from 'mobx-react'; import * as React from 'react'; import { DateField } from '../../../../fields/DateField'; import { Doc, DocListCast } from '../../../../fields/Doc'; -import { DocData } from '../../../../fields/DocSymbols'; import { Id } from '../../../../fields/FieldSymbols'; import { List } from '../../../../fields/List'; import { BoolCast, DocCast } from '../../../../fields/Types'; @@ -99,7 +98,7 @@ export class RecordingBox extends ViewBoxBaseComponent() { }); screengrabber.overlayX = 70; // was -400 screengrabber.overlayY = 590; // was 0 - screengrabber[DocData][Doc.LayoutFieldKey(screengrabber) + '_trackScreen'] = true; + screengrabber['$' + Doc.LayoutFieldKey(screengrabber) + '_trackScreen'] = true; Doc.AddToMyOverlay(screengrabber); // just adds doc to overlay DocumentView.addViewRenderedCb(screengrabber, docView => { RecordingBox.screengrabber = docView.ComponentView as RecordingBox; diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index 9adee53e8..fa099178c 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -331,7 +331,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent() { Doc.SetNativeHeight(imageSnapshot[DocData], Doc.NativeHeight(this.layoutDoc)); this._props.addDocument?.(imageSnapshot); 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); // do we need to set an end time? should default to +0.1 + // link && (DocCast(link.link_anchor_2).$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 && DocumentView.getFirstDocumentView(imageSnapshot)?.startDragging(downX, downY, dropActionType.move, true)); }; @@ -918,11 +918,10 @@ export class VideoBox extends ViewBoxAnnotatableComponent() { crop = (region: Doc | undefined, addCrop?: boolean) => { if (!region) return undefined; const cropping = Doc.MakeCopy(region, true); - const regionData = region[DocData]; - regionData.backgroundColor = 'transparent'; - regionData.lockedPosition = true; - regionData.title = 'region:' + this.Document.title; - regionData.followLinkToggle = true; + region.$backgroundColor = 'transparent'; + region.$lockedPosition = true; + region.$title = 'region:' + this.Document.title; + region.$followLinkToggle = true; region._timecodeToHide = NumCast(region._timecodeToShow) + 0.0001; this.addDocument(region); const anchx = NumCast(cropping.x); @@ -938,25 +937,24 @@ export class VideoBox extends ViewBoxAnnotatableComponent() { cropping.timecodeToHide = undefined; cropping.timecodeToShow = undefined; cropping.onClick = undefined; - const croppingProto = cropping[DocData]; - croppingProto.annotationOn = undefined; - croppingProto.isDataDoc = true; - croppingProto.proto = Cast(this.Document.proto, Doc, null)?.proto; // set proto of cropping's data doc to be IMAGE_PROTO - croppingProto.type = DocumentType.VID; - croppingProto.layout = VideoBox.LayoutString('data'); - croppingProto.data = ObjectField.MakeCopy(this.dataDoc[this.fieldKey] as ObjectField); - croppingProto.data_nativeWidth = anchw; - croppingProto.data_nativeHeight = anchh; - croppingProto.videoCrop = true; - croppingProto.layout_currentTimecode = this.layoutDoc._layout_currentTimecode; - croppingProto.freeform_scale = viewScale; - croppingProto.freeform_scale_min = viewScale; - croppingProto.freeform_ = anchx / viewScale; - croppingProto.freeform_panY = anchy / viewScale; - croppingProto.freeform_panX_min = anchx / viewScale; - croppingProto.freeform_panX_max = anchw / viewScale; - croppingProto.freeform_panY_min = anchy / viewScale; - croppingProto.freeform_panY_max = anchh / viewScale; + cropping.$annotationOn = undefined; + cropping.$isDataDoc = true; + cropping.$proto = Cast(this.Document.proto, Doc, null)?.proto; // set proto of cropping's data doc to be IMAGE_PROTO + cropping.$type = DocumentType.VID; + cropping.$layout = VideoBox.LayoutString('data'); + cropping.$data = ObjectField.MakeCopy(this.dataDoc[this.fieldKey] as ObjectField); + cropping.$data_nativeWidth = anchw; + cropping.$data_nativeHeight = anchh; + cropping.$videoCrop = true; + cropping.$layout_currentTimecode = this.layoutDoc._layout_currentTimecode; + cropping.$freeform_scale = viewScale; + cropping.$freeform_scale_min = viewScale; + cropping.$freeform_ = anchx / viewScale; + cropping.$freeform_panY = anchy / viewScale; + cropping.$freeform_panX_min = anchx / viewScale; + cropping.$freeform_panX_max = anchw / viewScale; + cropping.$freeform_panY_min = anchy / viewScale; + cropping.$freeform_panY_max = anchh / viewScale; if (addCrop) { DocUtils.MakeLink(region, cropping, { link_relationship: 'cropped image' }); } diff --git a/src/client/views/nodes/calendarBox/CalendarBox.tsx b/src/client/views/nodes/calendarBox/CalendarBox.tsx index 1cbfd5fba..e3408696b 100644 --- a/src/client/views/nodes/calendarBox/CalendarBox.tsx +++ b/src/client/views/nodes/calendarBox/CalendarBox.tsx @@ -113,7 +113,7 @@ export class CalendarBox extends CollectionSubView() { if (!super.onInternalDrop(e, de)) return false; de.complete.docDragData?.droppedDocuments.forEach(doc => { const today = new Date().toISOString(); - if (!doc.date_range) doc[DocData].date_range = `${today}|${today}`; + if (!doc.date_range) doc.$date_range = `${today}|${today}`; }); return true; }; diff --git a/src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.tsx b/src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.tsx index 6e9307d37..b023b1de6 100644 --- a/src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.tsx +++ b/src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.tsx @@ -733,9 +733,9 @@ export class ChatBox extends ViewBoxAnnotatableComponent() { const x2 = parseFloat(values[2]) * Doc.NativeWidth(doc); const y2 = parseFloat(values[3]) * Doc.NativeHeight(doc) + foundChunk.startPage * Doc.NativeHeight(doc); - const annotationKey = Doc.LayoutFieldKey(doc) + '_annotations'; + const annotationKey = '$' + Doc.LayoutFieldKey(doc) + '_annotations'; - const existingDoc = DocListCast(doc[DocData][annotationKey]).find(d => d.citation_id === citation.citation_id); + const existingDoc = DocListCast(doc[annotationKey]).find(d => d.citation_id === citation.citation_id); const highlightDoc = existingDoc ?? this.createImageCitationHighlight(x1, y1, x2, y2, citation, annotationKey, doc); DocumentManager.Instance.showDocument(highlightDoc, { willZoomCentered: true }, () => {}); @@ -779,7 +779,7 @@ export class ChatBox extends ViewBoxAnnotatableComponent() { _height: y2 - y1, backgroundColor: 'rgba(255, 255, 0, 0.5)', }); - highlight_doc[DocData].citation_id = citation.citation_id; + highlight_doc.$citation_id = citation.citation_id; Doc.AddDocToList(pdfDoc[DocData], annotationKey, highlight_doc); highlight_doc.annotationOn = pdfDoc; Doc.SetContainer(highlight_doc, pdfDoc); diff --git a/src/client/views/nodes/formattedText/EquationView.tsx b/src/client/views/nodes/formattedText/EquationView.tsx index e0450b202..827db190a 100644 --- a/src/client/views/nodes/formattedText/EquationView.tsx +++ b/src/client/views/nodes/formattedText/EquationView.tsx @@ -6,7 +6,6 @@ import { EditorView } from 'prosemirror-view'; import * as React from 'react'; import * as ReactDOM from 'react-dom/client'; import { Doc } from '../../../../fields/Doc'; -import { DocData } from '../../../../fields/DocSymbols'; import { StrCast } from '../../../../fields/Types'; import './DashFieldView.scss'; import EquationEditor from './EquationEditor'; @@ -63,9 +62,9 @@ export class EquationViewInternal extends React.Component }}> { - this._textBoxDoc[DocData][this._fieldKey] = str; + this._textBoxDoc['$' + this._fieldKey] = str; }} autoCommands="pi theta sqrt sum prod alpha beta gamma rho" autoOperatorNames="sin cos tan" diff --git a/src/client/views/nodes/imageEditor/ImageEditor.tsx b/src/client/views/nodes/imageEditor/ImageEditor.tsx index 657e689bb..85bd95d15 100644 --- a/src/client/views/nodes/imageEditor/ImageEditor.tsx +++ b/src/client/views/nodes/imageEditor/ImageEditor.tsx @@ -24,8 +24,8 @@ import { PointerHandler } from './imageEditorUtils/PointerHandler'; import { activeColor, bgColor, brushWidthOffset, canvasSize, eraserColor, freeformRenderSize, newCollectionSize, offsetDistanceY, offsetX } from './imageEditorUtils/imageEditorConstants'; import { CutMode, CursorData, ImageDimensions, ImageEditTool, ImageToolType, Point } from './imageEditorUtils/imageEditorInterfaces'; import { DocumentView } from '../DocumentView'; -import { DocData } from '../../../../fields/DocSymbols'; import { SettingsManager } from '../../../util/SettingsManager'; +import { Upload } from '../../../../server/SharedMediaTypes'; interface GenerativeFillProps { imageEditorOpen: boolean; @@ -397,9 +397,8 @@ const ImageEditor = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addDoc const newImgDoc = await createNewImgDoc(finalImg, firstDoc); if (newImgDoc) { // set the image to transparent to remove the background / brushstrokes - const docData = newImgDoc[DocData]; - docData.backgroundColor = 'transparent'; - docData.disableMixBlend = true; + newImgDoc.$backgroundColor = 'transparent'; + newImgDoc.$disableMixBlend = true; if (firstDoc) setIsFirstDoc(false); setEdits([...prevEdits, { url: finalImgURL, saveRes: undefined }]); } @@ -476,7 +475,7 @@ const ImageEditor = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addDoc const createNewImgDoc = async (img: HTMLImageElement, firstDoc: boolean /*, parent?: Doc */): Promise => { if (!imageRootDoc) return undefined; const { src } = img; - const [result] = await Networking.PostToServer('/uploadRemoteImage', { sources: [src] }); + const [result] = (await Networking.PostToServer('/uploadRemoteImage', { sources: [src] })) as Upload.ImageInformation[]; const source = ClientUtils.prepend(result.accessPaths.agnostic.client); if (firstDoc) { diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index ec97e067a..23155ebf3 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -532,11 +532,10 @@ export class PresBox extends ViewBoxBaseComponent() { const setData = bestTargetView?.ComponentView?.setData; if (setData) setData(activeItem.config_data); else { - const bestTargetData = bestTarget[DocData]; - const current = bestTargetData[fkey]; - const hash = bestTargetData[fkey] ? stringHash(Field.toString(bestTargetData[fkey] as FieldType)) : undefined; - if (hash) bestTargetData[fkey + '_' + hash] = current instanceof ObjectField ? current[Copy]() : current; - bestTargetData[fkey] = activeItem.config_data instanceof ObjectField ? activeItem.config_data[Copy]() : activeItem.config_data; + const current = bestTarget['$' + fkey]; + const hash = bestTarget['$' + fkey] ? stringHash(Field.toString(bestTarget['$' + fkey] as FieldType)) : undefined; + if (hash) bestTarget['$' + fkey + '_' + hash] = current instanceof ObjectField ? current[Copy]() : current; + bestTarget['$' + fkey] = activeItem.config_data instanceof ObjectField ? activeItem.config_data[Copy]() : activeItem.config_data; } bestTarget[fkey + '_usePath'] = activeItem.config_usePath; setTimeout(() => { diff --git a/src/client/views/smartdraw/SmartDrawHandler.tsx b/src/client/views/smartdraw/SmartDrawHandler.tsx index 1cceabed3..2283ef965 100644 --- a/src/client/views/smartdraw/SmartDrawHandler.tsx +++ b/src/client/views/smartdraw/SmartDrawHandler.tsx @@ -10,9 +10,11 @@ import { INode, parse } from 'svgson'; import { imageUrlToBase64, setupMoveUpEvents } from '../../../ClientUtils'; import { unimplementedFunction } from '../../../Utils'; import { Doc, DocListCast } from '../../../fields/Doc'; -import { DocData } from '../../../fields/DocSymbols'; import { InkData, InkField, InkTool } from '../../../fields/InkField'; +import { List } from '../../../fields/List'; import { BoolCast, ImageCast, NumCast, StrCast } from '../../../fields/Types'; +import { PointData } from '../../../pen-gestures/GestureTypes'; +import { Upload } from '../../../server/SharedMediaTypes'; import { Networking } from '../../Network'; import { GPTCallType, gptAPICall, gptDrawingColor } from '../../apis/gpt/GPT'; import { DocumentType } from '../../documents/DocumentTypes'; @@ -26,9 +28,6 @@ import { MarqueeView } from '../collections/collectionFreeForm'; import { ActiveInkArrowEnd, ActiveInkArrowStart, ActiveInkBezierApprox, ActiveInkColor, ActiveInkDash, ActiveInkFillColor, ActiveInkWidth, ActiveIsInkMask, DocumentView } from '../nodes/DocumentView'; import { FireflyDimensionsMap, FireflyImageData, FireflyImageDimensions } from './FireflyConstants'; import './SmartDrawHandler.scss'; -import { Upload } from '../../../server/SharedMediaTypes'; -import { PointData } from '../../../pen-gestures/GestureTypes'; -import { List } from '../../../fields/List'; export interface DrawingOptions { text?: string; @@ -153,9 +152,9 @@ export class SmartDrawHandler extends ObservableReactComponent { this._display = false; this.ShowRegenerate = true; this._showEditBox = false; - const docData = this._selectedDocs[0][DocData]; - this._lastResponse = StrCast(docData.drawingData); - this._lastInput = { text: StrCast(docData.ai_drawing_input), complexity: NumCast(docData.ai_drawing_complexity), size: NumCast(docData.ai_drawing_size), autoColor: BoolCast(docData.ai_drawing_colored), x: this._pageX, y: this._pageY }; + const docData = this._selectedDocs[0]; + this._lastResponse = StrCast(docData.$drawingData); + this._lastInput = { text: StrCast(docData.$ai_drawing_input), complexity: NumCast(docData.$ai_drawing_complexity), size: NumCast(docData.$ai_drawing_size), autoColor: BoolCast(docData.$ai_drawing_colored), x: this._pageX, y: this._pageY }; }; /** @@ -404,7 +403,7 @@ export class SmartDrawHandler extends ObservableReactComponent { const hrefComplete = `${hrefParts[0]}_o.${hrefParts[1]}`; try { const hrefBase64 = await imageUrlToBase64(hrefComplete); - const strokes = DocListCast(drawing[DocData].data); + const strokes = DocListCast(drawing.$data); const coords: string[] = []; strokes.forEach((stroke, i) => { const inkingStroke = DocumentView.getDocumentView(stroke)?.ComponentView as InkingStroke; @@ -423,14 +422,14 @@ export class SmartDrawHandler extends ObservableReactComponent { */ colorStrokes = undoable((res: string, drawing: Doc) => { const colorList = res.match(/\{.*?\}/g); - const strokes = DocListCast(drawing[DocData].data); + const strokes = DocListCast(drawing.$data); colorList?.forEach((colors, index) => { const strokeAndFill = colors.match(/#[0-9A-Fa-f]{6}/g); if (strokeAndFill && strokeAndFill.length == 2) { - strokes[index][DocData].color = strokeAndFill[0]; + strokes[index].$color = strokeAndFill[0]; const inkStroke = DocumentView.getDocumentView(strokes[index])?.ComponentView as InkingStroke; const { inkData } = inkStroke.inkScaledData(); - InkingStroke.IsClosed(inkData) ? (strokes[index][DocData].fillColor = strokeAndFill[1]) : (strokes[index][DocData].fillColor = undefined); + InkingStroke.IsClosed(inkData) ? (strokes[index].$fillColor = strokeAndFill[1]) : (strokes[index].$fillColor = undefined); } }); }, 'color strokes'); diff --git a/src/client/views/smartdraw/StickerPalette.tsx b/src/client/views/smartdraw/StickerPalette.tsx index e3305851a..080a05d42 100644 --- a/src/client/views/smartdraw/StickerPalette.tsx +++ b/src/client/views/smartdraw/StickerPalette.tsx @@ -9,7 +9,6 @@ import ReactLoading from 'react-loading'; import { returnEmptyFilter, returnFalse, returnTrue } from '../../../ClientUtils'; import { emptyFunction, numberRange } from '../../../Utils'; import { Doc, DocListCast, returnEmptyDoclist } from '../../../fields/Doc'; -import { DocData } from '../../../fields/DocSymbols'; import { ImageCast, NumCast } from '../../../fields/Types'; import { ImageField } from '../../../fields/URLField'; import { DocumentType } from '../../documents/DocumentTypes'; @@ -133,7 +132,7 @@ export class StickerPalette extends ObservableReactComponent { this._isLoading = true; - const prevDrawings = DocListCast(this._props.Document[DocData].data); - this._props.Document[DocData].data = undefined; + const prevDrawings = DocListCast(this._props.Document.$data); + this._props.Document.$data = undefined; SmartDrawHandler.Instance.AddDrawing = this.addDrawing; this._canInteract = false; Promise.all( @@ -164,7 +163,7 @@ export class StickerPalette extends ObservableReactComponent { this._gptRes.push(gptRes); - drawing[DocData].freeform_fitContentsToBox = true; + drawing.$freeform_fitContentsToBox = true; Doc.AddDocToList(this._props.Document, 'data', drawing); }; @@ -176,17 +175,16 @@ export class StickerPalette extends ObservableReactComponent { const cIndex = NumCast(this._props.Document.carousel_index); const focusedDrawing = DocListCast(this._props.Document.data)[cIndex]; - const docData = focusedDrawing[DocData]; - docData.title = this._opts.text?.match(/^(.*?)~~~.*$/)?.[1] || this._opts.text; - docData.ai_drawing_input = this._opts.text; - docData.ai_drawing_complexity = this._opts.complexity; - docData.ai_drawing_colored = this._opts.autoColor; - docData.ai_drawing_size = this._opts.size; - docData.ai_drawing_data = this._gptRes[cIndex]; - docData.ai = 'gpt'; + focusedDrawing.$title = this._opts.text?.match(/^(.*?)~~~.*$/)?.[1] || this._opts.text; + focusedDrawing.$ai_drawing_input = this._opts.text; + focusedDrawing.$ai_drawing_complexity = this._opts.complexity; + focusedDrawing.$ai_drawing_colored = this._opts.autoColor; + focusedDrawing.$ai_drawing_size = this._opts.size; + focusedDrawing.$ai_drawing_data = this._gptRes[cIndex]; + focusedDrawing.$ai = 'gpt'; focusedDrawing.width = this._opts.size; - docData.x = this._opts.x; - docData.y = this._opts.y; + focusedDrawing.x = this._opts.x; + focusedDrawing.y = this._opts.y; StickerPalette.addToPalette(focusedDrawing).then(() => this.resetPalette(true)); }; diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index e2f708700..4bf04da5c 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -273,12 +273,12 @@ export class Doc extends RefField { public static AddToMyOverlay(doc: Doc) { return Doc.ActiveDashboard ? Doc.AddDocToList(Doc.ActiveDashboard, 'myOverlayDocs', doc) : Doc.AddDocToList(DocCast(Doc.UserDoc().myOverlayDocs), undefined, doc); } // prettier-ignore public static RemFromMyOverlay(doc: Doc) { return Doc.ActiveDashboard ? Doc.RemoveDocFromList(Doc.ActiveDashboard,'myOverlayDocs', doc) : Doc.RemoveDocFromList(DocCast(Doc.UserDoc().myOverlayDocs), undefined, doc); } // prettier-ignore public static AddToMyPublished(doc: Doc) { - doc[DocData].title_custom = true; - doc[DocData].layout_showTitle = 'title'; + doc.$title_custom = true; + doc.$layout_showTitle = 'title'; Doc.ActiveDashboard ? Doc.AddDocToList(Doc.ActiveDashboard, 'myPublishedDocs', doc) : Doc.AddDocToList(DocCast(Doc.UserDoc().myPublishedDocs), undefined, doc); } // prettier-ignore public static RemFromMyPublished(doc: Doc){ - doc[DocData].title_custom = false; - doc[DocData].layout_showTitle = undefined; + doc.$title_custom = false; + doc.$layout_showTitle = undefined; Doc.ActiveDashboard ? Doc.RemoveDocFromList(Doc.ActiveDashboard,'myPublishedDocs', doc) : Doc.RemoveDocFromList(DocCast(Doc.UserDoc().myPublishedDocs), undefined, doc); } // prettier-ignore public static IsComicStyle(doc?: Doc) { return doc && Doc.ActiveDashboard && !Doc.IsSystem(doc) && Doc.UserDoc().renderStyle === 'comic' ; } // prettier-ignore @@ -629,7 +629,7 @@ export namespace Doc { */ export function RemoveDocFromList(listDoc: Doc, fieldKey: string | undefined, doc: Doc, ignoreProto = false) { const key = fieldKey || Doc.LayoutFieldKey(listDoc); - const list = Doc.Get(listDoc, key, ignoreProto) === undefined ? (listDoc[DocData][key] = new List()) : Cast(listDoc[key], listSpec(Doc)); + const list = Doc.Get(listDoc, key, ignoreProto) === undefined ? (listDoc['$' + key] = new List()) : Cast(listDoc[key], listSpec(Doc)); if (list) { const ind = list.indexOf(doc); if (ind !== -1) { @@ -646,7 +646,7 @@ export namespace Doc { */ export function AddDocToList(listDoc: Doc, fieldKey: string | undefined, doc: Doc, relativeTo?: Doc, before?: boolean, first?: boolean, allowDuplicates?: boolean, reversed?: boolean, ignoreProto?: boolean) { const key = fieldKey || Doc.LayoutFieldKey(listDoc); - const list = Doc.Get(listDoc, key, ignoreProto) === undefined ? (listDoc[DocData][key] = new List()) : Cast(listDoc[key], listSpec(Doc)); + const list = Doc.Get(listDoc, key, ignoreProto) === undefined ? (listDoc['$' + key] = new List()) : Cast(listDoc[key], listSpec(Doc)); if (list) { if (!allowDuplicates) { const pind = list.findIndex(d => d instanceof Doc && d[Id] === doc[Id]); @@ -691,7 +691,7 @@ export namespace Doc { Doc.SetLayout(embedding, Doc.MakeEmbedding(layout)); } embedding.createdFrom = doc; - embedding.proto_embeddingId = doc[DocData].proto_embeddingId = Doc.GetEmbeddings(doc).length - 1; + embedding.proto_embeddingId = doc.$proto_embeddingId = Doc.GetEmbeddings(doc).length - 1; !Doc.GetT(embedding, 'title', 'string', true) && (embedding.title = ComputedField.MakeFunction(`renameEmbedding(this)`)); embedding.author = ClientUtils.CurrentUserEmail(); @@ -699,9 +699,8 @@ export namespace Doc { } export function BestEmbedding(doc: Doc) { - const dataDoc = doc[DocData]; - const availableEmbeddings = Doc.GetEmbeddings(dataDoc); - const bestEmbedding = [...(dataDoc !== doc ? [doc] : []), ...availableEmbeddings].find(d => !d.embedContainer && d.author === ClientUtils.CurrentUserEmail()); + const availableEmbeddings = Doc.GetEmbeddings(doc); + const bestEmbedding = [...(doc[DocData] !== doc ? [doc] : []), ...availableEmbeddings].find(d => !d.embedContainer && d.author === ClientUtils.CurrentUserEmail()); bestEmbedding && Doc.AddEmbedding(doc, doc); return bestEmbedding ?? Doc.MakeEmbedding(doc); } @@ -1475,7 +1474,7 @@ export namespace Doc { * @returns */ export function getDescription(doc: Doc) { - const curDescription = StrCast(doc[DocData][Doc.LayoutFieldKey(doc) + '_description']); + const curDescription = StrCast(doc['$' + Doc.LayoutFieldKey(doc) + '_description']); const docText = (async (tdoc:Doc) => { switch (tdoc.type) { case DocumentType.PDF: return curDescription || StrCast(tdoc.text).split(/\s+/).slice(0, 50).join(' '); // first 50 words of pdf text @@ -1484,7 +1483,7 @@ export namespace Doc { case DocumentType.RTF: return RTFCast(tdoc[Doc.LayoutFieldKey(tdoc)]).Text; default: return StrCast(tdoc.title).startsWith("Untitled") ? "" : StrCast(tdoc.title); }}); // prettier-ignore - return docText(doc).then(text => (doc[DocData][Doc.LayoutFieldKey(doc) + '_description'] = text)); + return docText(doc).then(text => (doc['$' + Doc.LayoutFieldKey(doc) + '_description'] = text)); } // prettier-ignore @@ -1737,7 +1736,7 @@ ScriptingGlobals.add(function idToDoc(id: string): Doc { }); // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function renameEmbedding(doc: Doc) { - return StrCast(doc[DocData].title).replace(/\([0-9]*\)/, '') + `(${doc.proto_embeddingId})`; + return StrCast(doc.$title).replace(/\([0-9]*\)/, '') + `(${doc.proto_embeddingId})`; }); // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function getProto(doc: Doc) { diff --git a/src/fields/ScriptField.ts b/src/fields/ScriptField.ts index b294ee8c6..6e2d7eb78 100644 --- a/src/fields/ScriptField.ts +++ b/src/fields/ScriptField.ts @@ -210,6 +210,7 @@ export class ComputedField extends ScriptField { this._lastComputedResult = this._cachedResult ?? computedFn(() => + ((val) => val instanceof Array ? new List(val) : val)( this.script.compiled && this.script.run( { @@ -220,7 +221,7 @@ export class ComputedField extends ScriptField { _readOnly_: true, }, console.log - ).result as FieldResult + ).result as FieldResult) )(); // prettier-ignore return this._lastComputedResult; }; -- cgit v1.2.3-70-g09d2 From 63772da2b6f07365023d10c5df93c1e8c4f0b6b6 Mon Sep 17 00:00:00 2001 From: bobzel Date: Sun, 30 Mar 2025 11:16:37 -0400 Subject: changed Doc.Layout calls to doc[DocLayout]. fixed flashcard ui placement on card view. fixed css scaling for styleprovider icons and annotation resizer --- src/client/util/DropConverter.ts | 4 +- src/client/views/DocComponent.tsx | 6 +- src/client/views/DocumentDecorations.scss | 112 ++++++++++-------- src/client/views/DocumentDecorations.tsx | 18 +-- src/client/views/PropertiesView.tsx | 12 +- src/client/views/StyleProvider.scss | 16 ++- src/client/views/StyleProvider.tsx | 6 +- .../views/collections/CollectionCarouselView.scss | 4 +- .../views/collections/CollectionCarouselView.tsx | 4 +- src/client/views/collections/TabDocView.scss | 12 +- src/client/views/collections/TabDocView.tsx | 27 ++++- src/client/views/collections/TreeView.tsx | 6 +- .../CollectionFreeFormClusters.ts | 17 ++- .../CollectionFreeFormLayoutEngines.tsx | 7 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 22 ++-- .../collections/collectionFreeForm/MarqueeView.tsx | 3 +- .../collectionSchema/CollectionSchemaView.tsx | 2 +- .../collectionSchema/SchemaTableCell.tsx | 3 +- .../views/nodes/CollectionFreeFormDocumentView.tsx | 3 +- src/client/views/nodes/ComparisonBox.tsx | 131 +-------------------- src/client/views/nodes/DocumentContentsView.tsx | 101 ++++++++++++---- src/client/views/nodes/DocumentView.tsx | 44 +------ src/client/views/nodes/FieldView.tsx | 9 +- src/client/views/nodes/ImageBox.tsx | 40 ++++--- src/client/views/nodes/KeyValueBox.tsx | 3 +- src/client/views/nodes/KeyValuePair.tsx | 3 +- src/client/views/nodes/PDFBox.scss | 6 +- src/client/views/nodes/PDFBox.tsx | 4 +- .../nodes/formattedText/FormattedTextBox.scss | 5 +- .../views/nodes/formattedText/FormattedTextBox.tsx | 3 +- .../views/nodes/importBox/ImportElementBox.tsx | 2 - src/client/views/pdf/PDFViewer.tsx | 3 +- src/fields/Doc.ts | 48 +++----- src/fields/util.ts | 25 ++-- 34 files changed, 317 insertions(+), 394 deletions(-) (limited to 'src/client/views/PropertiesView.tsx') diff --git a/src/client/util/DropConverter.ts b/src/client/util/DropConverter.ts index b5d29be4c..7d3f63448 100644 --- a/src/client/util/DropConverter.ts +++ b/src/client/util/DropConverter.ts @@ -1,5 +1,5 @@ import { Doc, DocListCast, StrListCast } from '../../fields/Doc'; -import { DocData } from '../../fields/DocSymbols'; +import { DocData, DocLayout } from '../../fields/DocSymbols'; import { ObjectField } from '../../fields/ObjectField'; import { RichTextField } from '../../fields/RichTextField'; import { ComputedField, ScriptField } from '../../fields/ScriptField'; @@ -93,7 +93,7 @@ export function convertDropDataToButtons(data: DragManager.DocumentDragData) { data?.draggedDocuments.forEach((doc, i) => { let dbox = doc; // bcz: isButtonBar is intended to allow a collection of linear buttons to be dropped and nested into another collection of buttons... it's not being used yet, and isn't very elegant - if (doc.type === DocumentType.FONTICON || StrCast(Doc.Layout(doc).layout).includes(FontIconBox.name)) { + if (doc.type === DocumentType.FONTICON || StrCast(doc[DocLayout].layout).includes(FontIconBox.name)) { if (data.dropPropertiesToRemove || dbox.dropPropertiesToRemove) { // dbox = Doc.MakeEmbedding(doc); // don't need to do anything if dropping an icon doc onto an icon bar since there should be no layout data for an icon dbox = Doc.MakeEmbedding(dbox); diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index e3d47317c..d33f3b713 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -3,7 +3,7 @@ import * as React from 'react'; import { returnFalse } from '../../ClientUtils'; import { DateField } from '../../fields/DateField'; import { Doc, DocListCast, Opt } from '../../fields/Doc'; -import { AclAdmin, AclAugment, AclEdit, AclPrivate, AclReadonly, DocData } from '../../fields/DocSymbols'; +import { AclAdmin, AclAugment, AclEdit, AclPrivate, AclReadonly, DocData, DocLayout } from '../../fields/DocSymbols'; import { List } from '../../fields/List'; import { DocCast, toList } from '../../fields/Types'; import { GetEffectiveAcl, inheritParentAcls } from '../../fields/util'; @@ -116,7 +116,7 @@ export function ViewBoxBaseComponent

() { * This may or may not inherit from the data doc. */ @computed get layoutDoc() { - return Doc.Layout(this.Document); + return this.Document[DocLayout]; } /** @@ -187,7 +187,7 @@ export function ViewBoxAnnotatableComponent

() { * This is the document being rendered. It may be a template so it may or may no inherit from the data doc. */ @computed get layoutDoc() { - return Doc.Layout(this.Document); + return this.Document[DocLayout]; } /** diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss index a5afb1305..732c2645e 100644 --- a/src/client/views/DocumentDecorations.scss +++ b/src/client/views/DocumentDecorations.scss @@ -91,30 +91,71 @@ $resizeHandler: 8px; } } - .documentDecorations-closeButton { + .documentDecorations-closeWrapper { display: flex; - align-items: center; - justify-content: center; - background: #fb9d75; - border: solid 1.5px #a94442; - color: #fb9d75; - transition: 0.1s ease; - opacity: 1; - pointer-events: all; - width: 20px; - height: 20px; - min-width: 20px; - border-radius: 100%; - opacity: 0.5; - cursor: pointer; - - &:hover { - color: #a94442; + .documentDecorations-closeButton { + display: flex; + align-items: center; + justify-content: center; + background: #fb9d75; + border: solid 1.5px #a94442; + color: #fb9d75; + transition: 0.1s ease; opacity: 1; + pointer-events: all; + width: 20px; + height: 20px; + min-width: 20px; + border-radius: 100%; + opacity: 0.5; + cursor: pointer; + + &:hover { + color: #a94442; + opacity: 1; + width: 20px; + min-width: 20px; + } + + > svg { + margin: 0; + } } - > svg { - margin: 0; + .documentDecorations-minimizeButton { + display: none; + align-items: center; + justify-content: center; + background: #ffdd00; + border: solid 1.5px #a94442; + color: #ffdd00; + transition: 0.1s ease; + font-size: 11px; + opacity: 1; + grid-column: 2; + pointer-events: all; + width: 20px; + height: 20px; + min-width: 20px; + border-radius: 100%; + opacity: 0.5; + cursor: pointer; + + &:hover { + color: #a94442; + opacity: 1; + } + + > svg { + margin: 0; + } + } + &:hover { + width: 40px; + min-width: 40px; + .documentDecorations-minimizeButton { + display: flex; + } } } @@ -145,38 +186,9 @@ $resizeHandler: 8px; } } - .documentDecorations-minimizeButton { - display: flex; - align-items: center; - justify-content: center; - background: #ffdd00; - border: solid 1.5px #a94442; - color: #ffdd00; - transition: 0.1s ease; - font-size: 11px; - opacity: 1; - grid-column: 2; - pointer-events: all; - width: 20px; - height: 20px; - min-width: 20px; - border-radius: 100%; - opacity: 0.5; - cursor: pointer; - - &:hover { - color: #a94442; - opacity: 1; - } - - > svg { - margin: 0; - } - } - .documentDecorations-title { opacity: 1; - width: calc(100% - 60px); // = margin-left + margin-right + width: 100%; grid-column: 3; pointer-events: auto; overflow: hidden; diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 92b4d6fbf..7842c233a 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -320,7 +320,7 @@ export class DocumentDecorations extends ObservableReactComponent { - const docMax = Math.min(NumCast(doc.width) / 2, NumCast(doc.height) / 2); + const docMax = Math.min(NumCast(doc._width) / 2, NumCast(doc._height) / 2); const radius = Math.min(1, dist / maxDist) * docMax; // set radius based on ratio of drag distance to half diagonal distance of bounding box doc._layout_borderRounding = `${radius}px`; }); @@ -388,7 +388,7 @@ export class DocumentDecorations extends ObservableReactComponent { const accumRot = (NumCast(dv.Document._rotation) / 180) * Math.PI; const localRotCtr = dv.screenToViewTransform().transformPoint(rcScreen.X, rcScreen.Y); - const localRotCtrOffset = [localRotCtr[0] - NumCast(dv.Document.width) / 2, localRotCtr[1] - NumCast(dv.Document.height) / 2]; + const localRotCtrOffset = [localRotCtr[0] - NumCast(dv.Document._width) / 2, localRotCtr[1] - NumCast(dv.Document._height) / 2]; const startRotCtr = Utils.rotPt(localRotCtrOffset[0], localRotCtrOffset[1], -accumRot); const unrotatedDocPos = { x: NumCast(dv.Document.x) + localRotCtrOffset[0] - startRotCtr.x, y: NumCast(dv.Document.y) + localRotCtrOffset[1] - startRotCtr.y }; infos.set(dv.Document, { unrotatedDocPos, startRotCtr, accumRot }); @@ -623,8 +623,8 @@ export class DocumentDecorations extends ObservableReactComponent { doc.$data = new InkField(inkPts.map( (ipt) => ({// (new x — oldx) + newWidth * (oldxpoint /oldWidth) - X: NumCast(doc.x) - x + (NumCast(doc.width) * ipt.X) / width, - Y: NumCast(doc.y) - y + (NumCast(doc.height) * ipt.Y) / height, + X: NumCast(doc.x) - x + (NumCast(doc._width) * ipt.X) / width, + Y: NumCast(doc.y) - y + (NumCast(doc._height) * ipt.Y) / height, }))); // prettier-ignore Doc.SetNativeWidth(doc, undefined); Doc.SetNativeHeight(doc, undefined); @@ -719,7 +719,7 @@ export class DocumentDecorations extends ObservableReactComponent )} {sharingMenu} - {!useLock ? null : ( + {!useLock || hideTitle ? null : ( toggle ability to interact with document} placement="top">

@@ -818,8 +818,10 @@ export class DocumentDecorations extends ObservableReactComponent - {hideDeleteButton ? null : topBtn('close', 'times', undefined, () => this.onCloseClick(true), 'Close')} - {hideResizers || hideDeleteButton ? null : topBtn('minimize', 'window-maximize', undefined, () => this.onCloseClick(undefined), 'Minimize')} +
+ {hideDeleteButton ? null : topBtn('close', 'times', undefined, () => this.onCloseClick(true), 'Close')} + {hideResizers || hideDeleteButton ? null : topBtn('minimize', 'window-maximize', undefined, () => this.onCloseClick(undefined), 'Minimize')} +
{titleArea} {hideOpenButton ?
: topBtn('open', 'external-link-alt', this.onMaximizeDown, undefined, 'Open in Lightbox (ctrl: as alias, shift: in new collection, opption: in editor view)')}
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index 7e9cd002b..7fcb15afe 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -13,7 +13,7 @@ import ResizeObserver from 'resize-observer-polyfill'; import { ClientUtils, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, setupMoveUpEvents } from '../../ClientUtils'; import { emptyFunction } from '../../Utils'; import { Doc, DocListCast, Field, FieldResult, FieldType, HierarchyMapping, NumListCast, Opt, ReverseHierarchyMap, StrListCast, returnEmptyDoclist } from '../../fields/Doc'; -import { AclAdmin, DocAcl, DocData } from '../../fields/DocSymbols'; +import { AclAdmin, DocAcl, DocData, DocLayout } from '../../fields/DocSymbols'; import { Id } from '../../fields/FieldSymbols'; import { InkField } from '../../fields/InkField'; import { List } from '../../fields/List'; @@ -205,7 +205,7 @@ export class PropertiesView extends ObservableReactComponent(reqdKeys); const docs: Doc[] = DocumentView.Selected().length < 2 // - ? [this.layoutFields ? Doc.Layout(this.selectedDoc) : this.dataDoc] + ? [this.layoutFields ? this.selectedDoc[DocLayout] : this.dataDoc] : DocumentView.Selected().map(dv => (this.layoutFields ? dv.layoutDoc : dv.dataDoc)); docs.forEach(doc => Object.keys(doc) @@ -264,7 +264,7 @@ export class PropertiesView extends ObservableReactComponent (this.layoutFields ? dv.layoutDoc : dv.dataDoc)); // prettier-ignore docs.forEach(doc => { @@ -330,7 +330,7 @@ export class PropertiesView extends ObservableReactComponent Doc.toggleLockedPosition(doc), 'toggleBackground'); @@ -87,7 +89,7 @@ export function DefaultStyleProvider(doc: Opt, props: Opt {this.content} {this.navButtons} diff --git a/src/client/views/collections/TabDocView.scss b/src/client/views/collections/TabDocView.scss index 397e35ca9..931cdac2b 100644 --- a/src/client/views/collections/TabDocView.scss +++ b/src/client/views/collections/TabDocView.scss @@ -4,7 +4,6 @@ height: 100%; width: 100%; } - input.lm_title:focus, input.lm_title { max-width: unset !important; @@ -22,7 +21,6 @@ input.lm_title { .lm_iconWrap { display: flex; color: black; - width: 15px; height: 15px; align-items: center; align-self: center; @@ -30,6 +28,16 @@ input.lm_title { margin: 3px; border-radius: 20%; + width: auto; + svg:nth-of-type(2) { + display: none; + } + &:hover { + svg:nth-of-type(2) { + display: block; + } + } + .moreInfoDot { background-color: white; border-radius: 100%; diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index 620be2726..568a08792 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -10,7 +10,7 @@ import ResizeObserver from 'resize-observer-polyfill'; import { ClientUtils, DashColor, lightOrDark, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents, simulateMouseClick } from '../../../ClientUtils'; import { emptyFunction } from '../../../Utils'; import { Doc, Opt, returnEmptyDoclist } from '../../../fields/Doc'; -import { DocData } from '../../../fields/DocSymbols'; +import { DocData, DocLayout } from '../../../fields/DocSymbols'; import { Id } from '../../../fields/FieldSymbols'; import { List } from '../../../fields/List'; import { FieldId } from '../../../fields/RefField'; @@ -41,6 +41,7 @@ import { CollectionDockingView } from './CollectionDockingView'; import { CollectionView } from './CollectionView'; import './TabDocView.scss'; import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView'; +import { Tooltip } from '@mui/material'; interface TabMinimapViewProps { doc: Doc; @@ -311,7 +312,7 @@ export class TabDocView extends ObservableReactComponent { @observable _view: DocumentView | undefined = undefined; @observable _forceInvalidateScreenToLocal = 0; // screentolocal is computed outside of react using a dom resize ovbserver. this hack allows the resize observer to trigger a react update - @computed get layoutDoc() { return this._document && Doc.Layout(this._document); } // prettier-ignore + @computed get layoutDoc() { return this._document?.[DocLayout]; } // prettier-ignore @computed get isUserActivated() { return TabDocView.IsSelected(this._document) || this._isAnyChildContentActive; } // prettier-ignore @computed get isContentActive() { return this.isUserActivated || this._hovering; } // prettier-ignore @@ -353,7 +354,6 @@ export class TabDocView extends ObservableReactComponent { if (tab.element[0].children[1].children.length === 1) { iconWrap.className = 'lm_iconWrap lm_moreInfo'; - iconWrap.title = 'click for menu, drag to embed in document'; const dragBtnDown = (e: React.PointerEvent) => { setupMoveUpEvents( this, @@ -377,7 +377,26 @@ export class TabDocView extends ObservableReactComponent { ); }; - const docIcon = ; + const docIcon = ( + <> + + + + + { + if (doc.layout_fieldKey === 'layout_icon') { + const odoc = Doc.GetEmbeddings(doc).find(embedding => !embedding.embedContainer) ?? Doc.MakeEmbedding(doc); + Doc.deiconifyView(odoc); + } + this.addDocTab(doc, OpenWhere.lightboxAlways); + }} + /> + + + ); const closeIcon = ; ReactDOM.createRoot(iconWrap).render(docIcon); ReactDOM.createRoot(closeWrap).render(closeIcon); diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index 9889766d5..4dc937864 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -7,7 +7,7 @@ import * as React from 'react'; import { ClientUtils, lightOrDark, return18, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, returnZero, setupMoveUpEvents, simulateMouseClick } from '../../../ClientUtils'; import { emptyFunction } from '../../../Utils'; import { Doc, DocListCast, Field, FieldType, Opt, StrListCast, returnEmptyDoclist } from '../../../fields/Doc'; -import { DocData } from '../../../fields/DocSymbols'; +import { DocData, DocLayout } from '../../../fields/DocSymbols'; import { Id } from '../../../fields/FieldSymbols'; import { List } from '../../../fields/List'; import { RichTextField } from '../../../fields/RichTextField'; @@ -156,7 +156,7 @@ export class TreeView extends ObservableReactComponent { return this.Document[DocData]; } @computed get layoutDoc() { - return Doc.Layout(this.Document); + return this.Document[DocLayout]; } @computed get fieldKey() { return StrCast(this.Document._treeView_FieldKey, Doc.LayoutFieldKey(this.Document)); @@ -1309,7 +1309,7 @@ export class TreeView extends ObservableReactComponent { const indent = i === 0 ? undefined : (editTitle: boolean) => dentDoc(editTitle, docs[i - 1], undefined, treeViewRefs.get(docs[i - 1])); const outdent = !parentCollectionDoc ? undefined : (editTitle: boolean) => dentDoc(editTitle, parentCollectionDoc, containerPrevSibling, parentTreeView instanceof TreeView ? parentTreeView._props.parentTreeView : undefined); const addDocument = (doc: Doc | Doc[], annotationKey?: string, relativeTo?: Doc, before?: boolean) => add(doc, relativeTo ?? docs[i], before !== undefined ? before : false); - const childLayout = Doc.Layout(pair.layout); + const childLayout = pair.layout[DocLayout]; const rowHeight = () => { const aspect = Doc.NativeAspect(childLayout); return aspect ? Math.min(NumCast(childLayout._width), rowWidth()) / aspect : NumCast(childLayout._height); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormClusters.ts b/src/client/views/collections/collectionFreeForm/CollectionFreeFormClusters.ts index 3838852dd..903d92c90 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormClusters.ts +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormClusters.ts @@ -31,16 +31,14 @@ export class CollectionFreeFormClusters { get selectDocuments() { return this._view.selectDocuments; } // prettier-ignore static overlapping(doc1: Doc, doc2: Doc, clusterDistance: number) { - const doc2Layout = Doc.Layout(doc2); - const doc1Layout = Doc.Layout(doc1); const x2 = NumCast(doc2.x) - clusterDistance; const y2 = NumCast(doc2.y) - clusterDistance; - const w2 = NumCast(doc2Layout._width) + clusterDistance; - const h2 = NumCast(doc2Layout._height) + clusterDistance; + const w2 = NumCast(doc2._width) + clusterDistance; + const h2 = NumCast(doc2._height) + clusterDistance; const x = NumCast(doc1.x) - clusterDistance; const y = NumCast(doc1.y) - clusterDistance; - const w = NumCast(doc1Layout._width) + clusterDistance; - const h = NumCast(doc1Layout._height) + clusterDistance; + const w = NumCast(doc1._width) + clusterDistance; + const h = NumCast(doc1._height) + clusterDistance; return doc1.z === doc2.z && intersectRect({ left: x, top: y, width: w, height: h }, { left: x2, top: y2, width: w2, height: h2 }); } handlePointerDown(probe: number[]) { @@ -49,12 +47,11 @@ export class CollectionFreeFormClusters { .reduce((cluster, cd) => { const grouping = this.Document._freeform_useClusters ? NumCast(cd.layout_cluster, -1) : NumCast(cd.group, -1); if (grouping !== -1) { - const layoutDoc = Doc.Layout(cd); const cx = NumCast(cd.x) - this._clusterDistance / 2; const cy = NumCast(cd.y) - this._clusterDistance / 2; - const cw = NumCast(layoutDoc._width) + this._clusterDistance; - const ch = NumCast(layoutDoc._height) + this._clusterDistance; - return !layoutDoc.z && intersectRect({ left: cx, top: cy, width: cw, height: ch }, { left: probe[0], top: probe[1], width: 1, height: 1 }) ? grouping : cluster; + const cw = NumCast(cd._width) + this._clusterDistance; + const ch = NumCast(cd._height) + this._clusterDistance; + return !cd.z && intersectRect({ left: cx, top: cy, width: cw, height: ch }, { left: probe[0], top: probe[1], width: 1, height: 1 }) ? grouping : cluster; } return cluster; }, -1); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx index 4ea1de680..158bac7ba 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx @@ -1,5 +1,6 @@ /* eslint-disable no-use-before-define */ import { Doc, Field, FieldType, FieldResult } from '../../../../fields/Doc'; +import { DocLayout } from '../../../../fields/DocSymbols'; import { Id, ToString } from '../../../../fields/FieldSymbols'; import { ObjectField } from '../../../../fields/ObjectField'; import { RefField } from '../../../../fields/RefField'; @@ -237,7 +238,7 @@ export function computePivotLayout(poolData: Map, pivotDoc: Do payload: val, }); val?.docs.forEach((doc, i) => { - const layoutDoc = Doc.Layout(doc); + const layoutDoc = doc[DocLayout]; let wid = pivotAxisWidth; let hgt = pivotAxisWidth / (Doc.NativeAspect(layoutDoc) || 1); if (hgt > pivotAxisWidth) { @@ -249,7 +250,7 @@ export function computePivotLayout(poolData: Map, pivotDoc: Do y: -y + (pivotAxisWidth - hgt) / 2, width: wid, height: hgt, - backgroundColor: StrCast(layoutDoc.backgroundColor, 'white'), + backgroundColor: StrCast(doc.backgroundColor, 'white'), pair: { layout: doc }, replica: val.replicas[i], }); @@ -362,7 +363,7 @@ export function computeTimelineLayout(poolData: Map, pivotDoc: function layoutDocsAtTime(keyDocs: Doc[], key: number) { keyDocs.forEach(doc => { const stack = findStack(x, stacking); - const layoutDoc = Doc.Layout(doc); + const layoutDoc = doc[DocLayout]; let wid = pivotAxisWidth; let hgt = pivotAxisWidth / (Doc.NativeAspect(layoutDoc) || 1); if (hgt > pivotAxisWidth) { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 09d43c5b0..f01fb8fc7 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -11,7 +11,7 @@ import ReactLoading from 'react-loading'; import { ClientUtils, DashColor, lightOrDark, OmitKeys, returnFalse, returnZero, setupMoveUpEvents, UpdateIcon } from '../../../../ClientUtils'; import { DateField } from '../../../../fields/DateField'; import { Doc, DocListCast, Field, FieldType, Opt, StrListCast } from '../../../../fields/Doc'; -import { DocData, Height, Width } from '../../../../fields/DocSymbols'; +import { DocData, DocLayout, Height, Width } from '../../../../fields/DocSymbols'; import { Id } from '../../../../fields/FieldSymbols'; import { InkData, InkEraserTool, InkField, InkInkTool, InkTool, Segment } from '../../../../fields/InkField'; import { List } from '../../../../fields/List'; @@ -286,7 +286,7 @@ export class CollectionFreeFormView extends CollectionSubView this.fitContentBounds?.cx ?? NumCast(this.Document[this.panXFieldKey], NumCast(this.Document.freeform_panX, 1)); panY = () => this.fitContentBounds?.cy ?? NumCast(this.Document[this.panYFieldKey], NumCast(this.Document.freeform_panY, 1)); - zoomScaling = () => this.fitContentBounds?.scale ?? NumCast(Doc.Layout(this.Document)[this.scaleFieldKey], 1); // , NumCast(DocCast(this.Document.rootDocument)?.[this.scaleFieldKey], 1)); + zoomScaling = () => this.fitContentBounds?.scale ?? NumCast(this.Document[this.scaleFieldKey], 1); // , NumCast(DocCast(this.Document.rootDocument)?.[this.scaleFieldKey], 1)); PanZoomCenterXf = () => (this._props.isAnnotationOverlay && this.zoomScaling() === 1 ? `` : `translate(${this.centeringShiftX}px, ${this.centeringShiftY}px) scale(${this.zoomScaling()}) translate(${-this.panX()}px, ${-this.panY()}px)`); ScreenToContentsXf = () => this.screenToFreeformContentsXf.copy(); getActiveDocuments = () => this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).map(pair => pair.layout); @@ -440,7 +440,7 @@ export class CollectionFreeFormView extends CollectionSubView { - const layoutDoc = Doc.Layout(d); + const layoutDoc = d[DocLayout]; const delta = Utils.rotPt(x - dropPos[0], y - dropPos[1], fromScreenXf.Rotate); if (this.Document._currentFrame !== undefined) { CollectionFreeFormDocumentView.setupKeyframes([d], NumCast(this.Document._currentFrame), false); @@ -1440,9 +1440,8 @@ export class CollectionFreeFormView extends CollectionSubView { - const layoutdoc = Doc.Layout(doc); const pt = xf.transformPoint(NumCast(doc.x), NumCast(doc.y)); - const pt2 = xf.transformPoint(NumCast(doc.x) + NumCast(layoutdoc._width), NumCast(doc.y) + NumCast(layoutdoc._height)); + const pt2 = xf.transformPoint(NumCast(doc.x) + NumCast(doc._width), NumCast(doc.y) + NumCast(doc._height)); const bounds = { left: pt[0], right: pt2[0], top: pt[1], bot: pt2[1], width: pt2[0] - pt[0], height: pt2[1] - pt[1] }; if (scale !== undefined) { @@ -1489,9 +1488,9 @@ export class CollectionFreeFormView extends CollectionSubView { - const textDoc = DocCast(textBox.Document, textBox.Document); + const textDoc = DocCast(textBox.Document); const newDoc = Doc.MakeCopy(textDoc, true); - newDoc['$' + Doc.LayoutFieldKey(newDoc, textBox._props.LayoutTemplateString)] = undefined; // the copy should not copy the text contents of it source, just the render style + newDoc['$' + Doc.LayoutFieldKey(newDoc)] = undefined; // the copy should not copy the text contents of it source, just the render style newDoc.x = NumCast(textDoc.x) + (below ? 0 : NumCast(textDoc._width) + 10); newDoc.y = NumCast(textDoc.y) + (below ? NumCast(textDoc._height) + 10 : 0); DocumentView.SetSelectOnLoad(newDoc); @@ -1600,14 +1599,13 @@ export class CollectionFreeFormView extends CollectionSubView /* min should not be equal to max */ min + (((Math.abs(x * y) * 9301 + 49297) % 233280) / 233280) * (max - min); const childDoc = pair.layout; - const childDocLayout = Doc.Layout(childDoc); const layoutFrameNumber = Cast(this.Document._currentFrame, 'number'); // frame number that container is at which determines layout frame values - const contentFrameNumber = Cast(childDocLayout._currentFrame, 'number', layoutFrameNumber ?? null); // frame number that content is at which determines what content is displayed + const contentFrameNumber = Cast(childDoc._currentFrame, 'number', layoutFrameNumber ?? null); // frame number that content is at which determines what content is displayed const { z, zIndex } = childDoc; const { backgroundColor, color } = contentFrameNumber === undefined ? { backgroundColor: undefined, color: undefined } : CollectionFreeFormDocumentView.getStringValues(childDoc, contentFrameNumber); const { x, y, autoDim, _width, _height, opacity, _rotation } = layoutFrameNumber === undefined // -1 for width/height means width/height should be PanelWidth/PanelHeight (prevents collectionfreeformdocumentview width/height from getting out of synch with panelWIdth/Height which causes detailView to re-render and lose focus because HTMLtag scaling gets set to a bad intermediate value) - ? { autoDim: 1, _width: Cast(childDoc._width, 'number'), _height: Cast(childDoc._height, 'number'), _rotation: Cast(childDocLayout._rotation, 'number'), x: childDoc.x, y: childDoc.y, opacity: this._props.childOpacity?.() } + ? { autoDim: 1, _width: Cast(childDoc._width, 'number'), _height: Cast(childDoc._height, 'number'), _rotation: Cast(childDoc._rotation, 'number'), x: childDoc.x, y: childDoc.y, opacity: this._props.childOpacity?.() } : CollectionFreeFormDocumentView.getValues(childDoc, layoutFrameNumber); // prettier-ignore const rotation = Cast(_rotation,'number', @@ -1625,8 +1623,8 @@ export class CollectionFreeFormView extends CollectionSubView { const selection: Doc[] = []; const selectFunc = (doc: Doc) => { - const layoutDoc = Doc.Layout(doc); - const bounds = { left: NumCast(doc.x), top: NumCast(doc.y), width: NumCast(layoutDoc._width), height: NumCast(layoutDoc._height) }; + const bounds = { left: NumCast(doc.x), top: NumCast(doc.y), width: NumCast(doc._width), height: NumCast(doc._height) }; if (!this._lassoFreehand) { intersectRect(bounds, this.Bounds) && selection.push(doc); } else { diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx index 05670562e..5803acca0 100644 --- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx +++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx @@ -200,7 +200,7 @@ export class CollectionSchemaView extends CollectionSubView() { this._props.setContentViewBox?.(this); document.addEventListener('keydown', this.onKeyDown); - Object.entries(this._documentOptions).forEach((pair: [string, FInfo]) => this.fieldInfos.set(pair[0], pair[1])); + Object.entries(this._documentOptions).forEach(pair => this.fieldInfos.set(pair[0], pair[1] as FInfo)); this._keysDisposer = observe( this.dataDoc[this.fieldKey ?? 'data'] as List, change => { diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx index 72838074e..173984dc7 100644 --- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx +++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx @@ -32,6 +32,7 @@ import { FInfotoColType } from './CollectionSchemaView'; import './CollectionSchemaView.scss'; import { SchemaColumnHeader } from './SchemaColumnHeader'; import { SchemaCellField } from './SchemaCellField'; +import { DocLayout } from '../../../../fields/DocSymbols'; /** * SchemaTableCells make up the majority of the visual representation of the SchemaView. @@ -104,7 +105,7 @@ export class SchemaTableCell extends ObservableReactComponent() }; @action handleRenderGPTClick = () => { - const phonTrans = DocCast(this.Document.audio) ? DocCast(this.Document.audio).phoneticTranscription : undefined; - if (phonTrans) { - this._inputValue = StrCast(phonTrans); - this.askGPTPhonemes(this._inputValue); - this._renderSide = this.backKey; - this._outputValue = ''; - } else if (this._inputValue) this.askGPT(GPTCallType.QUIZDOC); + if (this._inputValue) this.askGPT(GPTCallType.QUIZDOC); }; onPointerMove = ({ movementX }: PointerEvent) => { @@ -467,45 +460,6 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent() this._props.DocumentView?.()._props.addDocument?.(newAudio); }; - /** - * Gets the transcription of an audio recording by sending the - * recording to backend. - */ - pushInfo = () => - axios - .post( - 'http://localhost:105/recognize/', // - { file: DocCast(this.Document.audio).$url }, - { headers: { 'Content-Type': 'application/json' } } - ) - .then(response => { - this.Document.phoneticTranscription = response.data.transcription; - }); - - /** - * Extracts the id of the youtube video url. - * @param url - * @returns - */ - getYouTubeVideoId = (url: string) => { - const regExp = /^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|&v=|\?v=)([^#&?]*).*/; - const match = url.match(regExp); - return match && match[2].length === 11 ? match[2] : null; - }; - - /** - * Gets the transcript of a youtube video by sending the video url to the backend. - * @returns transcription of youtube recording - */ - youtubeUpload = async () => - axios - .post( - 'http://localhost:105/youtube/', // - { file: this.getYouTubeVideoId(this.frontText) }, - { headers: { 'Content-Type': 'application/json' } } - ) - .then(response => response.data.transcription); - /** * Calls GPT for each flashcard type. */ @@ -539,45 +493,6 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent() layoutWidth = () => NumCast(this.layoutDoc.width, 200); layoutHeight = () => NumCast(this.layoutDoc.height, 200); - /** - * Ask GPT for advice on how to improve speech by comparing the phonetic transcription of - * a users audio recording with the phonetic transcription of their intended sentence. - * @param phonemes - */ - askGPTPhonemes = async (phonemes: string) => { - const sentence = this.frontText; - const phon6 = 'huː ɑɹ juː tədeɪ'; - const phon4 = 'kamo estas hɔi'; - const promptEng = - 'Consider all possible phonetic transcriptions of the intended sentence "' + - sentence + - '" that is standard in American speech without showing the user. Compare each word in the following phonemes with those phonetic transcriptions without displaying anything to the user: "' + - phon6 + - '". Steps to do this: Align the words with each word in the intended sentence by combining the phonemes to get a pronunciation that resembles the word in order. Do not describe phonetic corrections with the phonetic alphabet - describe it by providing other examples of how it should sound. Note if a word or sound missing, including missing vowels and consonants. If there is an additional word that does not match with the provided sentence, say so. For each word, if any letters mismatch and would sound weird in American speech and they are not allophones of the same phoneme and they are far away from each on the ipa vowel chat and that pronunciation is not normal for the meaning of the word, note this difference and explain how it is supposed to sound. Only note the difference if they are not allophones of the same phoneme and if they are far away on the vowel chart. The goal is to be understood, not sound like a native speaker. Just so you know, "i" sounds like "ee" as in "bee", not "ih" as an "lick". Interpret "ɹ" as the same as "r". Interpret "ʌ" as the same as "ə". If "ɚ", "ɔː", and "ɔ" are options for pronunciation, do not choose "ɚ". Ignore differences with colons. Ignore redundant letters and words and sounds and the splitting of words; do not mention this since there could be repeated words in the sentence. Provide a response like this: "Lets work on improving the pronunciation of "coffee." You said "ceeffee," which is close, but we need to adjust the vowel sound. In American English, "coffee" is pronounced /ˈkɔːfi/, with a long "aw" sound. Try saying "kah-fee." Your intonation is good, but try putting a bit more stress on "like" in the sentence "I would like a coffee with milk." This will make your speech sound more natural. Keep practicing, and lets try saying the whole sentence again!"'; - const promptSpa = - 'Consider all possible phonetic transcriptions of the intended sentence "' + - 'como estás hoy' + - '" that is standard in Spanish speech without showing the user. Compare each word in the following phonemes with those phonetic transcriptions without displaying anything to the user: "' + - phon4 + - '". Steps to do this: Align the words with each word in the intended sentence by combining the phonemes to get a pronunciation that resembles the word in order. Do not describe phonetic corrections with the phonetic alphabet - describe it by providing other examples of how it should sound. Note if a word or sound missing, including missing vowels and consonants. If there is an additional word that does not match with the provided sentence, say so. For each word, if any letters mismatch and would sound weird in Spanish speech and they are not allophones of the same phoneme and they are far away from each on the ipa vowel chat and that pronunciation is not normal for the meaning of the word, note this difference and explain how it is supposed to sound. Only note the difference if they are not allophones of the same phoneme and if they are far away on the vowel chart; say good job if it would be understood by a native Spanish speaker. Just so you know, "i" sounds like "ee" as in "bee", not "ih" as an "lick". Interpret "ɹ" as the same as "r". Interpret "ʌ" as the same as "ə". Do not make "θ" and "f" interchangable. Do not make "n" and "ɲ" interchangable. Do not make "e" and "i" interchangable. If "ɚ", "ɔː", and "ɔ" are options for pronunciation, do not choose "ɚ". Ignore differences with colons. Ignore redundant letters and words and sounds and the splitting of words; do not mention this since there could be repeated words in the sentence. Identify "ɔi" sounds like "oy". Ignore accents and do not say anything to the user about this.'; - const promptAll = - 'Consider all possible phonetic transcriptions of the intended sentence "' + - sentence + - '" that is standard in ' + - this.convertAbr() + - ' speech without showing the user. Compare each word in the following phonemes with those phonetic transcriptions without displaying anything to the user: "' + - phonemes + - '". Steps to do this: Align the words with each word in the intended sentence by combining the phonemes to get a pronunciation that resembles the word in order. Do not describe phonetic corrections with the phonetic alphabet - describe it by providing other examples of how it should sound. Note if a word or sound missing, including missing vowels and consonants. If there is an additional word that does not match with the provided sentence, say so. For each word, if any letters mismatch and would sound weird in ' + - this.convertAbr() + - ' speech and they are not allophones of the same phoneme and they are far away from each on the ipa vowel chat and that pronunciation is not normal for the meaning of the word, note this difference and explain how it is supposed to sound. Just so you know, "i" sounds like "ee" as in "bee", not "ih" as an "lick". Interpret "ɹ" as the same as "r". Interpret "ʌ" as the same as "ə". Do not make "θ" and "f" interchangable. Do not make "n" and "ɲ" interchangable. Do not make "e" and "i" interchangable. If "ɚ", "ɔː", and "ɔ" are options for pronunciation, do not choose "ɚ". Ignore differences with colons. Ignore redundant letters and words and sounds and the splitting of words; do not mention this since there could be repeated words in the sentence. Provide a response like this: "Lets work on improving the pronunciation of "coffee." You said "cawffee," which is close, but we need to adjust the vowel sound. In American English, "coffee" is pronounced /ˈkɔːfi/, with a long "aw" sound. Try saying "kah-fee." Your intonation is good, but try putting a bit more stress on "like" in the sentence "I would like a coffee with milk." This will make your speech sound more natural. Keep practicing, and lets try saying the whole sentence again!"'; - - switch (this._recognition.lang) { - case 'en-US': this._outputValue = await gptAPICall(promptEng, GPTCallType.PRONUNCIATION); break; - case 'es-ES': this._outputValue = await gptAPICall(promptSpa, GPTCallType.PRONUNCIATION); break; - default: this._outputValue = await gptAPICall(promptAll, GPTCallType.PRONUNCIATION); break; - } // prettier-ignore - }; - /** * Display a user's speech to text result. * @param e @@ -637,31 +552,6 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent() !appearance && ContextMenu.Instance.addItem({ description: 'Appearance...', subitems: appearanceItems, icon: 'eye' }); }; - testForTextFields = (whichSlot: string) => { - const slotData = Doc.Get(this.dataDoc, whichSlot, true); - const slotHasText = slotData instanceof RichTextField || typeof slotData === '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 === this.frontKey ? (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_0) - if (whichSlot === this.backKey && !layoutTemplateString?.includes(whichSlot)) { - const queryText = altText?.replace('(this)', subjectText); // TODO: this should be done in Doc.setField but it doesn't know about the fieldKey ... - if (queryText?.match(/\(\(.*\)\)/)) { - Doc.SetField(this.Document, whichSlot, ':=' + queryText); // make the second slot be a computed field on the data doc that calls ChatGpt - } - } - return layoutTemplateString; - }; childActiveFunc = () => this._childActive; contentScreenToLocalXf = () => this._props.ScreenToLocalTransform().scale(this._props.NativeDimScaling?.() || 1); @@ -682,24 +572,21 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent() displayDoc = (whichSlot: string) => { const whichDoc = DocCast(this.dataDoc[whichSlot]); const targetDoc = DocCast(whichDoc?.annotationOn, whichDoc); - const layoutString = targetDoc ? '' : this.testForTextFields(whichSlot); - return targetDoc || layoutString ? ( + return targetDoc ? ( <> ()
- {/*
this.openContextMenu(e.clientX, e.clientY, false)}> - -
*/} - {/* */} - {/*
this.openContextMenu(e.clientX, e.clientY, true)} style={{ left: '50px', zIndex: '100' }}> - -
*/} - {/* */} diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index d1eae1784..6f004bed3 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -11,10 +11,85 @@ import { Cast, DocCast, StrCast } from '../../../fields/Types'; import { GetEffectiveAcl, TraceMobx } from '../../../fields/util'; import { ObservableReactComponent, ObserverJsxParser } from '../ObservableReactComponent'; import './DocumentView.scss'; -import { FieldViewProps } from './FieldView'; +import { FieldViewProps, FieldViewSharedProps } from './FieldView'; +import { DragManager } from '../../util/DragManager'; +import { dropActionType } from '../../util/DropActionTypes'; +import { Property } from 'csstype'; + +interface DocOnlyProps { + LayoutTemplate?: () => Opt; + LayoutTemplateString?: string; + hideDecorations?: boolean; // whether to suppress all DocumentDecorations when doc is selected + hideResizeHandles?: boolean; // whether to suppress resized handles on doc decorations when this document is selected + hideTitle?: boolean; // forces suppression of title. e.g, treeView document labels suppress titles in case they are globally active via settings + hideDecorationTitle?: boolean; // forces suppression of title. e.g, treeView document labels suppress titles in case they are globally active via settings + hideDocumentButtonBar?: boolean; + hideOpenButton?: boolean; + hideDeleteButton?: boolean; + hideLinkAnchors?: boolean; + hideLinkButton?: boolean; + hideCaptions?: boolean; + contentPointerEvents?: Property.PointerEvents | undefined; // pointer events allowed for content of a document view. eg. set to "none" in menuSidebar for sharedDocs so that you can select a document, but not interact with its contents + dontCenter?: 'x' | 'y' | 'xy'; + showTags?: boolean; + showAIEditor?: boolean; + hideFilterStatus?: boolean; + childHideDecorationTitle?: boolean; + childHideResizeHandles?: boolean; + childDragAction?: dropActionType; // allows child documents to be dragged out of collection without holding the embedKey or dragging the doc decorations title bar. + dragWhenActive?: boolean; + dontHideOnDrag?: boolean; + onClickScriptDisable?: 'never' | 'always'; // undefined = only when selected + DataTransition?: () => string | undefined; + NativeWidth?: () => number; + NativeHeight?: () => number; + contextMenuItems?: () => { script?: ScriptField; method?: () => void; filter?: ScriptField; label: string; icon: string }[]; + dragConfig?: (data: DragManager.DocumentDragData) => void; + dragStarting?: () => void; + dragEnding?: () => void; + + reactParent?: React.Component; // parent React component view (see CollectionFreeFormDocumentView) +} +const DocOnlyProps = [ + 'layoutFieldKey', + 'LayoutTemplate', + 'LayoutTemplateString', + 'hideDecorations', // whether to suppress all DocumentDecorations when doc is selected + 'hideResizeHandles', // whether to suppress resized handles on doc decorations when this document is selected + 'hideTitle', // forces suppression of title. e.g, treeView document labels suppress titles in case they are globally active via settings + 'hideDecorationTitle', // forces suppression of title. e.g, treeView document labels suppress titles in case they are globally active via settings + 'hideDocumentButtonBar', + 'hideOpenButton', + 'hideDeleteButton', + 'hideLinkAnchors', + 'hideLinkButton', + 'hideCaptions', + 'contentPointerEvents', // pointer events allowed for content of a document view. eg. set to "none" in menuSidebar for sharedDocs so that you can select a document, but not interact with its contents + 'dontCenter', + 'showTags', + 'showAIEditor', + 'hideFilterStatus', + 'childHideDecorationTitle', + 'childHideResizeHandles', + 'childDragAction', // allows child documents to be dragged out of collection without holding the embedKey or dragging the doc decorations title bar. + 'dragWhenActive', + 'dontHideOnDrag', + 'onClickScriptDisable', // undefined = only when selected + 'DataTransition', + 'NativeWidth', + 'NativeHeight', + 'contextMenuItems', + 'dragConfig', + 'dragStarting', + 'dragEnding', + + 'reactParent', // parent React component view (see CollectionFreeFormDocumentView) +]; + +export interface DocumentViewProps extends DocOnlyProps, FieldViewSharedProps {} type BindingProps = Without; -export interface JsxBindings { +interface JsxBindings { props: BindingProps; } @@ -77,7 +152,7 @@ export class HTMLtag extends React.Component { } } -export interface DocumentContentsViewProps extends FieldViewProps { +interface DocumentContentsViewProps extends DocumentViewProps, FieldViewProps { layoutFieldKey: string; } @observer @@ -118,24 +193,6 @@ export class DocumentContentsView extends ObservableReactComponent, onInput: Opt): JsxBindings { - const docOnlyProps = [ - // these are the properties in DocumentViewProps that need to be removed to pass on only DocumentSharedViewProps to the FieldViews - 'hideResizeHandles', - 'hideTitle', - 'bringToFront', - 'childContentPointerEvents', - 'LayoutTemplateString', - 'LayoutTemplate', - 'showTags', - 'layoutFieldKey', - 'dontCenter', - 'DataTransition', - 'contextMenuItems', - // 'onClick', // don't need to omit this since it will be set - 'onDoubleClickScript', - 'onPointerDownScript', - 'onPointerUpScript', - ]; const templateDataDoc = this._props.TemplateDataDocument ?? (this.layoutDoc !== this._props.Document ? this._props.Document[DocData] : undefined); const list: BindingProps & React.DetailedHTMLProps, HTMLDivElement> = { ...this._props, @@ -146,7 +203,7 @@ export class DocumentContentsView extends ObservableReactComponent string | undefined; - NativeWidth?: () => number; - NativeHeight?: () => number; - contextMenuItems?: () => { script?: ScriptField; method?: () => void; filter?: ScriptField; label: string; icon: string }[]; - dragConfig?: (data: DragManager.DocumentDragData) => void; - dragStarting?: () => void; - dragEnding?: () => void; - - reactParent?: React.Component; // parent React component view (see CollectionFreeFormDocumentView) -} @observer -export class DocumentViewInternal extends DocComponent() { +export class DocumentViewInternal extends DocComponent() { // this makes mobx trace() statements more descriptive public get displayName() { return 'DocumentViewInternal(' + this.Document.title + ')'; } // prettier-ignore public static SelectAfterContextMenu = true; // whether a document should be selected after it's contextmenu is triggered. @@ -809,7 +778,7 @@ export class DocumentViewInternal extends DocComponent )} - {this.widgetDecorations ?? null} +
{this.widgetDecorations ?? null}
); } @@ -1271,9 +1240,8 @@ export class DocumentView extends DocComponent() { public get ContentDiv() { return this._docViewInternal?._contentDiv; } // prettier-ignore public get ComponentView() { return this._docViewInternal?._componentView; } // prettier-ignore public get allLinks() { return this._docViewInternal?._allLinks ?? []; } // prettier-ignore - public get TagBtnHeight() { - return this._docViewInternal?.TagsBtnHeight; - } + public get TagBtnHeight() { return this._docViewInternal?.TagsBtnHeight; } // prettier-ignore + public get UIBtnScaling() { return this._docViewInternal?.uiBtnScaling; } // prettier-ignore get LayoutFieldKey() { return Doc.LayoutFieldKey(this.Document, this._props.LayoutTemplateString); diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index 933868c46..74059bd12 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -48,8 +48,6 @@ export type StyleProviderFuncType = ( export interface FieldViewSharedProps { Document: Doc; TemplateDataDocument?: Doc; - LayoutTemplateString?: string; - LayoutTemplate?: () => Opt; renderDepth: number; scriptContext?: unknown; // can be assigned anything and will be passed as 'scriptContext' to any OnClick script that executes on this document screenXPadding?: () => number; // padding in screen space coordinates (used by text box to reflow around UI buttons in carouselView) @@ -62,7 +60,8 @@ 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) + suppressSetHeight?: boolean; + dontCenter?: 'x' | 'y' | 'xy' | undefined; LocalRotation?: () => number | undefined; // amount of rotation applied to freeformdocumentview containing document view containerViewPath?: () => DocumentView[]; fitContentsToBox?: () => boolean; // used by freeformview to fit its contents to its panel. corresponds to _freeform_fitContentsToBox property on a Document @@ -86,7 +85,6 @@ export interface FieldViewSharedProps { onPointerUpScript?: () => ScriptField; onKey?: (e: KeyboardEvent, textBox: FormattedTextBox) => boolean | undefined; fitWidth?: (doc: Doc) => boolean | undefined; - dontCenter?: 'x' | 'y' | 'xy' | undefined; searchFilterDocs: () => Doc[]; showTitle?: () => string; whenChildContentsActiveChanged: (isActive: boolean) => void; @@ -102,7 +100,6 @@ export interface FieldViewSharedProps { waitForDoubleClickToClick?: () => 'never' | 'always' | undefined; defaultDoubleClick?: () => 'default' | 'ignore' | undefined; pointerEvents?: () => Opt; - suppressSetHeight?: boolean; } /** @@ -110,13 +107,13 @@ export interface FieldViewSharedProps { * */ export interface FieldViewProps extends FieldViewSharedProps { DocumentView?: () => DocumentView; - fieldKey: string; isSelected: () => boolean; select: (ctrlPressed: boolean, shiftPress?: boolean) => void; docViewPath: () => DocumentView[]; setHeight?: (height: number) => void; NativeDimScaling?: () => number; // scaling the DocumentView does to transform its contents into its panel & needed by ScreenToLocal isHovering?: () => boolean; + fieldKey: string; // properties intended to be used from within layout strings (otherwise use the function equivalents that work more efficiently with React) // See currentUserUtils headerTemplate for examples of creating text boxes from html which set some of these fields diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index d89fe0bd4..030473be4 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -47,6 +47,7 @@ import './ImageBox.scss'; import { OpenWhere } from './OpenWhere'; import { RichTextField } from '../../../fields/RichTextField'; +const DefaultPath = '/assets/unknown-file-icon-hi.png'; export class ImageEditorData { // eslint-disable-next-line no-use-before-define private static _instance: ImageEditorData; @@ -426,7 +427,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent() { 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 ClientUtils.CorsProxy(url.href); - if (!/\.(png|jpg|jpeg|gif|webp)$/.test(lower) || lower.endsWith('/assets/unknown-file-icon-hi.png')) return `/assets/unknown-file-icon-hi.png`; + if (!/\.(png|jpg|jpeg|gif|webp)$/.test(lower) || lower.endsWith(DefaultPath)) return DefaultPath; const ext = extname(url.href); return url.href.replace(ext, (this._error ? '_o' : this._curSuffix) + ext); @@ -440,7 +441,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent() { @computed get nativeSize() { TraceMobx(); - if (this.paths.length && this.paths[0].includes('icon-hi')) return { nativeWidth: NumCast(this.layoutDoc._width), nativeHeight: NumCast(this.layoutDoc._height), nativeOrientation: 0 }; + if (this.paths.length && this.paths[0].includes(DefaultPath)) return { nativeWidth: NumCast(this.layoutDoc._width), nativeHeight: NumCast(this.layoutDoc._height), nativeOrientation: 0 }; const nativeWidth = NumCast(this.dataDoc[this.fieldKey + '_nativeWidth'], NumCast(this.layoutDoc[this.fieldKey + '_nativeWidth'], 500)); const nativeHeight = NumCast(this.dataDoc[this.fieldKey + '_nativeHeight'], NumCast(this.layoutDoc[this.fieldKey + '_nativeHeight'], 500)); const nativeOrientation = NumCast(this.dataDoc[this.fieldKey + '_nativeOrientation'], 1); @@ -527,7 +528,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent() { @computed get paths() { const field = this.dataDoc[this.fieldKey] instanceof ImageField ? Cast(this.dataDoc[this.fieldKey], ImageField, null) : new ImageField(String(this.dataDoc[this.fieldKey])); // 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 defaultUrl = new URL(ClientUtils.prepend('/assets/unknown-file-icon-hi.png')); + const defaultUrl = new URL(ClientUtils.prepend(DefaultPath)); const altpaths = alts ?.map(doc => (doc instanceof Doc ? (ImageCast(doc[Doc.LayoutFieldKey(doc)])?.url ?? defaultUrl) : defaultUrl)) @@ -703,21 +704,24 @@ export class ImageBox extends ViewBoxAnnotatableComponent() { } screenToLocalTransform = () => this.ScreenToLocalBoxXf().translate(0, NumCast(this.layoutDoc._layout_scrollTop) * this.ScreenToLocalBoxXf().Scale); marqueeDown = (e: React.PointerEvent) => { - if (!this.dataDoc[this.fieldKey]) { - this.chooseImage(); - } else if (!e.altKey && e.button === 0 && NumCast(this.layoutDoc._freeform_scale, 1) <= NumCast(this.dataDoc.freeform_scaleMin, 1) && this._props.isContentActive() && Doc.ActiveTool !== InkTool.Ink) { - setupMoveUpEvents( - this, - e, - action(moveEv => { - MarqueeAnnotator.clearAnnotations(this._savedAnnotations); - this.marqueeref.current?.onInitiateSelection([moveEv.clientX, moveEv.clientY]); - return true; - }), - returnFalse, - () => MarqueeAnnotator.clearAnnotations(this._savedAnnotations), - false - ); + if (!e.altKey && e.button === 0) { + if (NumCast(this.layoutDoc._freeform_scale, 1) <= NumCast(this.dataDoc.freeform_scaleMin, 1) && this._props.isContentActive() && Doc.ActiveTool !== InkTool.Ink) { + setupMoveUpEvents( + this, + e, + action(moveEv => { + MarqueeAnnotator.clearAnnotations(this._savedAnnotations); + this.marqueeref.current?.onInitiateSelection([moveEv.clientX, moveEv.clientY]); + return true; + }), + returnFalse, + () => { + if (!this.dataDoc[this.fieldKey]) this.chooseImage(); + else MarqueeAnnotator.clearAnnotations(this._savedAnnotations); + }, + false + ); + } } }; @action diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx index bb711a5fc..be897b3f3 100644 --- a/src/client/views/nodes/KeyValueBox.tsx +++ b/src/client/views/nodes/KeyValueBox.tsx @@ -22,6 +22,7 @@ import './KeyValueBox.scss'; import { KeyValuePair } from './KeyValuePair'; import { OpenWhere } from './OpenWhere'; import { FormattedTextBox } from './formattedText/FormattedTextBox'; +import { DocLayout } from '../../../fields/DocSymbols'; export type KVPScript = { script: CompiledScript; @@ -125,7 +126,7 @@ export class KeyValueBox extends ViewBoxBaseComponent() { public static SetField = undoable((doc: Doc, key: string, value: string, forceOnDelegateIn?: boolean, setResult?: (value: FieldResult) => void) => { const script = KeyValueBox.CompileKVPScript(value); if (!script) return false; - const ldoc = key.startsWith('_') ? Doc.Layout(doc) : doc; + const ldoc = key.startsWith('_') ? doc[DocLayout] : doc; const forceOnDelegate = forceOnDelegateIn || (ldoc !== doc && !value.startsWith('=')); // an '=' value redirects a key targeting the render template to the root document. // also, if ldoc and doc are equal, allow redirecting to data document when not using an equal diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx index e3c481a75..c9e0aea5a 100644 --- a/src/client/views/nodes/KeyValuePair.tsx +++ b/src/client/views/nodes/KeyValuePair.tsx @@ -16,6 +16,7 @@ import { DefaultStyleProvider, returnEmptyDocViewList } from '../StyleProvider'; import './KeyValueBox.scss'; import './KeyValuePair.scss'; import { OpenWhere } from './OpenWhere'; +import { DocLayout } from '../../../fields/DocSymbols'; // Represents one row in a key value plane @@ -60,7 +61,7 @@ export class KeyValuePair extends ObservableReactComponent { }; render() { - let doc = this._props.keyName.startsWith('_') ? Doc.Layout(this._props.doc) : this._props.doc; + let doc = this._props.keyName.startsWith('_') ? this._props.doc[DocLayout] : this._props.doc; const layoutField = doc !== this._props.doc; const key = layoutField ? this._props.keyName.replace(/^_/, '') : this._props.keyName; let protoCount = 0; diff --git a/src/client/views/nodes/PDFBox.scss b/src/client/views/nodes/PDFBox.scss index f2160feb7..eaea272dc 100644 --- a/src/client/views/nodes/PDFBox.scss +++ b/src/client/views/nodes/PDFBox.scss @@ -23,9 +23,9 @@ // glr: This should really be the same component as text and PDFs .pdfBox-sidebarBtn { background: global.$black; - height: 25px; - width: 25px; - right: 5px; + height: 20px; + width: 20px; + right: 0px; color: global.$white; display: flex; position: absolute; diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 78ddafa88..36d260fb9 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -499,8 +499,10 @@ export class PDFBox extends ViewBoxAnnotatableComponent() { title="Toggle Sidebar" style={{ display: !this._props.isContentActive() ? 'none' : undefined, - top: StrCast(this.layoutDoc._layout_showTitle) === 'title' ? 20 : 5, + top: StrCast(this.layoutDoc._layout_showTitle) === 'title' ? 20 : 0, backgroundColor: this.SidebarShown ? Colors.MEDIUM_BLUE : Colors.BLACK, + transformOrigin: 'top right', + transform: `scale(${this._props.DocumentView?.().UIBtnScaling || 1})`, }} onPointerDown={e => this.sidebarBtnDown(e, true)}> diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.scss b/src/client/views/nodes/formattedText/FormattedTextBox.scss index f9de4ab5a..547a2efa8 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.scss +++ b/src/client/views/nodes/formattedText/FormattedTextBox.scss @@ -141,8 +141,8 @@ audiotag:hover { position: absolute; top: 0; right: 0; - width: 17px; - height: 17px; + width: 20px; + height: 20px; font-size: 11px; border-radius: 3px; color: white; @@ -153,6 +153,7 @@ audiotag:hover { align-items: center; cursor: grabbing; box-shadow: global.$standard-box-shadow; + transform-origin: top right; // transition: 0.2s; opacity: 0.3; &:hover { diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 416cd577e..51b9a4333 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -1941,6 +1941,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent
@@ -2050,7 +2051,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { diff --git a/src/client/views/nodes/importBox/ImportElementBox.tsx b/src/client/views/nodes/importBox/ImportElementBox.tsx index 317719032..7e470a642 100644 --- a/src/client/views/nodes/importBox/ImportElementBox.tsx +++ b/src/client/views/nodes/importBox/ImportElementBox.tsx @@ -22,9 +22,7 @@ export class ImportElementBox extends ViewBoxBaseComponent() { return (
{ panelHeight = () => this._props.PanelHeight() / (this._props.NativeDimScaling?.() || 1); transparentFilter = () => [...this._props.childFilters(), ClientUtils.TransparentBackgroundFilter]; opaqueFilter = () => [...this._props.childFilters(), ClientUtils.noDragDocsFilter, ...(SnappingManager.CanEmbed && this._props.isContentActive() ? [] : [ClientUtils.OpaqueBackgroundFilter])]; - childStyleProvider = (doc: Doc | undefined, props: Opt, property: string) => { + childStyleProvider = (doc: Doc | undefined, props: Opt, property: string) => { if (doc instanceof Doc && property === StyleProp.PointerEvents) { if (this.inlineTextAnnotations.includes(doc) || this._props.isContentActive() === false) return 'none'; const isInk = doc.layout_isSvg && !props?.LayoutTemplateString; diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 65719374f..734af4ccd 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -64,7 +64,7 @@ export namespace Field { .trim() .replace(/^new List\((.*)\)$/, '$1'); }; - const notOnTemplate = !key.startsWith('_') || Doc.Layout(doc) === doc; + const notOnTemplate = !key.startsWith('_') || doc[DocLayout] === doc; const isOnDelegate = notOnTemplate && !Doc.IsDataProto(doc) && ((key.startsWith('_') && !Field.IsField(cfield)) || Object.keys(doc).includes(key.replace(/^_/, ''))); return (isOnDelegate ? '=' : '') + (!Field.IsField(cfield) ? '' : valFunc(cfield)); } @@ -407,9 +407,9 @@ export class Doc extends RefField { } @computed get __DATA__(): Doc { const self = this[SelfProxy]; - return self.rootDocument && !self.isTemplateForField ? self : Doc.GetProto(Cast(Doc.Layout(self).rootDocument, Doc, null) || self); + return self.rootDocument && !self.isTemplateForField ? self : Doc.GetProto(Cast(self[DocLayout].rootDocument, Doc, null) || self); } - @computed get __LAYOUT__(): Doc | undefined { + @computed get __LAYOUT__(): Doc { const self = this[SelfProxy]; const templateLayoutDoc = Cast(Doc.LayoutField(self), Doc, null); if (templateLayoutDoc) { @@ -422,7 +422,7 @@ export class Doc extends RefField { } return Cast(self['layout_' + templateLayoutDoc.title + '(' + renderFieldKey + ')'], Doc, null) || templateLayoutDoc; } - return undefined; + return self; } public async [HandleUpdate](diff: { $set: { [key: string]: FieldType } } | { $unset?: unknown }) { @@ -687,15 +687,11 @@ export namespace Doc { } export function MakeEmbedding(doc: Doc, id?: string) { - const embedding = (!GetT(doc, 'isDataDoc', 'boolean', true) && doc.proto) || doc.type === DocumentType.CONFIG ? Doc.MakeCopy(doc, undefined, id) : Doc.MakeDelegate(doc, id); - const layout = Doc.LayoutField(embedding); - if (layout instanceof Doc && layout !== embedding && layout === Doc.Layout(embedding)) { - Doc.SetLayout(embedding, Doc.MakeEmbedding(layout)); - } + const embedding = (!Doc.IsDataProto(doc) && doc.proto) || doc.type === DocumentType.CONFIG ? Doc.MakeCopy(doc, undefined, id) : Doc.MakeDelegate(doc, id); embedding.createdFrom = doc; - embedding.proto_embeddingId = doc.$proto_embeddingId = Doc.GetEmbeddings(doc).length - 1; - !Doc.GetT(embedding, 'title', 'string', true) && (embedding.title = ComputedField.MakeFunction(`renameEmbedding(this)`)); embedding.author = ClientUtils.CurrentUserEmail(); + embedding.proto_embeddingId = doc.$proto_embeddingId = Doc.GetEmbeddings(doc).length - 1; + embedding.title = ComputedField.MakeFunction(`renameEmbedding(this)`); return embedding; } @@ -768,9 +764,8 @@ export namespace Doc { // prune doc and do nothing } else if ( !Doc.IsSystem(docAtKey) && - (key.includes('layout[') || - key.startsWith('layout') || // - ['embedContainer', 'annotationOn', 'proto'].includes(key) || + (key.startsWith('layout') || + ['embedContainer', 'annotationOn', 'proto'].includes(key) || // (['link_anchor_1', 'link_anchor_2'].includes(key) && doc.author === ClientUtils.CurrentUserEmail())) ) { assignKey(await Doc.makeClone(docAtKey, cloneMap, linkMap, rtfs, exclusions, pruneDocs, cloneLinks, cloneTemplates)); @@ -1117,10 +1112,10 @@ export namespace Doc { Doc.GetProto(templateField)[metadataFieldKey] = ObjectField.MakeCopy(templateFieldValue); } // get the layout string that the template uses to specify its layout - const templateFieldLayoutString = StrCast(Doc.LayoutField(Doc.Layout(templateField))); + const templateFieldLayoutString = StrCast(Doc.LayoutField(templateField[DocLayout])); // change it to render the target metadata field instead of what it was rendering before and assign it to the template field layout document. - Doc.Layout(templateField).layout = templateFieldLayoutString.replace(/fieldKey={'[^']*'}/, `fieldKey={'${metadataFieldKey}'}`); + templateField[DocLayout].layout = templateFieldLayoutString.replace(/fieldKey={'[^']*'}/, `fieldKey={'${metadataFieldKey}'}`); return true; } @@ -1157,16 +1152,13 @@ export namespace Doc { // a layout field or 'layout' is given. export function Layout(doc: Doc, template?: Doc): Doc { const expandedTemplate = template && Cast(doc['layout_' + template.title + '(' + StrCast(template.isTemplateForField, 'data') + ')'], Doc, null); - return expandedTemplate || doc[DocLayout] || doc; - } - export function SetLayout(doc: Doc, layout: Doc | string) { - doc[StrCast(doc.layout_fieldKey, 'layout')] = layout; + return expandedTemplate || doc[DocLayout]; } export function LayoutField(doc: Doc) { return doc[StrCast(doc.layout_fieldKey, 'layout')]; } export function LayoutFieldKey(doc: Doc, templateLayoutString?: string): string { - const match = StrCast(templateLayoutString || Doc.Layout(doc).layout).match(/fieldKey={'([^']+)'}/); + const match = StrCast(templateLayoutString || doc[DocLayout].layout).match(/fieldKey={'([^']+)'}/); return match?.[1] || ''; // bcz: TODO check on this . used to always reference 'layout', now it uses the layout speicfied by the current layout_fieldKey } export function NativeAspect(doc: Doc, dataDoc?: Doc, useDim?: boolean) { @@ -1325,17 +1317,7 @@ export namespace Doc { } export function getDocTemplate(doc?: Doc) { - return !doc - ? undefined - : doc.isTemplateDoc - ? doc - : Cast(doc.dragFactory, Doc, null)?.isTemplateDoc - ? doc.dragFactory - : Cast(Doc.Layout(doc), Doc, null)?.isTemplateDoc - ? Cast(Doc.Layout(doc), Doc, null).rootDocument - ? Doc.Layout(doc).proto - : Doc.Layout(doc) - : undefined; + return !doc ? undefined : doc.isTemplateDoc ? doc : Cast(doc.dragFactory, Doc, null)?.isTemplateDoc ? doc.dragFactory : doc[DocLayout].isTemplateDoc ? (doc[DocLayout].rootDocument ? doc[DocLayout].proto : doc[DocLayout]) : undefined; } export function toggleLockedPosition(doc: Doc) { @@ -1737,7 +1719,7 @@ ScriptingGlobals.add(function idToDoc(id: string): Doc { }); // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function renameEmbedding(doc: Doc) { - return StrCast(doc.$title).replace(/\([0-9]*\)/, '') + `(${doc.proto_embeddingId})`; + return StrCast(doc.title).replace(/\([0-9]*\)/, '') + `(${doc.proto_embeddingId})`; }); // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function getProto(doc: Doc) { diff --git a/src/fields/util.ts b/src/fields/util.ts index abbe543e8..205c500a5 100644 --- a/src/fields/util.ts +++ b/src/fields/util.ts @@ -303,30 +303,23 @@ export function SetPropSetterCb(prop: string, propSetter: ((target: Doc, value: // // target should be either a Doc or ListImpl. receiver should be a Proxy Or List. // -export function setter(target: ListImpl | Doc, inProp: string | symbol | number, value: unknown, receiver: Doc | ListImpl): boolean { - if (!inProp) { +export function setter(target: ListImpl | Doc, prop: string | symbol | number, value: unknown, receiver: Doc | ListImpl): boolean { + if (!prop) { console.log('WARNING: trying to set an empty property. This should be fixed. '); return false; } - let prop = inProp; - const effectiveAcl = inProp === 'constructor' || typeof inProp === 'symbol' ? AclAdmin : GetPropAcl(target, prop); + const effectiveAcl = prop === 'constructor' || typeof prop === 'symbol' ? AclAdmin : GetPropAcl(target, prop); if (effectiveAcl !== AclEdit && effectiveAcl !== AclAugment && effectiveAcl !== AclAdmin) return true; // if you're trying to change an acl but don't have Admin access / you're trying to change it to something that isn't an acceptable acl, you can't if (typeof prop === 'string' && prop.startsWith('acl_') && (effectiveAcl !== AclAdmin || ![...Object.values(SharingPermissions), undefined].includes(value as SharingPermissions))) return true; - if (typeof prop === 'string' && prop !== '__id' && prop !== '__fieldTuples' && prop.startsWith('$')) { - prop = prop.substring(1); - if (target.__DATA__ instanceof Doc) { - target.__DATA__[prop] = value as FieldResult; - return true; - } + if (target instanceof Doc && typeof prop === 'string' && prop !== '__id' && prop !== '__fieldTuples' && prop.startsWith('$')) { + target.__DATA__[prop.substring(1)] = value as FieldResult; + return true; } - if (typeof prop === 'string' && prop !== '__id' && prop !== '__fieldTuples' && prop.startsWith('_')) { - if (!prop.startsWith('__')) prop = prop.substring(1); - if (target.__LAYOUT__ instanceof Doc) { - target.__LAYOUT__[prop] = value as FieldResult; - return true; - } + if (target instanceof Doc && typeof prop === 'string' && prop !== '__id' && prop !== '__fieldTuples' && prop.startsWith('_') && !prop.startsWith('__')) { + target.__LAYOUT__[prop.substring(1)] = value as FieldResult; + return true; } if (target.__fieldTuples[prop] instanceof ComputedField) { if (target.__fieldTuples[prop].setterscript && value !== undefined && !(value instanceof ComputedField)) { -- cgit v1.2.3-70-g09d2 From d818ef151ca65008e5c6bb5e92b709decb3026d8 Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 14 Apr 2025 18:35:49 -0400 Subject: fixed how templates are expanded to avoid template sub-component conflicts by changing how field keys are named. fixed various Cast functions to be more typesafe by including undefined as part of return type. overhaul of Doc.MakeClone, MakeCopy, FindRefernces - makeClone is no longer async. fixed inlined docs in text docs. --- src/client/documents/DocUtils.ts | 41 +- src/client/documents/DocumentTypes.ts | 2 +- src/client/documents/Documents.ts | 8 +- src/client/util/CurrentUserUtils.ts | 37 +- src/client/util/DictationManager.ts | 2 +- src/client/util/DocumentManager.ts | 2 +- src/client/util/DragManager.ts | 40 +- src/client/util/DropConverter.ts | 6 +- src/client/util/Import & Export/ImageUtils.ts | 2 +- src/client/util/LinkFollower.ts | 32 +- src/client/util/LinkManager.ts | 43 +- src/client/util/SearchUtil.ts | 6 +- src/client/util/SettingsManager.tsx | 2 +- src/client/views/DocComponent.tsx | 8 +- src/client/views/DocumentButtonBar.tsx | 8 +- src/client/views/FilterPanel.tsx | 4 +- src/client/views/GlobalKeyHandler.ts | 6 +- src/client/views/InkStrokeProperties.ts | 2 +- src/client/views/InkTranscription.tsx | 6 +- src/client/views/LightboxView.tsx | 4 +- src/client/views/Main.tsx | 4 +- src/client/views/MainView.tsx | 42 +- src/client/views/PinFuncs.ts | 8 +- src/client/views/PropertiesButtons.tsx | 2 +- src/client/views/PropertiesView.tsx | 8 +- src/client/views/StyleProvider.tsx | 12 +- src/client/views/StyleProviderQuiz.tsx | 2 +- src/client/views/TagsView.tsx | 6 +- src/client/views/animationtimeline/Timeline.tsx | 2 +- .../views/collections/CollectionCarouselView.tsx | 3 +- .../views/collections/CollectionDockingView.tsx | 16 +- .../views/collections/CollectionNoteTakingView.tsx | 11 +- .../views/collections/CollectionStackingView.tsx | 12 +- .../CollectionStackingViewFieldColumn.tsx | 4 +- src/client/views/collections/CollectionSubView.tsx | 37 +- src/client/views/collections/TabDocView.tsx | 14 +- src/client/views/collections/TreeView.tsx | 20 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 31 +- .../collectionFreeForm/FaceCollectionBox.tsx | 2 +- .../collectionFreeForm/ImageLabelBox.tsx | 4 +- .../collections/collectionFreeForm/MarqueeView.tsx | 6 +- .../collectionLinear/CollectionLinearView.tsx | 4 +- .../collectionSchema/CollectionSchemaView.tsx | 4 +- .../collectionSchema/SchemaTableCell.tsx | 2 +- src/client/views/global/globalScripts.ts | 37 +- src/client/views/newlightbox/NewLightboxView.tsx | 2 +- src/client/views/nodes/AudioBox.tsx | 2 +- .../views/nodes/CollectionFreeFormDocumentView.tsx | 4 +- src/client/views/nodes/DataVizBox/DataVizBox.tsx | 2 +- src/client/views/nodes/DocumentContentsView.tsx | 4 +- src/client/views/nodes/DocumentView.tsx | 100 ++-- src/client/views/nodes/FieldView.tsx | 2 +- src/client/views/nodes/IconTagBox.tsx | 2 +- src/client/views/nodes/ImageBox.tsx | 24 +- src/client/views/nodes/KeyValueBox.tsx | 8 +- src/client/views/nodes/KeyValuePair.tsx | 4 +- src/client/views/nodes/PDFBox.tsx | 2 +- .../views/nodes/RecordingBox/RecordingBox.tsx | 2 +- src/client/views/nodes/ScreenshotBox.tsx | 2 +- src/client/views/nodes/ScriptingBox.tsx | 13 +- src/client/views/nodes/WebBox.tsx | 18 +- .../nodes/chatbot/chatboxcomponents/ChatBox.tsx | 2 +- .../views/nodes/formattedText/FormattedTextBox.tsx | 55 +- .../views/nodes/formattedText/RichTextMenu.tsx | 6 +- .../views/nodes/formattedText/RichTextRules.ts | 19 +- src/client/views/nodes/trails/PresBox.tsx | 187 +++--- src/client/views/nodes/trails/PresElementBox.scss | 308 ---------- src/client/views/nodes/trails/PresElementBox.tsx | 628 --------------------- src/client/views/nodes/trails/PresSlideBox.scss | 308 ++++++++++ src/client/views/nodes/trails/PresSlideBox.tsx | 628 +++++++++++++++++++++ src/client/views/nodes/trails/index.ts | 2 +- src/client/views/pdf/GPTPopup/GPTPopup.tsx | 2 +- src/client/views/search/FaceRecognitionHandler.tsx | 4 +- src/client/views/search/SearchBox.tsx | 2 +- src/client/views/smartdraw/StickerPalette.tsx | 13 +- src/debug/Repl.tsx | 6 +- src/fields/Doc.ts | 486 ++++++++-------- src/fields/List.ts | 6 +- src/fields/Proxy.ts | 64 +-- src/fields/Schema.ts | 21 +- src/fields/ScriptField.ts | 12 +- src/fields/Types.ts | 91 +-- src/fields/util.ts | 10 +- 83 files changed, 1799 insertions(+), 1806 deletions(-) delete mode 100644 src/client/views/nodes/trails/PresElementBox.scss delete mode 100644 src/client/views/nodes/trails/PresElementBox.tsx create mode 100644 src/client/views/nodes/trails/PresSlideBox.scss create mode 100644 src/client/views/nodes/trails/PresSlideBox.tsx (limited to 'src/client/views/PropertiesView.tsx') diff --git a/src/client/documents/DocUtils.ts b/src/client/documents/DocUtils.ts index 5a8230847..7845c80aa 100644 --- a/src/client/documents/DocUtils.ts +++ b/src/client/documents/DocUtils.ts @@ -55,13 +55,13 @@ export namespace DocUtils { const matchLink = (val: string, anchor: Doc) => { const linkedToExp = (val ?? '').split('='); if (linkedToExp.length === 1) return Field.toScriptString(anchor) === val; - return Field.toScriptString(DocCast(anchor[linkedToExp[0]])) === linkedToExp[1]; + return DocCast(anchor[linkedToExp[0]]) && Field.toScriptString(DocCast(anchor[linkedToExp[0]])!) === linkedToExp[1]; }; // prettier-ignore return (value === Doc.FilterNone && !allLinks.length) || (value === Doc.FilterAny && !!allLinks.length) || - (allLinks.some(link => matchLink(value as string, DocCast(link.link_anchor_1)) || - matchLink(value as string, DocCast(link.link_anchor_2)) )); + (allLinks.some(link => (DocCast(link.link_anchor_1) && matchLink(value as string, DocCast(link.link_anchor_1)!)) || + (DocCast(link.link_anchor_2) && matchLink(value as string, DocCast(link.link_anchor_2)!)) )); } if (typeof value === 'string') { value = value.replace(`,${ClientUtils.noRecursionHack}`, ''); @@ -266,7 +266,7 @@ export namespace DocUtils { Object.keys(funcs) .filter(key => !key.endsWith('-setter')) .forEach(key => { - const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key])); + const cfield = ComputedField.DisableCompute(() => FieldValue(doc[key])); const func = funcs[key]; if (ScriptCast(cfield)?.script.originalScript !== func) { const setFunc = Cast(funcs[key + '-setter'], 'string', null); @@ -375,12 +375,13 @@ export namespace DocUtils { const documentList: ContextMenuProps[] = foo .filter(btnDoc => !btnDoc.hidden) - .map(btnDoc => Cast(btnDoc?.dragFactory, Doc, null)) + .map(btnDoc => DocCast(btnDoc?.dragFactory)) .filter(doc => doc && doc !== Doc.UserDoc().emptyTrail && doc.title) + .map(doc => doc!) .map(dragDoc => ({ description: ':' + StrCast(dragDoc.title).replace('Untitled ', ''), event: undoable(() => { - const newDoc = (Doc.isTemplateDoc(dragDoc) ? DocUtils.delegateDragFactory : DocUtils.copyDragFactory)(dragDoc); + const newDoc = (dragDoc.isTemplateDoc ? DocUtils.delegateDragFactory : DocUtils.copyDragFactory)(dragDoc); if (newDoc) { newDoc._author = ClientUtils.CurrentUserEmail(); newDoc.x = x; @@ -437,6 +438,7 @@ 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(doc => doc!) .map(dragDoc => ({ description: ':' + StrCast(dragDoc.title).replace('Untitled ', ''), event: undoable(() => { @@ -494,11 +496,11 @@ export namespace DocUtils { .map(btnDoc => (btnDoc.dragFactory as Doc) || btnDoc) .filter(d => d.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 + // first try to find a template that matches the specific document type (). otherwise, fallback to a general match on !docLayoutTemplate && allTemplates.forEach(tempDoc => { const templateType = StrCast(doc[templateName + '_fieldKey'] || doc.type); - StrCast(tempDoc.title) === templateName + '_' + templateType && (docLayoutTemplate = tempDoc); + StrCast(tempDoc.title) === templateName + (templateType[0].toUpperCase() + templateType.slice(1)) && (docLayoutTemplate = tempDoc); }); !docLayoutTemplate && allTemplates.forEach(tempDoc => { @@ -522,18 +524,13 @@ export namespace DocUtils { Doc.ApplyTemplateTo(docLayoutTemplate, doc, customName, undefined); } } else { - let fieldTemplate: Opt; - if (doc.data instanceof RichTextField || typeof doc.data === 'string') { - fieldTemplate = Docs.Create.TextDocument('', options); - } else if (doc.data instanceof PdfField) { - fieldTemplate = Docs.Create.PdfDocument('http://www.msn.com', options); - } else if (doc.data instanceof VideoField) { - fieldTemplate = Docs.Create.VideoDocument('http://www.cs.brown.edu', options); - } else if (doc.data instanceof AudioField) { - fieldTemplate = Docs.Create.AudioDocument('http://www.cs.brown.edu', options); - } else if (doc.data instanceof ImageField) { - fieldTemplate = Docs.Create.ImageDocument('http://www.cs.brown.edu', options); - } + const fieldTemplate = (() => { + if (doc.data instanceof RichTextField || typeof doc.data === 'string') return Docs.Create.TextDocument('', options); + if (doc.data instanceof PdfField) return Docs.Create.PdfDocument('http://www.msn.com', options); + if (doc.data instanceof VideoField) return Docs.Create.VideoDocument('http://www.cs.brown.edu', options); + if (doc.data instanceof AudioField) return Docs.Create.AudioDocument('http://www.cs.brown.edu', options); + if (doc.data instanceof ImageField) return Docs.Create.ImageDocument('http://www.cs.brown.edu', options); + })(); const docTemplate = creator?.(fieldTemplate ? [fieldTemplate] : [], { title: customName + '(' + doc.title + ')', isTemplateDoc: true, _width: _width + 20, _height: Math.max(100, _height + 45) }); fieldTemplate && Doc.MakeMetadataFieldTemplate(fieldTemplate, docTemplate ? Doc.GetProto(docTemplate) : docTemplate); docTemplate && Doc.ApplyTemplateTo(docTemplate, doc, customName, undefined); @@ -904,8 +901,8 @@ export namespace DocUtils { return ndoc; } - export async function Zip(doc: Doc, zipFilename = 'dashExport.zip') { - const { clone, map, linkMap } = await Doc.MakeClone(doc); + export function Zip(doc: Doc, zipFilename = 'dashExport.zip') { + const { clone, map, linkMap } = Doc.MakeClone(doc); const proms = new Set(); function replacer(key: string, value: { url: string; [key: string]: unknown }) { if (key && ['branchOf', 'cloneOf', 'cursors'].includes(key)) return undefined; diff --git a/src/client/documents/DocumentTypes.ts b/src/client/documents/DocumentTypes.ts index dd0985182..5c6559836 100644 --- a/src/client/documents/DocumentTypes.ts +++ b/src/client/documents/DocumentTypes.ts @@ -36,7 +36,7 @@ export enum DocumentType { // special purpose wrappers that either take no data or are compositions of lower level types LINK = 'link', PRES = 'presentation', - PRESELEMENT = 'preselement', + PRESSLIDE = 'presslide', COMPARISON = 'comparison', PUSHPIN = 'pushpin', MAPROUTE = 'maproute', diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index d93a432d4..bf9cc5bd4 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -217,7 +217,7 @@ export class DocumentOptions { author?: string; // STRt = new StrInfo('creator of document'); // bcz: don't change this. Otherwise, the userDoc's field Infos will have a FieldInfo assigned to its author field which will render it unreadable author_date?: DATEt = new DateInfo('date the document was created', true); annotationOn?: DOCt = new DocInfo('document annotated by this document', false); - rootDocument?: DOCt = new DocInfo('document that supplies the information needed for a rendering template (eg, pres slide for PresElement)'); + rootDocument?: DOCt = new DocInfo('document that stores the data for compound template documents.'); color?: STRt = new StrInfo('foreground color data doc', false); hidden?: BOOLt = new BoolInfo('whether the document is not rendered by its collection', false); backgroundColor?: STRt = new StrInfo('background color for data doc', false); @@ -1139,8 +1139,8 @@ export namespace Docs { return InstanceFromProto(Prototypes.get(DocumentType.FONTICON), undefined, { ...(options || {}) }); } - export function PresElementBoxDocument() { - return Prototypes.get(DocumentType.PRESELEMENT); + export function PresSlideDocument() { + return Prototypes.get(DocumentType.PRESSLIDE); } export function DataVizDocument(url: string, options?: DocumentOptions, overwriteDoc?: Doc) { @@ -1148,7 +1148,7 @@ export namespace Docs { } export function AnnoPaletteDocument(options?: DocumentOptions) { - return InstanceFromProto(Prototypes.get(DocumentType.ANNOPALETTE), new List([Doc.MyStickers]), { ...(options || {}) }); + return InstanceFromProto(Prototypes.get(DocumentType.ANNOPALETTE), new List([...(Doc.MyStickers ? [Doc.MyStickers] : [])]), { ...(options || {}) }); } export function DockDocument(documents: Array, config: string, options: DocumentOptions, id?: string) { diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 35dc5f1c7..1887c1716 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -148,12 +148,12 @@ export class CurrentUserUtils { { title: "Idea", backgroundColor: "pink", icon: "lightbulb" , _layout_showTitle: "title"}, { title: "Topic", backgroundColor: "lightblue", icon: "book-open" , _layout_showTitle: "title"}]; - + const metanote = DocCast(doc.emptyMetaNote); const reqdNoteList = [...reqdTempOpts.map(opts => { const reqdOpts = {...opts, isSystem:true, width:200, layout_autoHeight: true, layout_fitWidth: true}; const noteTemp = tempNotes ? DocListCast(tempNotes.data).find(fdoc => fdoc.title === opts.title): undefined; return DocUtils.AssignOpts(noteTemp, reqdOpts) ?? MakeTemplate(Docs.Create.TextDocument("",reqdOpts)); - }), DocCast(doc.emptyMetaNote), ... DocListCast(tempNotes?.data).filter(note => !reqdTempOpts.find(reqd => reqd.title === note.title))]; + }), ...(metanote ? [metanote]:[]), ... DocListCast(tempNotes?.data).filter(note => !reqdTempOpts.find(reqd => reqd.title === note.title))]; const reqdOpts:DocumentOptions = { title: "Note Layouts", _height: 75, isSystem: true }; return DocUtils.AssignOpts(tempNotes, reqdOpts, reqdNoteList) ?? (doc[field] = Docs.Create.TreeDocument(reqdNoteList, reqdOpts)); @@ -211,7 +211,7 @@ export class CurrentUserUtils { const fontBox = (opts:DocumentOptions, fieldKey:string) => Docs.Create.FontIconDocument({ layout:FontIconBox.LayoutString(fieldKey), _nativeHeight: 30, _nativeWidth: 30, _width: 30, _height: 30, ...opts }); const makeIconTemplate = (name: DocumentType | string | undefined, templateField: string, opts:DocumentOptions) => { - const title = "icon" + (name ? "_" + name : ""); + const title = "icon" + (name ? name[0].toUpperCase()+name.slice(1) : ""); const curIcon = DocCast(templateIconsDoc[title]); const creator = (() => { switch (opts.iconTemplate) { case DocumentType.IMG : return imageBox; @@ -424,7 +424,10 @@ pie title Minerals in my tap water const standardOps = (key:string) => ({ title : "Untitled "+ key, _layout_fitWidth: false, isSystem: true, "dragFactory_count": 0, cloneFieldFilter: new List(["isSystem"]) }); emptyThings.forEach( thing => DocUtils.AssignDocField(doc, "empty"+thing.key, (opts) => thing.creator(opts), {...standardOps(thing.key), ...thing.opts}, undefined, thing.scripts, thing.funcs)); - DocCast(Doc.UserDoc().emptyMetaNote).title = "MetaNote"; // hack: metanotes are used a template, so 'untitled metaNote' is an awkward name + const metanote = DocCast(Doc.UserDoc().emptyMetaNote); + if (metanote) { + metanote.title = "MetaNote"; // hack: metanotes are used a template, so 'untitled metaNote' is an awkward name + } return [ { toolTip: "Tap or drag to create a note", title: "Note", icon: "sticky-note", dragFactory: doc.emptyNote as Doc, clickFactory: DocCast(doc.emptyNote)}, @@ -490,7 +493,7 @@ pie title Minerals in my tap water { title: "Tools", toolTip: "Tools", target: this.setupToolsBtnPanel(doc, "myTools"), icon: "wrench", }, { title: "Imports", toolTip: "Imports ⌘I", target: this.setupImportSidebar(doc, "myImports"), icon: "upload", }, { title: "Closed", toolTip: "Recently Closed", target: this.setupRecentlyClosed(doc, "myRecentlyClosed"), icon: "archive", hidden: true }, // this doc is hidden from the Sidebar, but it's still being used in MyFilesystem which ignores the hidden field - { title: "Shared", toolTip: "Shared Docs", target: Doc.MySharedDocs, icon: "users", funcs: {badgeValue: badgeValue}}, + { title: "Shared", toolTip: "Shared Docs", target: Doc.MySharedDocs??Doc.UserDoc(), icon: "users", funcs: {badgeValue: badgeValue}}, { title: "Trails", toolTip: "Trails ⌘R", target: Doc.UserDoc(), icon: "pres-trail", funcs: {target: getActiveDashTrails}}, { title: "Image Grouper", toolTip: "Image Grouper", target: this.setupImageGrouper(doc, "myImageGrouper"), icon: "folder-open", hidden: false }, { title: "Faces", toolTip: "Unique Faces", target: this.setupFaceCollection(doc, "myFaceCollection"), icon: "face-smile", hidden: false }, @@ -551,7 +554,6 @@ 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); 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.", _layout_dontCenter: "y", @@ -975,7 +977,10 @@ pie title Minerals in my tap water }; DocUtils.AssignDocField(doc, "mySharedDocs", opts => Docs.Create.TreeDocument([], opts, sharingDocumentId + "layout", sharingDocumentId), sharedDocOpts, undefined, sharedScripts); - if (!Doc.GetProto(DocCast(doc.mySharedDocs)).data_dashboards) Doc.GetProto(DocCast(doc.mySharedDocs)).data_dashboards = new List(); + const sharedDocs = DocCast(doc.mySharedDocs); + if (sharedDocs) { + if (!Doc.GetProto(sharedDocs).data_dashboards) Doc.GetProto(sharedDocs).data_dashboards = new List(); + } } /// Import option on the left side button panel @@ -1000,9 +1005,9 @@ pie title Minerals in my tap water 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 groups = await DocListCastAsync(DocCast(doc.globalGroupDatabase)?.data); const mygroups = groups?.filter(group => JSON.parse(StrCast(group.members)).includes(ClientUtils.CurrentUserEmail())) || []; SetCachedGroups(["Guest", ...(mygroups?.map(g => StrCast(g.title))??[])]); }, { fireImmediately: true }); @@ -1058,11 +1063,13 @@ pie title Minerals in my tap water SelectionManager.DeselectAll(); // this forces SelectionManager implementation to copy over to DocumentView's API. This also triggers the LinkManager to be created - Doc.AddDocToList(Doc.MyFilesystem, undefined, Doc.MyDashboards); - Doc.AddDocToList(Doc.MyFilesystem, undefined, Doc.MySharedDocs); - Doc.AddDocToList(Doc.MyFilesystem, undefined, Doc.MyRecentlyClosed); + if (Doc.MyFilesystem) { + Doc.MyDashboards && Doc.AddDocToList(Doc.MyFilesystem, undefined, Doc.MyDashboards); + Doc.MyDashboards && Doc.AddDocToList(Doc.MyFilesystem, undefined, Doc.MyDashboards); + Doc.MyRecentlyClosed && Doc.AddDocToList(Doc.MyFilesystem, undefined, Doc.MyRecentlyClosed); + } - Doc.GetProto(DocCast(Doc.UserDoc().emptyWebpage)).data = new WebField("https://www.wikipedia.org"); + DocCast(Doc.UserDoc().emptyWebpage) && (Doc.GetProto(DocCast(Doc.UserDoc().emptyWebpage)!).data = new WebField("https://www.wikipedia.org")); DocServer.CacheNeedsUpdate() && setTimeout(UPDATE_SERVER_CACHE, 2500); setInterval(UPDATE_SERVER_CACHE, 120000); @@ -1142,7 +1149,7 @@ pie title Minerals in my tap water const file = input.files?.[0]; if (file?.type === 'application/zip' || file?.type === 'application/x-zip-compressed') { const doc = await Doc.importDocument(file); - const list = Cast(Doc.MyImports.data, listSpec(Doc), null); + 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) { const disposer = OverlayView.ShowSpinner(); @@ -1150,7 +1157,7 @@ pie title Minerals in my tap water if (results.length !== input.files?.length) { alert("Error uploading files - possibly due to unsupported file types"); } - const list = Cast(Doc.MyImports.data, listSpec(Doc), null); + const list = Cast(Doc.MyImports?.data, listSpec(Doc), null); list?.splice(0, 0, ...results); disposer(); } else { diff --git a/src/client/util/DictationManager.ts b/src/client/util/DictationManager.ts index 2eef3da0e..dcef4a4fe 100644 --- a/src/client/util/DictationManager.ts +++ b/src/client/util/DictationManager.ts @@ -364,7 +364,7 @@ export namespace DictationManager { case 'nested collection':return Docs.Create.FreeformDocument([], {}); } // prettier-ignore })(); - created && Doc.AddDocToList(target.dataDoc, Doc.LayoutFieldKey(target.Document), created); + created && Doc.AddDocToList(target.dataDoc, Doc.LayoutDataKey(target.Document), created); } } }, diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 5ce005811..ad57c2a62 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -183,7 +183,7 @@ export class DocumentManager { static _howl: Howl; static playAudioAnno(doc: Doc) { - const anno = Cast(doc[Doc.LayoutFieldKey(doc) + '_audioAnnotations'], listSpec(AudioField), null)?.lastElement(); + const anno = Cast(doc[Doc.LayoutDataKey(doc) + '_audioAnnotations'], listSpec(AudioField), null)?.lastElement(); if (anno) { this._howl?.stop(); if (anno instanceof AudioField) { diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index e2e4c0fe4..a66e6998b 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -234,29 +234,27 @@ export namespace DragManager { dropDoc instanceof Doc && CreateLinkToActiveAudio(() => dropDoc); return dropDoc; }; - const finishDrag = async (e: DragCompleteEvent) => { + const finishDrag = (e: DragCompleteEvent) => { const { docDragData } = e; setTimeout(() => dragData.dragEnding?.()); onDropCompleted?.(e); // glr: optional additional function to be called - in this case with presentation trails if (docDragData && !docDragData.droppedDocuments.length) { docDragData.dropAction = dragData.userDropAction || dragData.dropAction; - docDragData.droppedDocuments = ( - await Promise.all( - dragData.draggedDocuments.map(async d => - !dragData.isDocDecorationMove && !dragData.userDropAction && ScriptCast(d.onDragStart) - ? addAudioTag(ScriptCast(d.onDragStart).script.run({ this: d }).result as Doc) - : docDragData.dropAction === dropActionType.embed - ? Doc.BestEmbedding(d) - : docDragData.dropAction === dropActionType.add - ? d - : docDragData.dropAction === dropActionType.proto - ? d[DocData] - : docDragData.dropAction === dropActionType.copy - ? (await Doc.MakeClone(d)).clone - : d - ) + docDragData.droppedDocuments = dragData.draggedDocuments + .map(d => + !dragData.isDocDecorationMove && !dragData.userDropAction && ScriptCast(d.onDragStart) + ? addAudioTag(ScriptCast(d.onDragStart)!.script.run({ this: d }).result as Doc) + : docDragData.dropAction === dropActionType.embed + ? Doc.BestEmbedding(d) + : docDragData.dropAction === dropActionType.add + ? d + : docDragData.dropAction === dropActionType.proto + ? d[DocData] + : docDragData.dropAction === dropActionType.copy + ? Doc.MakeClone(d).clone + : d ) - ).filter(d => d); + .filter(d => d); ![dropActionType.same, dropActionType.proto].includes(StrCast(docDragData.dropAction) as dropActionType) && docDragData.droppedDocuments // .filter(drop => !drop.dragOnlyWithinContainer || ['embed', 'copy'].includes(docDragData.dropAction as any)) @@ -364,7 +362,7 @@ export namespace DragManager { }; } - async function dispatchDrag(target: Element, e: PointerEvent, complete: DragCompleteEvent, pos: { x: number; y: number }, finishDrag?: (e: DragCompleteEvent) => void, options?: DragOptions, endDrag?: () => void) { + 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, @@ -380,7 +378,7 @@ export namespace DragManager { }; 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); + 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?.(); @@ -566,11 +564,11 @@ export namespace DragManager { const targClassName = e.target instanceof HTMLElement && typeof e.target.className === 'string' ? e.target.className : ''; if (['lm_tab', 'lm_title_wrap', 'lm_tabs', 'lm_header'].includes(targClassName) && docDragData.draggedDocuments.length === 1) { if (!startWindowDragTimer) { - startWindowDragTimer = setTimeout(async () => { + startWindowDragTimer = setTimeout(() => { startWindowDragTimer = undefined; docDragData.dropAction = docDragData.userDropAction || dropActionType.same; AbortDrag(); - await finishDrag?.(new DragCompleteEvent(true, docDragData)); + finishDrag?.(new DragCompleteEvent(true, docDragData)); DragManager.StartWindowDrag?.(e, docDragData.droppedDocuments, aborted => { if (!aborted && (docDragData?.dropAction === dropActionType.move || docDragData?.dropAction === dropActionType.same)) { docDragData.removeDocument?.(docDragData?.draggedDocuments[0]); diff --git a/src/client/util/DropConverter.ts b/src/client/util/DropConverter.ts index 7d3f63448..b6b111930 100644 --- a/src/client/util/DropConverter.ts +++ b/src/client/util/DropConverter.ts @@ -3,7 +3,7 @@ import { DocData, DocLayout } from '../../fields/DocSymbols'; import { ObjectField } from '../../fields/ObjectField'; import { RichTextField } from '../../fields/RichTextField'; import { ComputedField, ScriptField } from '../../fields/ScriptField'; -import { StrCast } from '../../fields/Types'; +import { DocCast, StrCast } from '../../fields/Types'; import { ImageField } from '../../fields/URLField'; import { Docs, DocumentOptions } from '../documents/Documents'; import { DocumentType } from '../documents/DocumentTypes'; @@ -23,7 +23,7 @@ import { ScriptingGlobals } from './ScriptingGlobals'; */ function makeTemplate(doc: Doc, first: boolean = true): boolean { const layoutDoc = doc.layout instanceof Doc && doc.layout.isTemplateForField ? doc.layout : doc; - if (layoutDoc.layout instanceof Doc) { + if (DocCast(layoutDoc.layout)) { return true; // its already a template } const layout = StrCast(layoutDoc.layout).match(/fieldKey={'[^']*'}/)?.[0]; @@ -68,7 +68,7 @@ export function MakeTemplate(doc: Doc) { * Makes a draggable button or image that will create a template doc Instance */ export function makeUserTemplateButtonOrImage(doc: Doc, image?: string) { - const layoutDoc = doc; // doc.layout instanceof Doc && doc.layout.isTemplateForField ? doc.layout : doc; + const layoutDoc = doc; if (layoutDoc.type !== DocumentType.FONTICON) { !layoutDoc.isTemplateDoc && makeTemplate(layoutDoc); } diff --git a/src/client/util/Import & Export/ImageUtils.ts b/src/client/util/Import & Export/ImageUtils.ts index 9c32ca25a..eae3460b3 100644 --- a/src/client/util/Import & Export/ImageUtils.ts +++ b/src/client/util/Import & Export/ImageUtils.ts @@ -15,7 +15,7 @@ export namespace ImageUtils { export const AssignImgInfo = (document: Doc, data?: Upload.InspectionResults) => { if (data) { data.nativeWidth && (document._height = (NumCast(document._width) * data.nativeHeight) / data.nativeWidth); - const field = '$' + Doc.LayoutFieldKey(document); + const field = '$' + Doc.LayoutDataKey(document); document[`${field}_nativeWidth`] = data.nativeWidth; document[`${field}_nativeHeight`] = data.nativeHeight; document[`${field}_path`] = data.source; diff --git a/src/client/util/LinkFollower.ts b/src/client/util/LinkFollower.ts index 0e67dcfaa..6081c3fae 100644 --- a/src/client/util/LinkFollower.ts +++ b/src/client/util/LinkFollower.ts @@ -43,13 +43,13 @@ export class LinkFollower { }; public static traverseLink(link: Opt, sourceDoc: Doc, finished?: () => void, traverseBacklink?: boolean) { - const getView = (doc: Doc) => DocumentView.getFirstDocumentView(DocCast(doc.layout_unrendered ? doc.annotationOn : doc)); - const isAnchor = (source: Doc, anchor: Doc) => Doc.AreProtosEqual(anchor, source) || Doc.AreProtosEqual(anchor.annotationOn as Doc, source); + const getView = (doc: Doc) => DocumentView.getFirstDocumentView(DocCast(doc.layout_unrendered ? doc.annotationOn : doc)!); + const isAnchor = (source?: Doc, anchor?: Doc) => Doc.AreProtosEqual(anchor, source) || Doc.AreProtosEqual(DocCast(anchor?.annotationOn), source); const linkDocs = link ? [link] : Doc.Links(sourceDoc); - const fwdLinks = linkDocs.filter(l => isAnchor(sourceDoc, l.link_anchor_1 as Doc)); // link docs where 'sourceDoc' is link_anchor_1 - const backLinks = linkDocs.filter(l => isAnchor(sourceDoc, l.link_anchor_2 as Doc)); // link docs where 'sourceDoc' is link_anchor_2 - const fwdLinkWithoutTargetView = fwdLinks.find(l => !getView(DocCast(l.link_anchor_2))); - const backLinkWithoutTargetView = backLinks.find(l => !getView(DocCast(l.link_anchor_1))); + const fwdLinks = linkDocs.filter(l => isAnchor(sourceDoc, DocCast(l.link_anchor_1))); // link docs where 'sourceDoc' is link_anchor_1 + const backLinks = linkDocs.filter(l => isAnchor(sourceDoc, DocCast(l.link_anchor_2))); // link docs where 'sourceDoc' is link_anchor_2 + const fwdLinkWithoutTargetView = fwdLinks.find(l => !DocCast(l.link_anchor_2) || !getView(DocCast(l.link_anchor_2)!)); + const backLinkWithoutTargetView = backLinks.find(l => !DocCast(l.link_anchor_1) || !getView(DocCast(l.link_anchor_1)!)); const linkWithoutTargetDoc = traverseBacklink === undefined ? (fwdLinkWithoutTargetView ?? backLinkWithoutTargetView) : traverseBacklink ? backLinkWithoutTargetView : fwdLinkWithoutTargetView; const linkDocList = linkWithoutTargetDoc && !sourceDoc.followAllLinks ? [linkWithoutTargetDoc] : traverseBacklink === undefined ? fwdLinks.concat(backLinks) : traverseBacklink ? backLinks : fwdLinks; const followLinks = sourceDoc.followLinkToggle || sourceDoc.followAllLinks ? linkDocList : linkDocList.slice(0, 1); @@ -60,17 +60,17 @@ export class LinkFollower { return false; } followLinks.forEach(async linkDoc => { - const target = ( + const target = DocCast( sourceDoc === linkDoc.link_anchor_1 ? linkDoc.link_anchor_2 : sourceDoc === linkDoc.link_anchor_2 ? linkDoc.link_anchor_1 - : Doc.AreProtosEqual(sourceDoc, linkDoc.link_anchor_1 as Doc) || Doc.AreProtosEqual((linkDoc.link_anchor_1 as Doc).annotationOn as Doc, sourceDoc) + : Doc.AreProtosEqual(sourceDoc, DocCast(linkDoc.link_anchor_1)) || Doc.AreProtosEqual(DocCast(DocCast(linkDoc.link_anchor_1)?.annotationOn), sourceDoc) ? linkDoc.link_anchor_2 : linkDoc.link_anchor_1 - ) as Doc; - const srcAnchor: Doc = Doc.getOppositeAnchor(linkDoc, target) ?? sourceDoc; + ); if (target) { + const srcAnchor = Doc.getOppositeAnchor(linkDoc, target) ?? sourceDoc; const doFollow = (canToggle?: boolean) => { const toggleTarget = canToggle && BoolCast(sourceDoc.followLinkToggle); const options: FocusViewOptions = { @@ -102,14 +102,16 @@ export class LinkFollower { if (srcAnchor.followLinkLocation === OpenWhere.inParent) { const sourceDocParent = DocCast(sourceDoc.embedContainer); if (target.embedContainer instanceof Doc && target.embedContainer !== sourceDocParent) { - Doc.RemoveDocFromList(target.embedContainer, Doc.LayoutFieldKey(target.embedContainer), target); + Doc.RemoveDocFromList(target.embedContainer, Doc.LayoutDataKey(target.embedContainer), target); movedTarget = true; } - if (!DocListCast(sourceDocParent[Doc.LayoutFieldKey(sourceDocParent)]).includes(target)) { - Doc.AddDocToList(sourceDocParent, Doc.LayoutFieldKey(sourceDocParent), target); - movedTarget = true; + if (sourceDocParent) { + if (!DocListCast(sourceDocParent[Doc.LayoutDataKey(sourceDocParent)]).includes(target)) { + Doc.AddDocToList(sourceDocParent, Doc.LayoutDataKey(sourceDocParent), target); + movedTarget = true; + } + Doc.SetContainer(target, sourceDocParent); } - Doc.SetContainer(target, sourceDocParent); } const moveTo = [NumCast(sourceDoc.x) + NumCast(sourceDoc.followLinkXoffset), NumCast(sourceDoc.y) + NumCast(sourceDoc.followLinkYoffset)]; if (srcAnchor.followLinkXoffset !== undefined && moveTo[0] !== target.x) { diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index 3f98ab3c4..344e2e4c0 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -136,7 +136,7 @@ export class LinkManager { true ); FieldLoader.ServerLoadStatus.message = 'links'; - this.addLinkDB(Doc.LinkDBDoc()); + Doc.LinkDBDoc() && this.addLinkDB(Doc.LinkDBDoc()!); } public createlink_relationshipLists = () => { @@ -148,16 +148,21 @@ export class LinkManager { public addLink(linkDoc: Doc, checkExists = false) { Doc.AddDocToList(Doc.UserDoc(), 'links', linkDoc); - if (!checkExists || !DocListCast(Doc.LinkDBDoc().data).includes(linkDoc)) { - Doc.AddDocToList(Doc.LinkDBDoc(), 'data', linkDoc); - // eslint-disable-next-line no-use-before-define - setTimeout(UPDATE_SERVER_CACHE, 100); + if (Doc.LinkDBDoc()) { + if (!checkExists || !DocListCast(Doc.LinkDBDoc()!.data).includes(linkDoc)) { + Doc.AddDocToList(Doc.LinkDBDoc()!, 'data', linkDoc); + // eslint-disable-next-line no-use-before-define + setTimeout(UPDATE_SERVER_CACHE, 100); + } } } public deleteLink(linkDoc: Doc) { - const ret = Doc.RemoveDocFromList(Doc.LinkDBDoc(), 'data', linkDoc); - linkDoc.$link_anchor_1 = linkDoc.$link_anchor_2 = undefined; - return ret; + if (Doc.LinkDBDoc()) { + const ret = Doc.RemoveDocFromList(Doc.LinkDBDoc()!, 'data', linkDoc); + linkDoc.$link_anchor_1 = linkDoc.$link_anchor_2 = undefined; + return ret; + } + return false; } public deleteAllLinksOnAnchor(anchor: Doc) { LinkManager.Instance.relatedLinker(anchor).forEach(LinkManager.Instance.deleteLink); @@ -178,8 +183,8 @@ export class LinkManager { } const dirLinks = Array.from(anchor[DocData][DirectLinks]).filter(l => Doc.GetProto(anchor) === anchor[DocData] || ['1', '2'].includes(LinkManager.anchorIndex(l, anchor) as '0' | '1' | '2')); - 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']); + 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.LayoutDataKey(anchor) + '_annotations']); return Array.from( annos.reduce((set, anno) => { if (!processed.includes(anno)) { @@ -242,11 +247,13 @@ export function UPDATE_SERVER_CACHE() { .filter(doc => doc instanceof Doc) .map(doc => doc as Doc); const references = new Set(prototypes); + DocCast(Doc.UserDoc().myLinkDatabase) && references.add(DocCast(Doc.UserDoc().myLinkDatabase)!); // prevent crawling through link database here -- see below 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); + + DocListCast(DocCast(Doc.UserDoc().myLinkDatabase)?.data).forEach(link => { + if (DocCast(link.link_anchor_1) && !references.has(DocCast(link.link_anchor_1)!) && DocCast(link.link_anchor_2) && !references.has(DocCast(link.link_anchor_2)!)) { + DocCast(Doc.UserDoc().myLinkDatabase) && Doc.RemoveDocFromList(DocCast(Doc.UserDoc().myLinkDatabase)!, 'data', link); + Doc.MyRecentlyClosed && Doc.AddDocToList(Doc.MyRecentlyClosed, undefined, link); } }); LinkManager.Instance.userLinkDBs.forEach(linkDb => Doc.FindReferences(linkDb, references, undefined)); @@ -257,10 +264,10 @@ export function UPDATE_SERVER_CACHE() { 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)); + 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: { diff --git a/src/client/util/SearchUtil.ts b/src/client/util/SearchUtil.ts index 733eae5f4..e4adcaa7e 100644 --- a/src/client/util/SearchUtil.ts +++ b/src/client/util/SearchUtil.ts @@ -9,7 +9,7 @@ export namespace SearchUtil { export type HighlightingResult = { [id: string]: { [key: string]: string[] } }; export function SearchCollection(collectionDoc: Opt, queryIn: string, matchKeyNames: boolean, onlyKeys?: string[]) { - const blockedTypes = [DocumentType.PRESELEMENT, DocumentType.CONFIG, DocumentType.KVP, DocumentType.FONTICON, DocumentType.BUTTON, DocumentType.SCRIPTING]; + const blockedTypes = [DocumentType.PRESSLIDE, DocumentType.CONFIG, DocumentType.KVP, DocumentType.FONTICON, DocumentType.BUTTON, DocumentType.SCRIPTING]; const blockedKeys = matchKeyNames ? [] : Object.entries(DocOptions) @@ -21,7 +21,7 @@ export namespace SearchUtil { const results = new ObservableMap(); if (collectionDoc) { - const docs = DocListCast(collectionDoc[Doc.LayoutFieldKey(collectionDoc)]); + const docs = DocListCast(collectionDoc[Doc.LayoutDataKey(collectionDoc)]); // eslint-disable-next-line @typescript-eslint/ban-types const docIDs: String[] = []; SearchUtil.foreachRecursiveDoc(docs, (depth: number, doc: Doc) => { @@ -77,7 +77,7 @@ export namespace SearchUtil { // eslint-disable-next-line no-loop-func docs.filter(d => d && !visited.includes(d)).forEach(d => { visited.push(d); - const fieldKey = Doc.LayoutFieldKey(d); + const fieldKey = Doc.LayoutDataKey(d); const annos = !Field.toString(Doc.LayoutField(d) as FieldType).includes('CollectionView'); const data = d[annos ? fieldKey + '_annotations' : fieldKey]; data && newarray.push(...DocListCast(data)); diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx index 88f1f3260..9e79fd870 100644 --- a/src/client/util/SettingsManager.tsx +++ b/src/client/util/SettingsManager.tsx @@ -578,7 +578,7 @@ export class SettingsManager extends React.Component {
{ClientUtils.CurrentUserEmail()}
-