From e973de1476194fe68a2f218e458e4619fb71c75e Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 29 Feb 2024 09:34:56 -0500 Subject: fixed minimap to show at least as much as the freeformview is showing (was a problem for example when there was just a single document) --- .../views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 50b3dbd70..e48656f5e 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -159,8 +159,8 @@ export class CollectionFreeFormView extends CollectionSubView e.bounds?.width && !e.bounds.z).map(e => e.bounds!), - NumCast(this.layoutDoc._xPadding, 10), - NumCast(this.layoutDoc._yPadding, 10) + NumCast(this.layoutDoc._xPadding, this._props.xPadding ?? 10), + NumCast(this.layoutDoc._yPadding, this._props.yPadding ?? 10) ); } @computed get nativeWidth() { -- cgit v1.2.3-70-g09d2 From 4e837a73f5fae06368416f99c047d78f6b94565b Mon Sep 17 00:00:00 2001 From: newalina <157907056+newalina@users.noreply.github.com> Date: Thu, 29 Feb 2024 23:16:33 -0500 Subject: add state message gifs --- .../CollectionFreeFormInfoState.tsx | 20 ++-- .../CollectionFreeFormInfoUI.tsx | 117 +++++++++++++-------- src/server/public/assets/dash-colon-menu.gif | Bin 0 -> 348794 bytes .../public/assets/dash-create-link-board.gif | Bin 0 -> 167854 bytes src/server/public/assets/dash-following-link.gif | Bin 0 -> 181851 bytes 5 files changed, 83 insertions(+), 54 deletions(-) create mode 100644 src/server/public/assets/dash-colon-menu.gif create mode 100644 src/server/public/assets/dash-create-link-board.gif create mode 100644 src/server/public/assets/dash-following-link.gif (limited to 'src/client/views/collections/collectionFreeForm') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx index 58f6b1593..688f84cd5 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx @@ -5,7 +5,6 @@ import * as React from 'react'; import { SettingsManager } from '../../../util/SettingsManager'; import { ObservableReactComponent } from '../../ObservableReactComponent'; import './CollectionFreeFormView.scss'; -// import assets from './assets/link.png'; /** * An Fsa Arc. The first array element is a test condition function that will be observed. @@ -15,18 +14,18 @@ import './CollectionFreeFormView.scss'; export type infoArc = [() => any, (res?: any) => infoState]; export const StateMessage = Symbol('StateMessage'); -export const StateEntryFunc = Symbol('StateEntryFunc'); export const StateMessageGIF = Symbol('StateMessageGIF'); +export const StateEntryFunc = Symbol('StateEntryFunc'); export class infoState { [StateMessage]: string = ''; - [StateEntryFunc]?: () => any; - [StateMessageGIF]?: string = ''; [key: string]: infoArc; - constructor(message: string, arcs: { [key: string]: infoArc }, entryFunc?: () => any, messageGif?: string) { + [StateMessageGIF]?: string = ''; + [StateEntryFunc]?: () => any; + constructor(message: string, arcs: { [key: string]: infoArc }, messageGif?: string, entryFunc?: () => any) { this[StateMessage] = message; Object.assign(this, arcs); - this[StateEntryFunc] = entryFunc; this[StateMessageGIF] = messageGif; + this[StateEntryFunc] = entryFunc; } } @@ -36,16 +35,17 @@ export class infoState { * @param arcs an object with fields containing @infoArcs (an object with field names indicating the arc transition and * field values being a tuple of an arc transition trigger function (that returns a truthy value when the arc should fire), * and an arc transition action function (that sets the next state) + * @param gif the gif displayed when in this state * @param entryFunc a function to call when entering the state * @returns an FSA state */ export function InfoState( msg: string, // arcs: { [key: string]: infoArc }, - entryFunc?: () => any, - gif?: string + gif?: string, + entryFunc?: () => any ) { - return new infoState(msg, arcs, entryFunc, gif); + return new infoState(msg, arcs, gif, entryFunc); } export interface CollectionFreeFormInfoStateProps { @@ -103,7 +103,7 @@ export class CollectionFreeFormInfoState extends ObservableReactComponent
{this.State?.[StateMessage]}
- state message gif + state message gif
this.props.close())} /> diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx index 8628ca3c3..cd76003c4 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx @@ -11,6 +11,7 @@ import { DocButtonState, DocumentLinksButton } from '../../nodes/DocumentLinksBu import { CollectionFreeFormInfoState, InfoState, StateEntryFunc, infoState } from './CollectionFreeFormInfoState'; import { CollectionFreeFormView } from './CollectionFreeFormView'; import './CollectionFreeFormView.scss'; +import { TopBar } from '../../topbar/TopBar'; export interface CollectionFreeFormInfoUIProps { Document: Doc; @@ -75,72 +76,98 @@ export class CollectionFreeFormInfoUI extends ObservableReactComponent numDocs(), () => { - docX = firstDoc()?.x; - docY = firstDoc()?.y; - return oneDoc; - }], - }, setBackground("blue")); // prettier-ignore + docCreated: [() => numDocs(), () => { + docX = firstDoc()?.x; + docY = firstDoc()?.y; + return oneDoc; + }], + }); // prettier-ignore const oneDoc = InfoState('Hello world! You can drag and drop to move your document around.', { - // docCreated: [() => numDocs() > 1, () => multipleDocs], - docDeleted: [() => numDocs() < 1, () => start], - docMoved: [() => (docX && docX != docNewX()) || (docY && docY != docNewY()), () => { - docX = firstDoc()?.x; - docY = firstDoc()?.y; - return movedDoc1; - }], - }, setBackground("red")); // prettier-ignore + // docCreated: [() => numDocs() > 1, () => multipleDocs], + docDeleted: [() => numDocs() < 1, () => start], + docMoved: [() => (docX && docX != docNewX()) || (docY && docY != docNewY()), () => { + docX = firstDoc()?.x; + docY = firstDoc()?.y; + return movedDoc1; + }], + }); // prettier-ignore - const movedDoc1 = InfoState('Great moves. Try creating a second document.', { + const movedDoc1 = InfoState( + 'Great moves. Try creating a second document. You can see the list of supported document types by typing \":\".', + { docCreated: [() => numDocs() == 2, () => multipleDocs], docDeleted: [() => numDocs() < 1, () => start], docMoved: [() => (docX && docX != docNewX()) || (docY && docY != docNewY()), () => { - docX = firstDoc()?.x; - docY = firstDoc()?.y; - return movedDoc2; - }], - }, setBackground("yellow")); // prettier-ignore + docX = firstDoc()?.x; + docY = firstDoc()?.y; + return movedDoc2; + }], + }, + 'dash-colon-menu.gif' + ); // prettier-ignore - const movedDoc2 = InfoState('Slick moves. Try creating a second document.', { + const movedDoc2 = InfoState( + 'Slick moves. Try creating a second document. You can see the list of supported document types by typing \":\".', + { docCreated: [() => numDocs() == 2, () => multipleDocs], docDeleted: [() => numDocs() < 1, () => start], docMoved: [() => (docX && docX != docNewX()) || (docY && docY != docNewY()), () => { - docX = firstDoc()?.x; - docY = firstDoc()?.y; - return movedDoc3; - }], - }, setBackground("pink")); // prettier-ignore + docX = firstDoc()?.x; + docY = firstDoc()?.y; + return movedDoc3; + }], + }, + 'dash-colon-menu.gif' + ); // prettier-ignore - const movedDoc3 = InfoState('Groovy moves. Try creating a second document.', { + const movedDoc3 = InfoState( + 'Groovy moves. Try creating a second document. You can see the list of supported document types by typing \":\".', + { docCreated: [() => numDocs() == 2, () => multipleDocs], docDeleted: [() => numDocs() < 1, () => start], docMoved: [() => (docX && docX != docNewX()) || (docY && docY != docNewY()), () => { - docX = firstDoc()?.x; - docY = firstDoc()?.y; - return movedDoc1; - }], - }, setBackground("green")); // prettier-ignore + docX = firstDoc()?.x; + docY = firstDoc()?.y; + return movedDoc1; + }], + }, + 'dash-colon-menu.gif' + ); // prettier-ignore - const multipleDocs = InfoState('Let\'s create a new link. Click the link icon on one of your documents.', { + const multipleDocs = InfoState( + 'Let\'s create a new link. Click the link icon on one of your documents.', + { linkStarted: [() => linkStart(), () => startedLink], docRemoved: [() => numDocs() < 2, () => oneDoc], - }, setBackground("purple")); // prettier-ignore + }, + 'dash-create-link-board.gif' + ); // prettier-ignore - const startedLink = InfoState('Now click the highlighted link icon on your other document.', { + const startedLink = InfoState( + 'Now click the highlighted link icon on your other document.', + { linkCreated: [() => numDocLinks(), () => madeLink], docRemoved: [() => numDocs() < 2, () => oneDoc], - }, setBackground("orange")); // prettier-ignore + }, + 'dash-create-link-board.gif' + ); // prettier-ignore - const madeLink = InfoState('You made your first link! You can view your links by selecting the blue dot.', { + const madeLink = InfoState( + 'You made your first link! You can view your links by selecting the blue dot.', + { linkCreated: [() => !numDocLinks(), () => multipleDocs], linkViewed: [() => linkMenuOpen(), () => { - alert(numDocLinks() + " cheer for " + numDocLinks() + " link!"); - return viewedLink; + alert(numDocLinks() + " cheer for " + numDocLinks() + " link!"); + return viewedLink; }], - }, setBackground("blue")); // prettier-ignore + }, + 'dash-following-link.gif' + ); // prettier-ignore - const viewedLink = InfoState('Great work. You are now ready to create your own hypermedia world.', { + const viewedLink = InfoState( + 'Great work. You are now ready to create your own hypermedia world. Click the blinking question mark to learn more.', + { linkDeleted: [() => !numDocLinks(), () => multipleDocs], docRemoved: [() => numDocs() < 2, () => oneDoc], docCreated: [() => numDocs() == 3, () => { @@ -148,7 +175,10 @@ export class CollectionFreeFormInfoUI extends ObservableReactComponent activeTool() === InkTool.Pen, () => penMode], - }, setBackground("black")); // prettier-ignore + }, + 'documentation.png', + + ); // prettier-ignore const presentDocs = InfoState( 'Another document! You could make a presentation. Click the pin icon in the top left corner.', @@ -162,7 +192,6 @@ export class CollectionFreeFormInfoUI extends ObservableReactComponent numDocs() < 3, () => viewedLink], }, - setBackground('black'), '/assets/dash-pin-with-view.gif' ); @@ -244,7 +273,7 @@ export class CollectionFreeFormInfoUI extends ObservableReactComponent numDocs() == 1, () => oneDoc], - }, setBackground("white")); // prettier-ignore + }); // prettier-ignore return start; }; diff --git a/src/server/public/assets/dash-colon-menu.gif b/src/server/public/assets/dash-colon-menu.gif new file mode 100644 index 000000000..b5512afb1 Binary files /dev/null and b/src/server/public/assets/dash-colon-menu.gif differ diff --git a/src/server/public/assets/dash-create-link-board.gif b/src/server/public/assets/dash-create-link-board.gif new file mode 100644 index 000000000..354188fd9 Binary files /dev/null and b/src/server/public/assets/dash-create-link-board.gif differ diff --git a/src/server/public/assets/dash-following-link.gif b/src/server/public/assets/dash-following-link.gif new file mode 100644 index 000000000..9e3e6df82 Binary files /dev/null and b/src/server/public/assets/dash-following-link.gif differ -- cgit v1.2.3-70-g09d2 From 1a4a5d56a5a9962d75b625bf784420d9f8aedb4e Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 1 Mar 2024 08:31:25 -0500 Subject: added flipping of documentation icon --- .../views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx | 1 + 1 file changed, 1 insertion(+) (limited to 'src/client/views/collections/collectionFreeForm') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx index cd76003c4..a81195c95 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx @@ -42,6 +42,7 @@ export class CollectionFreeFormInfoUI extends ObservableReactComponent Date: Mon, 4 Mar 2024 12:55:05 -0500 Subject: changes to restore template menu and fix freeform templates to always show contents. Fix for alt-dropping an image to replace another image. --- src/client/views/DocumentButtonBar.tsx | 48 +++++++++------------- src/client/views/DocumentDecorations.tsx | 4 +- src/client/views/InkingStroke.tsx | 2 +- src/client/views/TemplateMenu.tsx | 2 +- src/client/views/collections/TabDocView.tsx | 2 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 9 ++-- src/client/views/nodes/ImageBox.tsx | 1 + 7 files changed, 30 insertions(+), 38 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm') diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx index 8a4b42ae0..d65e0b406 100644 --- a/src/client/views/DocumentButtonBar.tsx +++ b/src/client/views/DocumentButtonBar.tsx @@ -4,12 +4,12 @@ import { Tooltip } from '@mui/material'; import { action, computed, makeObservable, observable, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; -import { emptyFunction, returnFalse, setupMoveUpEvents, simulateMouseClick } from '../../Utils'; +import { emptyFunction, returnFalse, returnTrue, setupMoveUpEvents, simulateMouseClick } from '../../Utils'; import { Doc } from '../../fields/Doc'; import { Cast, DocCast } from '../../fields/Types'; import { DocUtils } from '../documents/Documents'; import { CalendarManager } from '../util/CalendarManager'; -import { DragManager } from '../util/DragManager'; +import { DragManager, dropActionType } from '../util/DragManager'; import { IsFollowLinkScript } from '../util/LinkFollower'; import { SelectionManager } from '../util/SelectionManager'; import { SharingManager } from '../util/SharingManager'; @@ -24,6 +24,9 @@ import { DocumentView, DocumentViewInternal, OpenWhere } from './nodes/DocumentV import { DashFieldView } from './nodes/formattedText/DashFieldView'; import { PinProps } from './nodes/trails'; import { faCalendarDays } from '@fortawesome/free-solid-svg-icons'; +import { Popup } from 'browndash-components'; +import { TemplateMenu } from './TemplateMenu'; +import { FaEdit } from 'react-icons/fa'; @observer export class DocumentButtonBar extends ObservableReactComponent<{ views: () => (DocumentView | undefined)[]; stack?: any }> { @@ -306,7 +309,7 @@ export class DocumentButtonBar extends ObservableReactComponent<{ views: () => ( const dragDocView = this.view0!; const dragData = new DragManager.DocumentDragData([dragDocView.Document]); const [left, top] = dragDocView.screenToContentsTransform().inverse().transformPoint(0, 0); - dragData.defaultDropAction = 'embed'; + dragData.defaultDropAction = dropActionType.embed; dragData.canEmbed = true; DragManager.StartDocumentDrag([dragDocView.ContentDiv!], dragData, left, top, { hideSource: false }); return true; @@ -316,35 +319,24 @@ export class DocumentButtonBar extends ObservableReactComponent<{ views: () => ( _ref = React.createRef(); @observable _tooltipOpen: boolean = false; + @computed get templateMenu() { + return ( +
+ v) + .map(v => v as DocumentView)} + /> +
+ ); + } @computed get templateButton() { - const view0 = this.view0; - const views = this._props.views(); - return !view0 ? null : ( + return !this.view0 ? null : ( Tap to Customize Layout. Drag an embedding
} open={this._tooltipOpen} onClose={action(() => (this._tooltipOpen = false))} placement="bottom">
!this._ref.current?.getBoundingClientRect().width && (this._tooltipOpen = true))}> - { - /* (this._embedDown = true))} - onClose={action(() => (this._embedDown = false))} - content={ - !this._embedDown ? null : ( -
- {' '} - v).map(v => v as DocumentView)} /> -
- ) - }> -
- -
-
*/ - -
- -
- } + } popup={this.templateMenu} popupContainsPt={returnTrue} />
); diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index e9c4d9cc5..9d53c58c0 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -77,7 +77,7 @@ export class DocumentDecorations extends ObservableReactComponent center.x+x || this.Bounds.r < center.x+x || this.Bounds.y > center.y+y || this.Bounds.b < center.y+y ))); })); // prettier-ignore @@ -514,7 +514,7 @@ export class DocumentDecorations extends ObservableReactComponent (doc.isGroup ? DocListCast(doc.data).some(this.hasFixedAspect) : !BoolCast(doc.layout_nativeDimEditable)); + hasFixedAspect = (doc: Doc): boolean => (doc.isGroup ? DocListCast(doc.data).some(this.hasFixedAspect) : !BoolCast(doc._layout_nativeDimEditable)); // // resize a single DocumentView about the specified reference point, possibly setting/updating the native dimensions of the Doc diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index 92644d3c5..122e5c4c3 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -438,7 +438,7 @@ export class InkingStroke extends ViewBoxBaseComponent() impleme { componentDidMount() { !this._addedKeys && (this._addedKeys = new ObservableSet()); [...Array.from(Object.keys(this.props.docViews[0].Document[DocData])), ...Array.from(Object.keys(this.props.docViews[0].Document))] - .filter(key => key.startsWith('layout_')) + .filter(key => key.startsWith('layout_') && key !== 'layout_fieldKey') .map(key => runInAction(() => this._addedKeys.add(key.replace('layout_', '')))); } diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index b52f2e7b6..699ec3b95 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -638,7 +638,7 @@ export class TabMinimapView extends ObservableReactComponent - } color={SettingsManager.userVariantColor} type={Type.TERT} onPointerDown={e => e.stopPropagation()} placement={'top-end'} popup={this.popup} /> + } color={SettingsManager.userVariantColor} type={Type.TERT} onPointerDown={e => e.stopPropagation()} placement="top-end" popup={this.popup} /> ); } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index e48656f5e..877569ab3 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -17,7 +17,7 @@ import { BoolCast, Cast, DocCast, NumCast, ScriptCast, StrCast } from '../../../ import { ImageField } from '../../../../fields/URLField'; import { TraceMobx } from '../../../../fields/util'; import { GestureUtils } from '../../../../pen-gestures/GestureUtils'; -import { aggregateBounds, DashColor, emptyFunction, intersectRect, lightOrDark, OmitKeys, returnFalse, returnZero, setupMoveUpEvents, Utils } from '../../../../Utils'; +import { aggregateBounds, DashColor, emptyFunction, intersectRect, lightOrDark, numberValue, OmitKeys, returnFalse, returnZero, setupMoveUpEvents, Utils } from '../../../../Utils'; import { CognitiveServices } from '../../../cognitive_services/CognitiveServices'; import { Docs, DocUtils } from '../../../documents/Documents'; import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes'; @@ -760,7 +760,6 @@ export class CollectionFreeFormView extends CollectionSubView { if (this.tryDragCluster(e, this._hitCluster)) { e.stopPropagation(); // we're moving a cluster, so stop propagation and return true to end panning and let the document drag take over @@ -1344,7 +1343,7 @@ export class CollectionFreeFormView extends CollectionSubView { if (!LightboxView.LightboxDoc || LightboxView.Contains(this.DocumentView?.())) { const layout_unrendered = this.childDocs.filter(doc => !this._renderCutoffData.get(doc[Id])); - const loadIncrement = 5; + const loadIncrement = this.Document.isTemplateDoc ? Number.MAX_VALUE : 5; for (var i = 0; i < Math.min(layout_unrendered.length, loadIncrement); i++) { this._renderCutoffData.set(layout_unrendered[i][Id] + '', true); } diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 923aead64..251235b93 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -141,6 +141,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent() impl const targetField = Doc.LayoutFieldKey(layoutDoc); const targetDoc = layoutDoc[DocData]; if (targetDoc[targetField] instanceof ImageField) { + added = true; this.dataDoc[this.fieldKey] = ObjectField.MakeCopy(targetDoc[targetField] as ImageField); Doc.SetNativeWidth(this.dataDoc, Doc.NativeWidth(targetDoc), this.fieldKey); Doc.SetNativeHeight(this.dataDoc, Doc.NativeHeight(targetDoc), this.fieldKey); -- cgit v1.2.3-70-g09d2 From 5458574510fcda755ae23b4001e17efa799865a5 Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 6 Mar 2024 13:29:30 -0500 Subject: fixed plotly/mermaids loading from a new account --- src/client/documents/Documents.ts | 6 ++-- src/client/util/CurrentUserUtils.ts | 38 ++++++++++++---------- src/client/util/DropConverter.ts | 1 - src/client/views/DashboardView.tsx | 1 - .../collections/collectionFreeForm/MarqueeView.tsx | 1 - src/client/views/nodes/DocumentView.tsx | 2 +- src/client/views/nodes/FontIconBox/FontIconBox.tsx | 4 +-- src/client/views/nodes/trails/PresBox.tsx | 8 ++--- src/fields/Doc.ts | 7 ++-- 9 files changed, 36 insertions(+), 32 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index c9b5cb915..d41cd24d4 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -273,7 +273,7 @@ export class DocumentOptions { layout_hideResizeHandles?: BOOLt = new BoolInfo('whether to hide the resize handles when selected'); layout_hideLinkButton?: BOOLt = new BoolInfo('whether the blue link counter button should be hidden'); layout_hideDecorationTitle?: BOOLt = new BoolInfo('whether to suppress the document decortations title when selected'); - _layout_hideContextMenu?: BOOLt = new BoolInfo('whether the context menu can be shown'); + layout_hideContextMenu?: BOOLt = new BoolInfo('whether the context menu can be shown'); layout_borderRounding?: string; _layout_modificationDate?: DATEt = new DateInfo('last modification date of doc layout', false); _layout_nativeDimEditable?: BOOLt = new BoolInfo('native dimensions can be modified using document decoration reizers', false); @@ -286,7 +286,6 @@ export class DocumentOptions { _layout_currentTimecode?: NUMt = new NumInfo('the current timecode of a time-based document (e.g., current time of a video) value is in seconds', false); _layout_centered?: BOOLt = new BoolInfo('whether text should be vertically centered in Doc'); _layout_fitWidth?: BOOLt = new BoolInfo('whether document should scale its contents to fit its rendered width or not (e.g., for PDFviews)'); - _layout_fitContentsToBox?: BOOLt = new BoolInfo('whether a freeformview should zoom/scale to create a shrinkwrapped view of its content'); _layout_fieldKey?: STRt = new StrInfo('the field key containing the current layout definition', false); _layout_enableAltContentUI?: BOOLt = new BoolInfo('whether to show alternate content button'); _layout_showTitle?: string; // field name to display in header (:hover is an optional suffix) @@ -397,6 +396,7 @@ export class DocumentOptions { _freeform_panY?: NUMt = new NumInfo('vertical pan location of a freeform view'); _freeform_noAutoPan?: BOOLt = new BoolInfo('disables autopanning when this item is dragged'); _freeform_noZoom?: BOOLt = new BoolInfo('disables zooming (used by Pile docs)'); + _freeform_fitContentsToBox?: BOOLt = new BoolInfo('whether a freeformview should zoom/scale to create a shrinkwrapped view of its content'); //BUTTONS buttonText?: string; @@ -699,7 +699,7 @@ export namespace Docs { DocumentType.FONTICON, { layout: { view: FontIconBox, dataField: 'icon' }, - options: { defaultDoubleClick: 'ignore', waitForDoubleClickToClick: 'never', layout_hideLinkButton: true, _width: 40, _height: 40 }, + options: { defaultDoubleClick: 'ignore', waitForDoubleClickToClick: 'never', layout_hideContextMenu: true, layout_hideLinkButton: true, _width: 40, _height: 40 }, }, ], [ diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index d75c717c9..310613bc9 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -66,7 +66,7 @@ export class CurrentUserUtils { const userTemplates = DocListCast(userDocTemplates?.data).filter(doc => !Doc.IsSystem(doc)); const reqdOpts:DocumentOptions = { title: "User Tools", _xMargin: 0, _layout_showTitle: "title", _chromeHidden: true, hidden: false, - _dragOnlyWithinContainer: true, _layout_hideContextMenu: true, isSystem: true, _forceActive: true, + _dragOnlyWithinContainer: true, layout_hideContextMenu: true, isSystem: true, _forceActive: true, _layout_autoHeight: true, _width: 500, _height: 300, _layout_fitWidth: true, _columnWidth: 35, ignoreClick: true, _lockedPosition: true, }; const reqdScripts = { dropConverter : "convertToButtons(dragData)" }; @@ -235,7 +235,7 @@ export class CurrentUserUtils { MakeTemplate(Doc.GetProto(slide), true, "Untitled Slide View"); return slide; } - const plotlyView = (opts:DocumentOptions) => { + const plotlyApi = () => { var plotly = Doc.MyPublishedDocs.find(doc => doc.title === "@plotly"); if (!plotly) { const plotly = Docs.Create.TextDocument( @@ -244,6 +244,8 @@ export class CurrentUserUtils { , {title: "@plotly", title_custom: true, _layout_showTitle:"title", _width:300,_height:400}); Doc.AddToMyPublished(plotly); } + } + const plotlyView = (opts:DocumentOptions) => { const rtfield = new RichTextField(JSON.stringify( {doc: {type:"doc",content:[ {type:"code_block",content:[ @@ -269,10 +271,10 @@ export class CurrentUserUtils { slide[DocData].text = rtfield; slide[DocData].layout_textPainted = ``; slide[DocData]._type_collection = CollectionViewType.Freeform; - slide.onPaint = ScriptField.MakeScript(`toggleDetail(documentView, "textPainted", "")`); + slide.onPaint = ScriptField.MakeScript(`toggleDetail(documentView, "textPainted", "")`, {documentView:"any"}); return slide; } - const mermaidsView = (opts:DocumentOptions) => { + const mermaidsApi = () => { var mermaids = Doc.MyPublishedDocs.find(doc => doc.title === "@mermaids"); if (!mermaids) { const mermaids = Docs.Create.TextDocument( @@ -294,6 +296,8 @@ export class CurrentUserUtils { , {title: "@mermaids", title_custom: true, _layout_showTitle:"title", _width:300,_height:400}); Doc.AddToMyPublished(mermaids); } + } + const mermaidsView = (opts:DocumentOptions) => { const rtfield = new RichTextField(JSON.stringify( {doc: {type:"doc",content:[ {type:"code_block",content:[ @@ -321,9 +325,10 @@ pie title Minerals in my tap water slide[DocData].text = rtfield; slide[DocData].layout_textPainted = ``; slide[DocData]._type_collection = CollectionViewType.Freeform; - slide.onPaint = ScriptField.MakeScript(`toggleDetail(documentView, "textPainted", "")`); + slide.onPaint = ScriptField.MakeScript(`toggleDetail(documentView, "textPainted", "")`, {documentView:"any"}); return slide; } + const apis = [plotlyApi(), mermaidsApi()] const emptyThings:{key:string, // the field name where the empty thing will be stored opts:DocumentOptions, // the document options that are required for the empty thing funcs?:{[key:string]: any}, // computed fields that are rquired for the empth thing @@ -396,14 +401,14 @@ pie title Minerals in my tap water const creatorBtns = CurrentUserUtils.creatorBtnDescriptors(doc).map((reqdOpts) => { const btn = dragCreatorDoc ? DocListCast(dragCreatorDoc.data).find(doc => doc.title === reqdOpts.title): undefined; const opts:DocumentOptions = {...OmitKeys(reqdOpts, ["funcs", "scripts", "backgroundColor"]).omit, - _width: 60, _height: 60, _layout_hideContextMenu: true, _dragOnlyWithinContainer: true, + _width: 60, _height: 60, _dragOnlyWithinContainer: true, btnType: ButtonType.ToolButton, backgroundColor: reqdOpts.backgroundColor ?? Colors.DARK_GRAY, color: Colors.WHITE, isSystem: true, }; return DocUtils.AssignScripts(DocUtils.AssignOpts(btn, opts) ?? Docs.Create.FontIconDocument(opts), reqdOpts.scripts, reqdOpts.funcs); }); const reqdOpts:DocumentOptions = { - title: "Document Creators", _layout_showTitle: "title", _xMargin: 0, _dragOnlyWithinContainer: true, _layout_hideContextMenu: true, _chromeHidden: true, isSystem: true, + title: "Document Creators", _layout_showTitle: "title", _xMargin: 0, _dragOnlyWithinContainer: true, layout_hideContextMenu: true, _chromeHidden: true, isSystem: true, _layout_autoHeight: true, _width: 500, _height: 300, _layout_fitWidth: true, _columnWidth: 40, ignoreClick: true, _lockedPosition: true, _forceActive: true, childDragAction: dropActionType.embed }; @@ -441,7 +446,7 @@ pie title Minerals in my tap water const btnDoc = myLeftSidebarMenu ? DocListCast(myLeftSidebarMenu.data).find(doc => doc.title === title) : undefined; const reqdBtnOpts:DocumentOptions = { title, icon, target, toolTip, hidden, btnType: ButtonType.MenuButton, isSystem: true, undoIgnoreFields: new List(['height', 'data_columnHeaders']), dontRegisterView: true, - _width: 60, _height: 60, _dragOnlyWithinContainer: true, _layout_hideContextMenu: true, + _width: 60, _height: 60, _dragOnlyWithinContainer: true, }; return DocUtils.AssignScripts(DocUtils.AssignOpts(btnDoc, reqdBtnOpts) ?? Docs.Create.FontIconDocument(reqdBtnOpts), scripts, funcs); }); @@ -544,7 +549,7 @@ pie title Minerals in my tap water 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_showTitle: "title", _width: 500, _yMargin: 20, _lockedPosition: true, _forceActive: true, _dragOnlyWithinContainer: true, _layout_hideContextMenu: true, _chromeHidden: true, + _layout_showTitle: "title", _width: 500, _yMargin: 20, _lockedPosition: true, _forceActive: true, _dragOnlyWithinContainer: true, layout_hideContextMenu: true, _chromeHidden: true, }; return DocUtils.AssignDocField(doc, field, (opts, items) => Docs.Create.StackingDocument(items??[], opts), reqdToolOps, [creatorBtns, userBtns]); } @@ -555,7 +560,7 @@ pie title Minerals in my tap water const newDashboard = `createNewDashboard()`; - const reqdBtnOpts:DocumentOptions = { _forceActive: true, _width: 30, _height: 30, _dragOnlyWithinContainer: true, _layout_hideContextMenu: true, + const reqdBtnOpts:DocumentOptions = { _forceActive: true, _width: 30, _height: 30, _dragOnlyWithinContainer: true, title: "new Dash", btnType: ButtonType.ClickButton, toolTip: "Create new dashboard", buttonText: "New trail", icon: "plus", isSystem: true }; const reqdBtnScript = {onClick: newDashboard,} const newDashboardButton = DocUtils.AssignScripts(DocUtils.AssignOpts(DocCast(myDashboards?.layout_headerButton), reqdBtnOpts) ?? Docs.Create.FontIconDocument(reqdBtnOpts), reqdBtnScript); @@ -596,7 +601,7 @@ pie title Minerals in my tap water var myFilesystem = DocCast(doc[field]); const newFolderOpts: DocumentOptions = { - _forceActive: true, _dragOnlyWithinContainer: true, _embedContainer: Doc.MyFilesystem, _layout_hideContextMenu: true, _width: 30, _height: 30, undoIgnoreFields:new List(['treeView_SortCriterion']), + _forceActive: true, _dragOnlyWithinContainer: true, _embedContainer: Doc.MyFilesystem, _width: 30, _height: 30, undoIgnoreFields:new List(['treeView_SortCriterion']), title: "New folder", color: Colors.BLACK, btnType: ButtonType.ClickButton, toolTip: "Create new folder", buttonText: "New folder", icon: "folder-plus", isSystem: true }; const newFolderScript = { onClick: CollectionTreeView.AddTreeFunc}; @@ -624,7 +629,7 @@ pie title Minerals in my tap water const recentlyClosed = DocUtils.AssignDocField(doc, field, (opts) => Docs.Create.TreeDocument([], opts), reqdOpts); const clearAll = (target:string) => `getProto(${target}).data = new List([])`; - const clearBtnsOpts:DocumentOptions = { _width: 30, _height: 30, _forceActive: true, _dragOnlyWithinContainer: true, _layout_hideContextMenu: true, + const clearBtnsOpts:DocumentOptions = { _width: 30, _height: 30, _forceActive: true, _dragOnlyWithinContainer: true, layout_hideContextMenu: true, title: "Empty", target: recentlyClosed, btnType: ButtonType.ClickButton, color: Colors.BLACK, buttonText: "Empty", icon: "trash", isSystem: true, toolTip: "Empty recently closed",}; DocUtils.AssignDocField(recentlyClosed, "layout_headerButton", (opts) => Docs.Create.FontIconDocument(opts), clearBtnsOpts, undefined, {onClick: clearAll("this.target")}); @@ -658,8 +663,7 @@ pie title Minerals in my tap water }) static createToolButton = (opts: DocumentOptions) => Docs.Create.FontIconDocument({ - btnType: ButtonType.ToolButton, _layout_hideContextMenu: true, - _dropPropertiesToRemove: new List([ "_layout_hideContextMenu"]), + btnType: ButtonType.ToolButton, _dropPropertiesToRemove: new List([ "layout_hideContextMenu"]), /*_nativeWidth: 40, _nativeHeight: 40, */ _width: 40, _height: 40, isSystem: true, ...opts, }) @@ -804,7 +808,7 @@ pie title Minerals in my tap water _nativeWidth: params.width ?? 30, _width: params.width ?? 30, _height: 30, _nativeHeight: 30, linearBtnWidth: params.linearBtnWidth, toolType: params.toolType, expertMode: params.expertMode, - _dragOnlyWithinContainer: true, _layout_hideContextMenu: true, _lockedPosition: true, + _dragOnlyWithinContainer: true, _lockedPosition: true, }; const reqdFuncs:{[key:string]:any} = { ...params.funcs, @@ -914,14 +918,14 @@ pie title Minerals in my tap water static setupImportSidebar(doc: Doc, field:string) { const reqdOpts:DocumentOptions = { title: "My Imports", _forceActive: true, _layout_showTitle: "title", childLayoutString: ImportElementBox.LayoutString('data'), - _dragOnlyWithinContainer: true, _layout_hideContextMenu: true, childLimitHeight: 0, onClickScriptDisable:"never", + _dragOnlyWithinContainer: true, layout_hideContextMenu: true, childLimitHeight: 0, onClickScriptDisable:"never", childDragAction: dropActionType.copy, _layout_autoHeight: true, _yMargin: 50, _gridGap: 15, layout_boxShadow: "0 0", _lockedPosition: true, isSystem: true, _chromeHidden: true, dontRegisterView: true, layout_explainer: "This is where documents that are Imported into Dash will go." }; const myImports = DocUtils.AssignDocField(doc, field, (opts) => Docs.Create.MasonryDocument([], opts), reqdOpts, undefined, {onClick: "deselectAll()"}); const reqdBtnOpts:DocumentOptions = { _forceActive: true, toolTip: "Import from computer", - _width: 30, _height: 30, color: Colors.BLACK, _dragOnlyWithinContainer: true, _layout_hideContextMenu: true, title: "Import", btnType: ButtonType.ClickButton, + _width: 30, _height: 30, color: Colors.BLACK, _dragOnlyWithinContainer: true, title: "Import", btnType: ButtonType.ClickButton, buttonText: "Import", icon: "upload", isSystem: true }; DocUtils.AssignDocField(myImports, "layout_headerButton", (opts) => Docs.Create.FontIconDocument(opts), reqdBtnOpts, undefined, { onClick: "importDocument()" }); return myImports; diff --git a/src/client/util/DropConverter.ts b/src/client/util/DropConverter.ts index 54066d267..ba981145d 100644 --- a/src/client/util/DropConverter.ts +++ b/src/client/util/DropConverter.ts @@ -73,7 +73,6 @@ export function convertDropDataToButtons(data: DragManager.DocumentDragData) { _nativeHeight: 100, _width: 100, _height: 100, - _layout_hideContextMenu: true, backgroundColor: StrCast(doc.backgroundColor), title: StrCast(layoutDoc.title), btnType: ButtonType.ClickButton, diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx index 439d250de..146ac5b01 100644 --- a/src/client/views/DashboardView.tsx +++ b/src/client/views/DashboardView.tsx @@ -428,7 +428,6 @@ export class DashboardView extends ObservableReactComponent<{}> { _width: 30, _height: 30, _dragOnlyWithinContainer: true, - _layout_hideContextMenu: true, title: 'New trail', toolTip: 'Create new trail', color: Colors.BLACK, diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index d0e59180d..e22b83307 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -494,7 +494,6 @@ export class MarqueeView extends ObservableReactComponent { - if (e && this.layoutDoc._layout_hideContextMenu && Doc.noviceMode) { + if (e && this.layoutDoc.layout_hideContextMenu && Doc.noviceMode) { e.preventDefault(); e.stopPropagation(); //!this._props.isSelected(true) && SelectionManager.SelectView(this.DocumentView(), false); diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx index 91b6de80b..d24c79b10 100644 --- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx +++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx @@ -333,8 +333,8 @@ export class FontIconBox extends ViewBoxBaseComponent() { label={this.label} onPointerDown={e => setupMoveUpEvents(this, e, returnTrue, emptyFunction, (e, doubleTap) => { - !doubleTap && script.script.run({ this: this.Document, self: this.Document, value: !toggleStatus, _readOnly_: false }); - doubleTap && double.script.run({ this: this.Document, self: this.Document, value: !toggleStatus, _readOnly_: false }); + (!doubleTap || !double) && script.script.run({ this: this.Document, self: this.Document, value: !toggleStatus, _readOnly_: false }); + doubleTap && double && double.script.run({ this: this.Document, self: this.Document, value: !toggleStatus, _readOnly_: false }); }) } /> diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index 918987034..0d4f9ec78 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -2223,10 +2223,10 @@ export class PresBox extends ViewBoxBaseComponent() { // prettier-ignore switch (layout) { case 'blank': return Docs.Create.FreeformDocument([], { title: input ? input : 'Blank slide', _width: 400, _height: 225, x, y }); - case 'title': return Docs.Create.FreeformDocument([title(), subtitle()], { title: input ? input : 'Title slide', _width: 400, _height: 225, _layout_fitContentsToBox: true, x, y }); - case 'header': return Docs.Create.FreeformDocument([header()], { title: input ? input : 'Section header', _width: 400, _height: 225, _layout_fitContentsToBox: true, x, y }); - case 'content': return Docs.Create.FreeformDocument([contentTitle(), content()], { title: input ? input : 'Title and content', _width: 400, _height: 225, _layout_fitContentsToBox: true, x, y }); - case 'twoColumns': return Docs.Create.FreeformDocument([contentTitle(), content1(), content2()], { title: input ? input : 'Title and two columns', _width: 400, _height: 225, _layout_fitContentsToBox: true, x, y }) + case 'title': return Docs.Create.FreeformDocument([title(), subtitle()], { title: input ? input : 'Title slide', _width: 400, _height: 225, _freeform_fitContentsToBox: true, x, y }); + case 'header': return Docs.Create.FreeformDocument([header()], { title: input ? input : 'Section header', _width: 400, _height: 225, _freeform_fitContentsToBox: true, x, y }); + case 'content': return Docs.Create.FreeformDocument([contentTitle(), content()], { title: input ? input : 'Title and content', _width: 400, _height: 225, _freeform_fitContentsToBox: true, x, y }); + case 'twoColumns': return Docs.Create.FreeformDocument([contentTitle(), content1(), content2()], { title: input ? input : 'Title and two columns', _width: 400, _height: 225, _freeform_fitContentsToBox: true, x, y }) } }; diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 1df9d80a1..be1af3e6c 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -172,7 +172,7 @@ export class Doc extends RefField { public static get MyTrails() { return DocCast(Doc.ActiveDashboard?.myTrails); } // prettier-ignore public static get MyCalendars() { return DocCast(Doc.ActiveDashboard?.myCalendars); } // prettier-ignore public static get MyOverlayDocs() { return DocListCast(Doc.ActiveDashboard?.myOverlayDocs ?? DocCast(Doc.UserDoc().myOverlayDocs)?.data); } // prettier-ignore - public static get MyPublishedDocs() { return DocListCast(Doc.ActiveDashboard?.myPublishedDocs ?? DocCast(Doc.UserDoc().myPublishedDocs)?.data); } // prettier-ignore + public static get MyPublishedDocs() { return DocListCast(Doc.ActiveDashboard?.myPublishedDocs).concat(DocListCast(DocCast(Doc.UserDoc().myPublishedDocs)?.data)); } // prettier-ignore public static get MyDashboards() { return DocCast(Doc.UserDoc().myDashboards); } // prettier-ignore public static get MyTemplates() { return DocCast(Doc.UserDoc().myTemplates); } // prettier-ignore public static get MyImports() { return DocCast(Doc.UserDoc().myImports); } // prettier-ignore @@ -583,6 +583,9 @@ export namespace Doc { Doc.RemoveDocFromList(doc[DocData], 'proto_embeddings', embedding); } export function AddEmbedding(doc: Doc, embedding: Doc) { + if (embedding === null) { + console.log('WHAT?'); + } Doc.AddDocToList(doc[DocData], 'proto_embeddings', embedding, undefined, undefined, undefined, undefined, undefined, true); } export function GetEmbeddings(doc: Doc) { @@ -607,7 +610,7 @@ export namespace Doc { const dataDoc = doc[DocData]; const availableEmbeddings = Doc.GetEmbeddings(dataDoc); const bestEmbedding = [...(dataDoc !== doc ? [doc] : []), ...availableEmbeddings].find(doc => !doc.embedContainer && doc.author === Doc.CurrentUserEmail); - bestEmbedding && Doc.AddDocToList(dataDoc, 'proto_embeddings', doc, undefined, undefined, undefined, undefined, undefined, true); + bestEmbedding && Doc.AddEmbedding(doc, doc); return bestEmbedding ?? Doc.MakeEmbedding(doc); } -- cgit v1.2.3-70-g09d2 From ecf3bf771156b3935d8c87f1fbaf5c4e294c6aee Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 6 Mar 2024 13:36:45 -0500 Subject: fixed backgroundColors to be set on dataDoc in infoUI --- .../collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx index 8628ca3c3..43b877705 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx @@ -34,7 +34,7 @@ export class CollectionFreeFormInfoUI extends ObservableReactComponent (this._currState = val)); } // prettier-ignore componentWillUnmount(): void { - this._props.Freeform.layoutDoc.backgroundColor = this._originalbackground; + this._props.Freeform.dataDoc.backgroundColor = this._originalbackground; } setCurrState = (state: infoState) => { @@ -45,9 +45,9 @@ export class CollectionFreeFormInfoUI extends ObservableReactComponent { - this._originalbackground = StrCast(this._props.Freeform.layoutDoc.backgroundColor); + this._originalbackground = StrCast(this._props.Freeform.dataDoc.backgroundColor); // state entry functions - const setBackground = (colour: string) => () => (this._props.Freeform.layoutDoc.backgroundColor = colour); + const setBackground = (colour: string) => () => (this._props.Freeform.dataDoc.backgroundColor = colour); const setOpacity = (opacity: number) => () => (this._props.Freeform.layoutDoc.opacity = opacity); // arc transition trigger conditions const firstDoc = () => (this._props.Freeform.childDocs.length ? this._props.Freeform.childDocs[0] : undefined); -- cgit v1.2.3-70-g09d2 From b938ac2909290bd5dd11eeb0a0056f4679dbb25e Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 7 Mar 2024 10:24:56 -0500 Subject: fixed several things related to templates for labels (used as icons) and FontIconboxes. Also made labels non uniformly resizable . --- src/client/documents/Documents.ts | 16 +++++++--------- src/client/util/CurrentUserUtils.ts | 16 +++++++++------- src/client/views/DocumentDecorations.tsx | 10 +++++----- .../collectionFreeForm/CollectionFreeFormView.tsx | 12 ++++++++---- src/client/views/nodes/FontIconBox/FontIconBox.tsx | 8 ++++++-- src/client/views/nodes/LabelBox.tsx | 10 +++------- 6 files changed, 38 insertions(+), 34 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index d41cd24d4..b7d8222b9 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -277,8 +277,8 @@ export class DocumentOptions { layout_borderRounding?: string; _layout_modificationDate?: DATEt = new DateInfo('last modification date of doc layout', false); _layout_nativeDimEditable?: BOOLt = new BoolInfo('native dimensions can be modified using document decoration reizers', false); - _layout_reflowVertical?: BOOLt = new BoolInfo('native height can be changed independent of width by dragging decoration resizers'); - _layout_reflowHorizontal?: BOOLt = new BoolInfo('whether a doc with a native size can be horizonally resized, causing some form of reflow'); + _layout_reflowVertical?: BOOLt = new BoolInfo('permit vertical resizing with content "reflow"'); + _layout_reflowHorizontal?: BOOLt = new BoolInfo('permit horizontal resizing with content reflow'); layout_boxShadow?: string; // box-shadow css string OR "standard" to use dash standard box shadow layout_maxShown?: NUMt = new NumInfo('maximum number of children to display at one time (see multicolumnview)'); _layout_autoHeight?: BOOLt = new BoolInfo('whether document automatically resizes vertically to display contents'); @@ -317,7 +317,6 @@ export class DocumentOptions { _label_minFontSize?: NUMt = new NumInfo('minimum font size for labelBoxes', false); _label_maxFontSize?: NUMt = new NumInfo('maximum font size for labelBoxes', false); stroke_width?: NUMt = new NumInfo('width of an ink stroke', false); - icon_label?: STRt = new StrInfo('label to use for a fontIcon doc (otherwise, the title is used)', false); mediaState?: STRt = new StrInfo(`status of audio/video media document: ${media_state.PendingRecording}, ${media_state.Recording}, ${media_state.Paused}, ${media_state.Playing}`, false); recording?: BOOLt = new BoolInfo('whether WebCam is recording or not'); slides?: DOCt = new DocInfo('presentation slide associated with video recording (bcz: should be renamed!!)'); @@ -652,8 +651,8 @@ export namespace Docs { [ DocumentType.LABEL, { - layout: { view: LabelBox, dataField: defaultDataKey }, - options: { _singleLine: true }, + layout: { view: LabelBox, dataField: 'title' }, + options: { _singleLine: true, layout_nativeDimEditable: true, layout_reflowHorizontal: true, layout_reflowVertical: true }, }, ], [ @@ -684,8 +683,8 @@ export namespace Docs { [ DocumentType.BUTTON, { - layout: { view: LabelBox, dataField: 'onClick' }, - options: {}, + layout: { view: LabelBox, dataField: 'title' }, + options: { layout_nativeDimEditable: true, layout_reflowHorizontal: true, layout_reflowVertical: true }, }, ], [ @@ -1881,8 +1880,7 @@ export namespace DocUtils { const hasContextAnchor = LinkManager.Links(doc).some(l => (l.link_anchor_2 === doc && Cast(l.link_anchor_1, Doc, null)?.annotationOn === context) || (l.link_anchor_1 === doc && Cast(l.link_anchor_2, Doc, null)?.annotationOn === context)); if (context && !hasContextAnchor && (context.type === DocumentType.VID || context.type === DocumentType.WEB || context.type === DocumentType.PDF || context.type === DocumentType.IMG)) { const pushpin = Docs.Create.FontIconDocument({ - title: 'pushpin', - icon_label: '', + title: '', annotationOn: Cast(doc.annotationOn, Doc, null), followLinkToggle: true, icon: 'map-pin', diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 310613bc9..cfba003fc 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -22,7 +22,7 @@ import { CollectionTreeView, TreeViewType } from "../views/collections/Collectio import { Colors } from "../views/global/globalEnums"; import { media_state } from "../views/nodes/AudioBox"; import { OpenWhere } from "../views/nodes/DocumentView"; -import { ButtonType } from "../views/nodes/FontIconBox/FontIconBox"; +import { ButtonType, FontIconBox } from "../views/nodes/FontIconBox/FontIconBox"; import { ImportElementBox } from "../views/nodes/importBox/ImportElementBox"; import { DragManager, dropActionType } from "./DragManager"; import { MakeTemplate } from "./DropConverter"; @@ -32,6 +32,8 @@ import { ScriptingGlobals } from "./ScriptingGlobals"; import { ColorScheme } from "./SettingsManager"; import { SnappingManager } from "./SnappingManager"; import { UndoManager } from "./UndoManager"; +import { LabelBox } from "../views/nodes/LabelBox"; +import { ImageBox } from "../views/nodes/ImageBox"; interface Button { // DocumentOptions fields a button can set @@ -156,14 +158,14 @@ export class CurrentUserUtils { } const allopts = {isSystem: true, onClickScriptDisable: "never", ...opts}; return DocUtils.AssignScripts( (curIcon?.iconTemplate === opts.iconTemplate ? - DocUtils.AssignOpts(curIcon, allopts):undefined) ?? ((templateIconsDoc[iconFieldName] = MakeTemplate(creator(allopts), true, iconFieldName, templateField))), + DocUtils.AssignOpts(curIcon, allopts):undefined) ?? ((templateIconsDoc[iconFieldName] = MakeTemplate(creator(allopts, templateField), true, iconFieldName, templateField))), {onClick:"deiconifyView(documentView)", onDoubleClick: "deiconifyViewToLightbox(documentView)", }); }; - const labelBox = (opts: DocumentOptions, data?:string) => Docs.Create.LabelDocument({ - textTransform: "unset", letterSpacing: "unset", _singleLine: false, _label_minFontSize: 14, _label_maxFontSize: 14, layout_borderRounding: "5px", _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, ...opts + const labelBox = (opts: DocumentOptions, fieldKey:string) => Docs.Create.LabelDocument({ + layout: LabelBox.LayoutString(fieldKey), textTransform: "unset", letterSpacing: "unset", _singleLine: false, _label_minFontSize: 14, _label_maxFontSize: 14, layout_borderRounding: "5px", _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, ...opts }); - const imageBox = (opts: DocumentOptions, url?:string) => Docs.Create.ImageDocument(url ?? "http://www.cs.brown.edu/~bcz/noImage.png", { "icon_nativeWidth": 360 / 4, "icon_nativeHeight": 270 / 4, iconTemplate:DocumentType.IMG, _width: 360 / 4, _height: 270 / 4, _layout_showTitle: "title", ...opts }); - const fontBox = (opts:DocumentOptions, data?:string) => Docs.Create.FontIconDocument({ _nativeHeight: 30, _nativeWidth: 30, _width: 30, _height: 30, ...opts }); + const imageBox = (opts: DocumentOptions, fieldKey:string) => Docs.Create.ImageDocument( "http://www.cs.brown.edu/~bcz/noImage.png", { layout:ImageBox.LayoutString(fieldKey), "icon_nativeWidth": 360 / 4, "icon_nativeHeight": 270 / 4, iconTemplate:DocumentType.IMG, _width: 360 / 4, _height: 270 / 4, _layout_showTitle: "title", ...opts }); + const fontBox = (opts:DocumentOptions, fieldKey:string) => Docs.Create.FontIconDocument({ layout:FontIconBox.LayoutString(fieldKey), _nativeHeight: 30, _nativeWidth: 30, _width: 30, _height: 30, ...opts }); const iconTemplates = [ makeIconTemplate(undefined, "title", { iconTemplate:DocumentType.LABEL, backgroundColor: "dimgray"}), makeIconTemplate(DocumentType.AUDIO, "title", { iconTemplate:DocumentType.LABEL, backgroundColor: "lightgreen"}), @@ -174,7 +176,7 @@ export class CurrentUserUtils { makeIconTemplate(DocumentType.COL, "icon", { iconTemplate:DocumentType.IMG}), makeIconTemplate(DocumentType.COL, "icon", { iconTemplate:DocumentType.IMG}), makeIconTemplate(DocumentType.VID, "icon", { iconTemplate:DocumentType.IMG}), - makeIconTemplate(DocumentType.BUTTON,"data", { iconTemplate:DocumentType.FONTICON}), + makeIconTemplate(DocumentType.BUTTON,"title", { iconTemplate:DocumentType.FONTICON}), //nasty hack .. templates are looked up exclusively by type -- but we want a template for a document with a certain field (transcription) .. so this hack and the companion hack in createCustomView does this for now makeIconTemplate("transcription" as any, "transcription", { iconTemplate:DocumentType.LABEL, backgroundColor: "orange" }), //makeIconTemplate(DocumentType.PDF, "icon", {iconTemplate:DocumentType.IMG}, (opts) => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", opts)) diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 87ee962a0..2fb9f0fc1 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -323,7 +323,7 @@ export class DocumentDecorations extends ObservableReactComponent { 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`; + doc._layout_borderRounding = `${radius}px`; }); return false; }, @@ -533,9 +533,9 @@ export class DocumentDecorations extends ObservableReactComponent docCurveTVal) { const localStartTVal = startSegmentT - Math.floor(i / 4); t !== (localStartTVal < 0 ? 0 : localStartTVal) && segment.push(inkSegment.split(localStartTVal < 0 ? 0 : localStartTVal, t)); - segment.length && segments.push(segment); + if (segment.length && (Math.abs(segment[0].points[0].x - segment[0].points.lastElement().x) > 0.5 || Math.abs(segment[0].points[0].y - segment[0].points.lastElement().y) > 0.5)) segments.push(segment); } // start a new segment from the intersection t value - segment = tVals.length - 1 === index ? [inkSegment.split(t).right] : []; + if (tVals.length - 1 === index) { + const split = inkSegment.split(t).right; + if (split && (Math.abs(split.points[0].x - split.points.lastElement().x) > 0.5 || Math.abs(split.points[0].y - split.points.lastElement().y) > 0.5)) segment = [split]; + else segment = []; + } else segment = []; startSegmentT = docCurveTVal; }); } else { @@ -1647,7 +1651,7 @@ export class CollectionFreeFormView extends CollectionSubView (this._showAnimTimeline = !this._showAnimTimeline)), icon: 'eye' }); - this._props.renderDepth && optionItems.push({ description: 'Use Background Color as Default', event: () => (Cast(Doc.UserDoc().emptyCollection, Doc, null)._backgroundColor = StrCast(this.layoutDoc._backgroundColor)), icon: 'palette' }); + this._props.renderDepth && optionItems.push({ description: 'Use Background Color as Default', event: () => (Cast(Doc.UserDoc().emptyCollection, Doc, null).backgroundColor = StrCast(this.layoutDoc.backgroundColor)), icon: 'palette' }); this._props.renderDepth && optionItems.push({ description: 'Fit Content Once', event: this.fitContentOnce, icon: 'object-group' }); if (!Doc.noviceMode) { optionItems.push({ description: (!Doc.NativeWidth(this.layoutDoc) || !Doc.NativeHeight(this.layoutDoc) ? 'Freeze' : 'Unfreeze') + ' Aspect', event: this.toggleNativeDimensions, icon: 'snowflake' }); diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx index d24c79b10..46043eefb 100644 --- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx +++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx @@ -81,9 +81,13 @@ export class FontIconBox extends ViewBoxBaseComponent() { } }; - // Determining UI Specs + /** + * this chooses the appropriate title for the label + * if the Document is a template, then we use the title of the data doc that it renders + * otherwise, we use the Document's title itself. + */ @computed get label() { - return StrCast(this.dataDoc.icon_label, StrCast(this.Document.title)); + return StrCast(this.Document.isTemplateDoc ? this.dataDoc.title : this.Document.title); } Icon = (color: string, iconFalse?: boolean) => { let icon; diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx index fd3074a88..be20b5934 100644 --- a/src/client/views/nodes/LabelBox.tsx +++ b/src/client/views/nodes/LabelBox.tsx @@ -17,12 +17,8 @@ import './LabelBox.scss'; import { PinProps, PresBox } from './trails'; import { Docs } from '../../documents/Documents'; -export interface LabelBoxProps extends FieldViewProps { - label?: string; -} - @observer -export class LabelBox extends ViewBoxBaseComponent() { +export class LabelBox extends ViewBoxBaseComponent() { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(LabelBox, fieldKey); } @@ -32,7 +28,7 @@ export class LabelBox extends ViewBoxBaseComponent() { private dropDisposer?: DragManager.DragDropDisposer; private _timeout: any; - constructor(props: LabelBoxProps) { + constructor(props: FieldViewProps) { super(props); makeObservable(this); } @@ -45,7 +41,7 @@ export class LabelBox extends ViewBoxBaseComponent() { } @computed get Title() { - return this.dataDoc.title_custom ? StrCast(this.Document.title) : this._props.label ? this._props.label : Field.toString(this.dataDoc[this.fieldKey] as Field); + return Field.toString(this.dataDoc[this.fieldKey] as Field); } protected createDropTarget = (ele: HTMLDivElement) => { -- cgit v1.2.3-70-g09d2 From f1ed1cff137c06afc4d4db8a8778f7827404889b Mon Sep 17 00:00:00 2001 From: bobzel Date: Sat, 9 Mar 2024 19:05:30 -0500 Subject: fixed up default text that uses a template to process an initial carriage return properly. fixed text with inherited templates to be able to show fields with a default dashField value from template that can be overidden on instance. --- src/client/documents/Documents.ts | 11 +++--- src/client/views/MarqueeAnnotator.tsx | 4 +-- src/client/views/PreviewCursor.tsx | 2 +- src/client/views/collections/TreeView.tsx | 2 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 36 +++++++++++-------- .../collections/collectionFreeForm/MarqueeView.tsx | 2 +- .../collectionSchema/SchemaTableCell.tsx | 2 +- src/client/views/nodes/MapBox/MapBox.tsx | 4 +-- .../views/nodes/MapboxMapBox/MapboxContainer.tsx | 4 +-- .../views/nodes/formattedText/FormattedTextBox.tsx | 40 ++++++++++------------ 10 files changed, 55 insertions(+), 52 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index e6969d1f3..7a3b965fe 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -3,7 +3,7 @@ import { action, reaction, runInAction } from 'mobx'; import { basename } from 'path'; import { DateField } from '../../fields/DateField'; import { Doc, DocListCast, Field, LinkedTo, Opt, StrListCast, updateCachedAcls } from '../../fields/Doc'; -import { Initializing } from '../../fields/DocSymbols'; +import { DocData, Initializing } from '../../fields/DocSymbols'; import { Id } from '../../fields/FieldSymbols'; import { HtmlField } from '../../fields/HtmlField'; import { InkField, PointData } from '../../fields/InkField'; @@ -1981,10 +1981,8 @@ export namespace DocUtils { } } - export function GetNewTextDoc(title: string, x: number, y: number, width?: number, height?: number, noMargins?: boolean, annotationOn?: Doc, backgroundColor?: string) { + export function GetNewTextDoc(title: string, x: number, y: number, width?: number, height?: number, annotationOn?: Doc, backgroundColor?: string) { const tbox = Docs.Create.TextDocument('', { - _xMargin: noMargins ? 0 : undefined, - _yMargin: noMargins ? 0 : undefined, annotationOn, backgroundColor, _width: width || 200, @@ -1997,11 +1995,14 @@ export namespace DocUtils { _layout_enableAltContentUI: BoolCast(Doc.UserDoc().defaultToFlashcards), title, }); + const template = Doc.UserDoc().defaultTextLayout; if (template instanceof Doc) { + // if a default text template is specified tbox._width = NumCast(template._width); tbox.layout_fieldKey = 'layout_' + StrCast(template.title); - Doc.GetProto(tbox)[StrCast(tbox.layout_fieldKey)] = template; + Doc.GetProto(tbox)[StrCast(tbox.layout_fieldKey)] = template; // set the text doc's layout to render with the text template + tbox[DocData].proto = template; // and also set the text doc to inherit from the template (this allows the template to specify default field values) } return tbox; } diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx index a4303c3aa..f59042b04 100644 --- a/src/client/views/MarqueeAnnotator.tsx +++ b/src/client/views/MarqueeAnnotator.tsx @@ -88,7 +88,7 @@ export class MarqueeAnnotator extends ObservableReactComponent this.highlight(this.props.highlightDragSrcColor ?? 'rgba(173, 216, 230, 0.75)', true, undefined, true); // hyperlink color const targetCreator = (annotationOn: Doc | undefined) => { - const target = DocUtils.GetNewTextDoc('Note linked to ' + this.props.Document.title, 0, 0, 100, 100, undefined, annotationOn, 'yellow'); + const target = DocUtils.GetNewTextDoc('Note linked to ' + this.props.Document.title, 0, 0, 100, 100, annotationOn, 'yellow'); FormattedTextBox.SetSelectOnLoad(target); return target; }; diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx index 456b753b4..a94c18295 100644 --- a/src/client/views/PreviewCursor.tsx +++ b/src/client/views/PreviewCursor.tsx @@ -85,7 +85,7 @@ export class PreviewCursor extends ObservableReactComponent<{}> { } else { FormattedTextBox.PasteOnLoad = e; if (e.clipboardData.getData('dash/pdfAnchor')) e.preventDefault(); - UndoManager.RunInBatch(() => this._addLiveTextDoc?.(DocUtils.GetNewTextDoc('', newPoint[0], newPoint[1], 500, undefined, undefined, undefined)), 'paste'); + UndoManager.RunInBatch(() => this._addLiveTextDoc?.(DocUtils.GetNewTextDoc('', newPoint[0], newPoint[1], 500, undefined, undefined)), 'paste'); } } //pasting in images diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index 0c0e49411..c6bbcb0a5 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -884,7 +884,7 @@ export class TreeView extends ObservableReactComponent { // prettier-ignore switch (property.split(':')[0]) { case StyleProp.Opacity: return this.treeView.outlineMode ? undefined : 1; - case StyleProp.BackgroundColor: return this.selected ? '#7089bb' : StrCast(doc._backgroundColor, StrCast(doc.backgroundColor)); + case StyleProp.BackgroundColor: return this.selected ? '#7089bb' : undefined;//StrCast(doc._backgroundColor, StrCast(doc.backgroundColor)); case StyleProp.Highlighting: if (this.treeView.outlineMode) return undefined; case StyleProp.BoxShadow: return undefined; case StyleProp.DocContents: diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 4eb946939..9500b918a 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -30,7 +30,7 @@ import { SelectionManager } from '../../../util/SelectionManager'; import { freeformScrollMode } from '../../../util/SettingsManager'; import { SnappingManager } from '../../../util/SnappingManager'; import { Transform } from '../../../util/Transform'; -import { undoBatch, UndoManager } from '../../../util/UndoManager'; +import { undoable, undoBatch, UndoManager } from '../../../util/UndoManager'; import { Timeline } from '../../animationtimeline/Timeline'; import { ContextMenu } from '../../ContextMenu'; import { GestureOverlay } from '../../GestureOverlay'; @@ -1146,23 +1146,29 @@ export class CollectionFreeFormView extends CollectionSubView this._props.isContentActive(); - @undoBatch + /** + * Create a new text note of the same style as the one being typed into. + * If the text doc is be part of a larger templated doc, the new Doc will be a copy of the templated Doc + * + * @param fieldProps render props for the text doc being typed into + * @param below whether to place the new text Doc below or to the right of the one being typed into. + * @returns whether the new text doc was created and added successfully + */ + createTextDocCopy = undoable((fieldProps: FieldViewProps, below: boolean) => { + 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.x = NumCast(textDoc.x) + (below ? 0 : NumCast(textDoc._width) + 10); + newDoc.y = NumCast(textDoc.y) + (below ? NumCast(textDoc._height) + 10 : 0); + FormattedTextBox.SetSelectOnLoad(newDoc); + FormattedTextBox.DontSelectInitialText = true; + return this.addDocument?.(newDoc); + }, 'copied text note'); + onKeyDown = (e: React.KeyboardEvent, fieldProps: FieldViewProps) => { if ((e.metaKey || e.ctrlKey || e.altKey || fieldProps.Document._createDocOnCR) && ['Tab', 'Enter'].includes(e.key)) { e.stopPropagation?.(); - const below = !e.altKey && e.key !== 'Tab'; - const layout_fieldKey = StrCast(fieldProps.fieldKey); - const newDoc = Doc.MakeCopy(fieldProps.Document, true); - const dataField = fieldProps.Document[Doc.LayoutFieldKey(newDoc)]; - newDoc[DocData][Doc.LayoutFieldKey(newDoc)] = dataField === undefined || Cast(dataField, listSpec(Doc), null)?.length !== undefined ? new List([]) : undefined; - if (below) newDoc.y = NumCast(fieldProps.Document.y) + NumCast(fieldProps.Document._height) + 10; - else newDoc.x = NumCast(fieldProps.Document.x) + NumCast(fieldProps.Document._width) + 10; - if (layout_fieldKey !== 'layout' && fieldProps.Document[layout_fieldKey] instanceof Doc) { - newDoc[layout_fieldKey] = fieldProps.Document[layout_fieldKey]; - } - newDoc[DocData].text = undefined; - FormattedTextBox.SetSelectOnLoad(newDoc); - return this.addDocument?.(newDoc); + return this.createTextDocCopy(fieldProps, !e.altKey && e.key !== 'Tab'); } }; @computed get childPointerEvents() { diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index e22b83307..b913e05ad 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -178,7 +178,7 @@ export class MarqueeView extends ObservableReactComponent() implem }); const targetCreator = (annotationOn: Doc | undefined) => { - const target = DocUtils.GetNewTextDoc('Note linked to ' + this.Document.title, 0, 0, 100, 100, undefined, annotationOn, 'yellow'); + const target = DocUtils.GetNewTextDoc('Note linked to ' + this.Document.title, 0, 0, 100, 100, annotationOn, 'yellow'); FormattedTextBox.SetSelectOnLoad(target); return target; }; @@ -592,7 +592,7 @@ export class MapBox extends ViewBoxAnnotatableComponent() implem /// this should use SELECTED pushpin for lat/long if there is a selection, otherwise CENTER const anchor = Docs.Create.ConfigDocument({ title: 'MapAnchor:' + this.Document.title, - text: StrCast(this.selectedPinOrRoute?.map) || StrCast(this.Document.map) || 'map location', + text: (StrCast(this.selectedPinOrRoute?.map) || StrCast(this.Document.map) || 'map location') as any, config_latitude: NumCast((existingPin ?? this.selectedPinOrRoute)?.latitude ?? this.dataDoc.latitude), config_longitude: NumCast((existingPin ?? this.selectedPinOrRoute)?.longitude ?? this.dataDoc.longitude), config_map_zoom: NumCast(this.dataDoc.map_zoom), diff --git a/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx b/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx index 8a5bd7ce6..3eb051dbf 100644 --- a/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx +++ b/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx @@ -232,7 +232,7 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent }); const targetCreator = (annotationOn: Doc | undefined) => { - const target = DocUtils.GetNewTextDoc('Note linked to ' + this.Document.title, 0, 0, 100, 100, undefined, annotationOn, 'yellow'); + const target = DocUtils.GetNewTextDoc('Note linked to ' + this.Document.title, 0, 0, 100, 100, annotationOn, 'yellow'); FormattedTextBox.SetSelectOnLoad(target); return target; }; @@ -466,7 +466,7 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent /// this should use SELECTED pushpin for lat/long if there is a selection, otherwise CENTER const anchor = Docs.Create.ConfigDocument({ title: 'MapAnchor:' + this.Document.title, - text: StrCast(this.selectedPin?.map) || StrCast(this.Document.map) || 'map location', + text: (StrCast(this.selectedPin?.map) || StrCast(this.Document.map) || 'map location') as any, config_latitude: NumCast((existingPin ?? this.selectedPin)?.latitude ?? this.dataDoc.latitude), config_longitude: NumCast((existingPin ?? this.selectedPin)?.longitude ?? this.dataDoc.longitude), config_map_zoom: NumCast(this.dataDoc.map_zoom), diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 54e3e7b44..1ff7274f8 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -67,7 +67,6 @@ import { RichTextMenu, RichTextMenuPlugin } from './RichTextMenu'; import { RichTextRules } from './RichTextRules'; import { schema } from './schema_rts'; import { SummaryView } from './SummaryView'; -import Select from 'react-select'; // import * as applyDevTools from 'prosemirror-dev-tools'; @observer export class FormattedTextBox extends ViewBoxAnnotatableComponent() implements ViewBoxInterface { @@ -100,7 +99,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent; private _keymap: any = undefined; @@ -306,7 +304,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { - const target = DocUtils.GetNewTextDoc('Note linked to ' + this.Document.title, 0, 0, 100, 100, undefined, annotationOn); + const target = DocUtils.GetNewTextDoc('Note linked to ' + this.Document.title, 0, 0, 100, 100, annotationOn); FormattedTextBox.SetSelectOnLoad(target); return target; }; @@ -1457,39 +1455,37 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent m.type !== mark.type), mark]; - const tr = this._editorView.state.tr - .setStoredMarks(storedMarks) - .insertText(FormattedTextBox.SelectOnLoadChar, this._editorView.state.doc.content.size - 1, this._editorView.state.doc.content.size) - .setStoredMarks(storedMarks); + const tr1 = this._editorView.state.tr.setStoredMarks(storedMarks); + const tr2 = selLoadChar === 'Enter' ? tr1.insert(this._editorView.state.doc.content.size - 1, schema.nodes.paragraph.create()) : tr1.insertText(selLoadChar, this._editorView.state.doc.content.size - 1); + const tr = tr2.setStoredMarks(storedMarks); + this._editorView.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(tr.doc.content.size)))); } else if (curText && !FormattedTextBox.DontSelectInitialText) { selectAll(this._editorView.state, this._editorView?.dispatch); } } - selectOnLoad && this._editorView!.focus(); + if (selectOnLoad) { + FormattedTextBox.DontSelectInitialText = false; + this._editorView!.focus(); + } if (this._props.isContentActive()) this.prepareForTyping(); - if (this._editorView) { - const tr = this._editorView.state.tr; - const { from, to } = tr.selection; - // for some reason, the selection is sometimes lost in the sidebar view when prosemirror syncs the seledtion with the dom, so reset the selection after the document has ben fully instantiated. - if (FormattedTextBox.DontSelectInitialText) setTimeout(() => this._editorView?.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(from), tr.doc.resolve(to)))), 250); - - if (FormattedTextBox.PasteOnLoad) { - const pdfAnchorId = FormattedTextBox.PasteOnLoad.clipboardData?.getData('dash/pdfAnchor'); - FormattedTextBox.PasteOnLoad = undefined; - pdfAnchorId && this.addPdfReference(pdfAnchorId); - } + if (this._editorView && FormattedTextBox.PasteOnLoad) { + const pdfAnchorId = FormattedTextBox.PasteOnLoad.clipboardData?.getData('dash/pdfAnchor'); + FormattedTextBox.PasteOnLoad = undefined; + pdfAnchorId && this.addPdfReference(pdfAnchorId); } - FormattedTextBox.DontSelectInitialText = false; } // add user mark for any first character that was typed since the user mark that gets set in KeyPress won't have been called yet. -- cgit v1.2.3-70-g09d2 From e1fb489ea8a16472e7b752fd5d08e964fbd87a8d Mon Sep 17 00:00:00 2001 From: newalina <157907056+newalina@users.noreply.github.com> Date: Sat, 9 Mar 2024 23:58:29 -0500 Subject: add show less show more button --- .../CollectionFreeFormInfoState.tsx | 24 ++++++++++++++++++++- .../CollectionFreeFormInfoUI.tsx | 13 ++++++----- .../public/assets/nProtectOnlineSecurityV1.dmg | Bin 0 -> 19716090 bytes 3 files changed, 31 insertions(+), 6 deletions(-) create mode 100644 src/server/public/assets/nProtectOnlineSecurityV1.dmg (limited to 'src/client/views/collections/collectionFreeForm') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx index 688f84cd5..ed77f29ad 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx @@ -98,12 +98,34 @@ export class CollectionFreeFormInfoState extends ObservableReactComponent
{this.State?.[StateMessage]}
- state message gif + + state message gif +
this.props.close())} /> diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx index a81195c95..9ced90a00 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx @@ -42,7 +42,7 @@ export class CollectionFreeFormInfoUI extends ObservableReactComponent TopBar.Instance.FlipDocumentationIcon() ); // prettier-ignore const movedDoc2 = InfoState( @@ -119,7 +120,8 @@ export class CollectionFreeFormInfoUI extends ObservableReactComponent TopBar.Instance.FlipDocumentationIcon() ); // prettier-ignore const movedDoc3 = InfoState( @@ -133,7 +135,8 @@ export class CollectionFreeFormInfoUI extends ObservableReactComponent TopBar.Instance.FlipDocumentationIcon() ); // prettier-ignore const multipleDocs = InfoState( @@ -178,7 +181,7 @@ export class CollectionFreeFormInfoUI extends ObservableReactComponent activeTool() === InkTool.Pen, () => penMode], }, 'documentation.png', - + () => TopBar.Instance.FlipDocumentationIcon() ); // prettier-ignore const presentDocs = InfoState( diff --git a/src/server/public/assets/nProtectOnlineSecurityV1.dmg b/src/server/public/assets/nProtectOnlineSecurityV1.dmg new file mode 100644 index 000000000..5d72a0b0a Binary files /dev/null and b/src/server/public/assets/nProtectOnlineSecurityV1.dmg differ -- cgit v1.2.3-70-g09d2 From a8119d13d78a12c4b3178510b8648168a870b53d Mon Sep 17 00:00:00 2001 From: newalina <157907056+newalina@users.noreply.github.com> Date: Sun, 10 Mar 2024 00:06:54 -0500 Subject: clean --- .../CollectionFreeFormInfoState.tsx | 2 +- .../CollectionFreeFormInfoUI.tsx | 185 +++++++++++---------- 2 files changed, 98 insertions(+), 89 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx index ed77f29ad..7661f5aff 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx @@ -123,7 +123,7 @@ export class CollectionFreeFormInfoState extends ObservableReactComponent state message gif -
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx index 9ced90a00..f416b61ac 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx @@ -76,112 +76,118 @@ export class CollectionFreeFormInfoUI extends ObservableReactComponent Doc.ActivePresentation?.presentation_status; // set of states - const start = InfoState('Click anywhere and begin typing to create your first text document.', { - docCreated: [() => numDocs(), () => { - docX = firstDoc()?.x; - docY = firstDoc()?.y; - return oneDoc; - }], - }); // prettier-ignore + const start = InfoState( + 'Click anywhere and begin typing to create your first text document.', + { + docCreated: [() => numDocs(), () => { + docX = firstDoc()?.x; + docY = firstDoc()?.y; + return oneDoc; + }], + } + ); // prettier-ignore - const oneDoc = InfoState('Hello world! You can drag and drop to move your document around.', { - // docCreated: [() => numDocs() > 1, () => multipleDocs], - docDeleted: [() => numDocs() < 1, () => start], - docMoved: [() => (docX && docX != docNewX()) || (docY && docY != docNewY()), () => { - docX = firstDoc()?.x; - docY = firstDoc()?.y; - return movedDoc1; - }], - }); // prettier-ignore + const oneDoc = InfoState( + 'Hello world! You can drag and drop to move your document around.', + { + // docCreated: [() => numDocs() > 1, () => multipleDocs], + docDeleted: [() => numDocs() < 1, () => start], + docMoved: [() => (docX && docX != docNewX()) || (docY && docY != docNewY()), () => { + docX = firstDoc()?.x; + docY = firstDoc()?.y; + return movedDoc1; + }], + } + ); // prettier-ignore const movedDoc1 = InfoState( - 'Great moves. Try creating a second document. You can see the list of supported document types by typing \":\".', - { - docCreated: [() => numDocs() == 2, () => multipleDocs], - docDeleted: [() => numDocs() < 1, () => start], - docMoved: [() => (docX && docX != docNewX()) || (docY && docY != docNewY()), () => { - docX = firstDoc()?.x; - docY = firstDoc()?.y; - return movedDoc2; - }], - }, - 'dash-colon-menu.gif', - () => TopBar.Instance.FlipDocumentationIcon() + 'Great moves. Try creating a second document. You can see the list of supported document types by typing \":\".', + { + docCreated: [() => numDocs() == 2, () => multipleDocs], + docDeleted: [() => numDocs() < 1, () => start], + docMoved: [() => (docX && docX != docNewX()) || (docY && docY != docNewY()), () => { + docX = firstDoc()?.x; + docY = firstDoc()?.y; + return movedDoc2; + }], + }, + 'dash-colon-menu.gif', + () => TopBar.Instance.FlipDocumentationIcon() ); // prettier-ignore const movedDoc2 = InfoState( - 'Slick moves. Try creating a second document. You can see the list of supported document types by typing \":\".', - { - docCreated: [() => numDocs() == 2, () => multipleDocs], - docDeleted: [() => numDocs() < 1, () => start], - docMoved: [() => (docX && docX != docNewX()) || (docY && docY != docNewY()), () => { - docX = firstDoc()?.x; - docY = firstDoc()?.y; - return movedDoc3; - }], - }, - 'dash-colon-menu.gif', - () => TopBar.Instance.FlipDocumentationIcon() + 'Slick moves. Try creating a second document. You can see the list of supported document types by typing \":\".', + { + docCreated: [() => numDocs() == 2, () => multipleDocs], + docDeleted: [() => numDocs() < 1, () => start], + docMoved: [() => (docX && docX != docNewX()) || (docY && docY != docNewY()), () => { + docX = firstDoc()?.x; + docY = firstDoc()?.y; + return movedDoc3; + }], + }, + 'dash-colon-menu.gif', + () => TopBar.Instance.FlipDocumentationIcon() ); // prettier-ignore const movedDoc3 = InfoState( - 'Groovy moves. Try creating a second document. You can see the list of supported document types by typing \":\".', - { - docCreated: [() => numDocs() == 2, () => multipleDocs], - docDeleted: [() => numDocs() < 1, () => start], - docMoved: [() => (docX && docX != docNewX()) || (docY && docY != docNewY()), () => { - docX = firstDoc()?.x; - docY = firstDoc()?.y; - return movedDoc1; - }], - }, - 'dash-colon-menu.gif', - () => TopBar.Instance.FlipDocumentationIcon() + 'Groovy moves. Try creating a second document. You can see the list of supported document types by typing \":\".', + { + docCreated: [() => numDocs() == 2, () => multipleDocs], + docDeleted: [() => numDocs() < 1, () => start], + docMoved: [() => (docX && docX != docNewX()) || (docY && docY != docNewY()), () => { + docX = firstDoc()?.x; + docY = firstDoc()?.y; + return movedDoc1; + }], + }, + 'dash-colon-menu.gif', + () => TopBar.Instance.FlipDocumentationIcon() ); // prettier-ignore const multipleDocs = InfoState( - 'Let\'s create a new link. Click the link icon on one of your documents.', - { - linkStarted: [() => linkStart(), () => startedLink], - docRemoved: [() => numDocs() < 2, () => oneDoc], - }, - 'dash-create-link-board.gif' + 'Let\'s create a new link. Click the link icon on one of your documents.', + { + linkStarted: [() => linkStart(), () => startedLink], + docRemoved: [() => numDocs() < 2, () => oneDoc], + }, + 'dash-create-link-board.gif' ); // prettier-ignore const startedLink = InfoState( - 'Now click the highlighted link icon on your other document.', - { - linkCreated: [() => numDocLinks(), () => madeLink], - docRemoved: [() => numDocs() < 2, () => oneDoc], - }, - 'dash-create-link-board.gif' + 'Now click the highlighted link icon on your other document.', + { + linkCreated: [() => numDocLinks(), () => madeLink], + docRemoved: [() => numDocs() < 2, () => oneDoc], + }, + 'dash-create-link-board.gif' ); // prettier-ignore const madeLink = InfoState( - 'You made your first link! You can view your links by selecting the blue dot.', - { - linkCreated: [() => !numDocLinks(), () => multipleDocs], - linkViewed: [() => linkMenuOpen(), () => { - alert(numDocLinks() + " cheer for " + numDocLinks() + " link!"); - return viewedLink; - }], - }, - 'dash-following-link.gif' + 'You made your first link! You can view your links by selecting the blue dot.', + { + linkCreated: [() => !numDocLinks(), () => multipleDocs], + linkViewed: [() => linkMenuOpen(), () => { + alert(numDocLinks() + " cheer for " + numDocLinks() + " link!"); + return viewedLink; + }], + }, + 'dash-following-link.gif' ); // prettier-ignore const viewedLink = InfoState( - 'Great work. You are now ready to create your own hypermedia world. Click the blinking question mark to learn more.', - { - linkDeleted: [() => !numDocLinks(), () => multipleDocs], - docRemoved: [() => numDocs() < 2, () => oneDoc], - docCreated: [() => numDocs() == 3, () => { - trail = pin().length; - return presentDocs; - }], - activePen: [() => activeTool() === InkTool.Pen, () => penMode], - }, - 'documentation.png', - () => TopBar.Instance.FlipDocumentationIcon() + 'Great work. You are now ready to create your own hypermedia world. Click the ? icon in the top right corner to learn more.', + { + linkDeleted: [() => !numDocLinks(), () => multipleDocs], + docRemoved: [() => numDocs() < 2, () => oneDoc], + docCreated: [() => numDocs() == 3, () => { + trail = pin().length; + return presentDocs; + }], + activePen: [() => activeTool() === InkTool.Pen, () => penMode], + }, + 'documentation.png', + () => TopBar.Instance.FlipDocumentationIcon() ); // prettier-ignore const presentDocs = InfoState( @@ -275,9 +281,12 @@ export class CollectionFreeFormInfoUI extends ObservableReactComponent numDocs() == 4, () => completed], }); - const completed = InfoState('Eager to learn more? Click the ? icon in the top right corner to read our full documentation.', { - docRemoved: [() => numDocs() == 1, () => oneDoc], - }); // prettier-ignore + const completed = InfoState( + 'Eager to learn more? Click the ? icon in the top right corner to read our full documentation.', + { docRemoved: [() => numDocs() == 1, () => oneDoc] }, + 'documentation.png', + () => TopBar.Instance.FlipDocumentationIcon() + ); // prettier-ignore return start; }; -- cgit v1.2.3-70-g09d2 From 9bf84a22532262f05920142b1b4932a5e832f522 Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 13 Mar 2024 10:46:58 -0400 Subject: cleaned up currState in freeform ui --- .../collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx index fcc89d662..3b1847c00 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx @@ -26,12 +26,12 @@ export class CollectionFreeFormInfoUI extends ObservableReactComponent (this._currState = val)); } // prettier-ignore componentWillUnmount(): void { @@ -42,7 +42,6 @@ export class CollectionFreeFormInfoUI extends ObservableReactComponent; + return !this.currState ? null : ; } } -- cgit v1.2.3-70-g09d2 From 088603bf5a56e84baa3bfd5b9a45c9196df19332 Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 13 Mar 2024 17:03:11 -0400 Subject: css and other cleanup for info UI --- .../CollectionFreeFormInfoState.tsx | 60 ++++++++-------------- .../CollectionFreeFormInfoUI.tsx | 43 ++-------------- .../collectionFreeForm/CollectionFreeFormView.scss | 39 +++++++------- 3 files changed, 45 insertions(+), 97 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx index 7661f5aff..24aedfa56 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx @@ -18,9 +18,9 @@ export const StateMessageGIF = Symbol('StateMessageGIF'); export const StateEntryFunc = Symbol('StateEntryFunc'); export class infoState { [StateMessage]: string = ''; - [key: string]: infoArc; [StateMessageGIF]?: string = ''; [StateEntryFunc]?: () => any; + [key: string]: infoArc; constructor(message: string, arcs: { [key: string]: infoArc }, messageGif?: string, entryFunc?: () => any) { this[StateMessage] = message; Object.assign(this, arcs); @@ -58,6 +58,7 @@ export interface CollectionFreeFormInfoStateProps { export class CollectionFreeFormInfoState extends ObservableReactComponent { _disposers: IReactionDisposer[] = []; @observable _hide = false; + @observable _expanded = false; constructor(props: any) { super(props); @@ -72,22 +73,16 @@ export class CollectionFreeFormInfoState extends ObservableReactComponent this._disposers.map(disposer => disposer()); - initState = () => - (this._disposers = this.Arcs.map(arc => ({ test: arc[0], act: arc[1] })).map(arc => { - return reaction( - // + initState = () => (this._disposers = + this.Arcs.map(arc => ({ test: arc[0], act: arc[1] })).map( + arc => reaction( arc.test, - res => { - if (res) { - const next = arc.act(res); - this._props.next(next); - } - }, + res => res && this._props.next(arc.act(res)), { fireImmediately: true } - ); - })); + ) + )); // prettier-ignore - componentDidMount(): void { + componentDidMount() { this.initState(); } componentDidUpdate(prevProps: Readonly) { @@ -95,39 +90,24 @@ export class CollectionFreeFormInfoState extends ObservableReactComponent -
{this.State?.[StateMessage]}
-
- - state message gif - +

+
+ state message gif
-
+
this.props.close())} />
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx index 3b1847c00..f8fe2bb0b 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx @@ -61,6 +61,7 @@ export class CollectionFreeFormInfoUI extends ObservableReactComponent firstDoc()?.y; const linkStart = () => DocumentLinksButton.StartLink; + const linkUnstart = () => !DocumentLinksButton.StartLink; const numDocLinks = () => LinkManager.Instance.getAllDirectLinks(firstDoc())?.length; const linkMenuOpen = () => DocButtonState.Instance.LinkEditorDocView; @@ -94,51 +95,16 @@ export class CollectionFreeFormInfoUI extends ObservableReactComponent (docX && docX != docNewX()) || (docY && docY != docNewY()), () => { docX = firstDoc()?.x; docY = firstDoc()?.y; - return movedDoc1; + return movedDoc; }], } ); // prettier-ignore - const movedDoc1 = InfoState( - 'Great moves. Try creating a second document. You can see the list of supported document types by typing \":\".', + const movedDoc = InfoState( + 'Great moves. Try creating a second document. You can see the list of supported document types by typing a colon (\":\")', { docCreated: [() => numDocs() == 2, () => multipleDocs], docDeleted: [() => numDocs() < 1, () => start], - docMoved: [() => (docX && docX != docNewX()) || (docY && docY != docNewY()), () => { - docX = firstDoc()?.x; - docY = firstDoc()?.y; - return movedDoc2; - }], - }, - 'dash-colon-menu.gif', - () => TopBar.Instance.FlipDocumentationIcon() - ); // prettier-ignore - - const movedDoc2 = InfoState( - 'Slick moves. Try creating a second document. You can see the list of supported document types by typing \":\".', - { - docCreated: [() => numDocs() == 2, () => multipleDocs], - docDeleted: [() => numDocs() < 1, () => start], - docMoved: [() => (docX && docX != docNewX()) || (docY && docY != docNewY()), () => { - docX = firstDoc()?.x; - docY = firstDoc()?.y; - return movedDoc3; - }], - }, - 'dash-colon-menu.gif', - () => TopBar.Instance.FlipDocumentationIcon() - ); // prettier-ignore - - const movedDoc3 = InfoState( - 'Groovy moves. Try creating a second document. You can see the list of supported document types by typing \":\".', - { - docCreated: [() => numDocs() == 2, () => multipleDocs], - docDeleted: [() => numDocs() < 1, () => start], - docMoved: [() => (docX && docX != docNewX()) || (docY && docY != docNewY()), () => { - docX = firstDoc()?.x; - docY = firstDoc()?.y; - return movedDoc1; - }], }, 'dash-colon-menu.gif', () => TopBar.Instance.FlipDocumentationIcon() @@ -156,6 +122,7 @@ export class CollectionFreeFormInfoUI extends ObservableReactComponent linkUnstart(), () => multipleDocs], linkCreated: [() => numDocLinks(), () => madeLink], docRemoved: [() => numDocs() < 2, () => oneDoc], }, diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss index 9e7d364ea..2c94446fb 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss @@ -273,33 +273,34 @@ margin: 15px; padding: 10px; + .collectionFreeform-infoUI-close { + position: absolute; + top: -10; + left: -10; + } - .msg { + .collectionFreeform-infoUI-msg { + position: relative; + max-width: 500; + margin: 10; + } + .collectionFreeform-infoUI-button { + border-radius: 50px; + font-size: 12px; + padding: 6; position: relative; - // display: block; - -webkit-user-select: none; - -khtml-user-select: none; - -moz-user-select: none; - -o-user-select: none; - user-select: none; } - .gif-container { + .collectionFreeform-infoUI-gif-container { position: relative; margin-top: 5px; - // display: block; + pointer-events: none; - justify-content: center; - align-items: center; - -webkit-user-select: none; - -khtml-user-select: none; - -moz-user-select: none; - -o-user-select: none; - user-select: none; - - .gif { - background-color: transparent; + > img { height: 300px; } } + .collectionFreeform-hidden { + display: none; + } } -- cgit v1.2.3-70-g09d2 From a5d81be52ec685aede35791a706e5e1cb7f40613 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 14 Mar 2024 08:45:16 -0400 Subject: fixed disabling info ui to keep it off -- use settings manager to turn it back on. --- src/client/util/SettingsManager.tsx | 3 ++- .../collections/collectionFreeForm/CollectionFreeFormInfoState.tsx | 4 ++-- .../views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx | 2 +- .../views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 4 ++-- src/fields/Doc.ts | 2 ++ 5 files changed, 9 insertions(+), 6 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm') diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx index 682704770..8594a1c92 100644 --- a/src/client/util/SettingsManager.tsx +++ b/src/client/util/SettingsManager.tsx @@ -483,7 +483,8 @@ export class SettingsManager extends React.Component<{}> { toggleStatus={BoolCast(Doc.defaultAclPrivate)} onClick={action(() => (Doc.defaultAclPrivate = !Doc.defaultAclPrivate))} /> - (Doc.IsSharingEnabled = !Doc.IsSharingEnabled))} /> + (Doc.IsSharingEnabled = !Doc.IsSharingEnabled))} /> + (Doc.IsInfoUIDisabled = !Doc.IsInfoUIDisabled))} />
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx index 24aedfa56..73dd7fea3 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx @@ -5,6 +5,7 @@ import * as React from 'react'; import { SettingsManager } from '../../../util/SettingsManager'; import { ObservableReactComponent } from '../../ObservableReactComponent'; import './CollectionFreeFormView.scss'; +import { Doc } from '../../../../fields/Doc'; /** * An Fsa Arc. The first array element is a test condition function that will be observed. @@ -57,7 +58,6 @@ export interface CollectionFreeFormInfoStateProps { @observer export class CollectionFreeFormInfoState extends ObservableReactComponent { _disposers: IReactionDisposer[] = []; - @observable _hide = false; @observable _expanded = false; constructor(props: any) { @@ -97,7 +97,7 @@ export class CollectionFreeFormInfoState extends ObservableReactComponent +

{this.State?.[StateMessage]}

); }; + + /** + * Display the Docs in the before/after fields of the comparison. This also supports a GPT flash card use case + * where if there are no Docs in the slots, but the main fieldKey contains text, then + * @param which + * @returns + */ const displayDoc = (which: string) => { const whichDoc = DocCast(this.dataDoc[which]); const targetDoc = DocCast(whichDoc?.annotationOn, whichDoc); - const subjectText = RTFCast(this.Document[this.fieldKey])?.Text; + const subjectText = RTFCast(this.Document[this.fieldKey])?.Text.trim(); // if there is no Doc in the first comparison slot, but the comparison box's fieldKey slot has a RichTextField, then render a text box to show the contents of the document's field key slot // of if there is no Doc in the second comparison slot, but the second slot has a RichTextField, then render a text box to show the contents of the document's field key slot const layoutTemplateString = !targetDoc @@ -188,15 +194,18 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent() : undefined; // A bit hacky to try out the concept of using GPT to fill in flashcards -- this whole process should probably be packaged into a script to be more generic. - // If the second slot doesn't have anything in it, but the fieldKey slot has text - // and the fieldKey + "_alternate" has a text that incldues "--TEXT--", then - // treat the fieldKey + "_altenrate" text as a GPT query parameterized by the fieldKey text - // Call GPT to fill in an "answer" value in the second slot. + // If the second slot doesn't have anything in it, but the fieldKey slot has text (e.g., this.text is a string) + // and the fieldKey + "_alternate" has text, then treat the _alternate's text as a GPT query (indicated by (( && )) ) that is parameterized (optionally) + // by the field references in the text (eg., this.text_alternate is + // "((Provide a one sentence definition for (this) that doesn't use any word in (this.excludeWords) ))" + // where (this) is replaced by the text in the fieldKey slot abd this.excludeWords is repalced by the conetnts of the excludeWords field + // A GPT call will put the "answer" in the second slot of the comparison (eg., text_2) if (which.endsWith('2') && !layoutTemplateString && !targetDoc) { - const queryText = RTFCast(this.Document[this.fieldKey + '_alternate'])?.Text; - if (queryText?.includes('--TEXT--') && subjectText) { - this.Document[DocData][this.fieldKey + '_2'] = ''; - gptAPICall(queryText?.replace('--TEXT--', subjectText), GPTCallType.COMPLETION).then(value => (this.Document[DocData][this.fieldKey + '_2'] = value.trim())); + var queryText = RTFCast(this.Document[this.fieldKey + '_alternate']) + ?.Text.replace('(this)', subjectText) // TODO: this should be done in KeyValueBox.setField but it doesn't know about the fieldKey ... + .trim(); + if (subjectText && queryText.match(/\(\(.*\)\)/)) { + KeyValueBox.SetField(this.Document, which, ':=' + queryText, false); // make the second slot be a computed field on the data doc that calls ChatGpt } } return targetDoc || layoutTemplateString ? ( diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index bbeacef88..9848f18e0 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -1215,7 +1215,7 @@ export class DocumentView extends DocComponent() { if (layout_fieldKey && layout_fieldKey !== 'layout' && layout_fieldKey !== 'layout_icon') this.Document.deiconifyLayout = layout_fieldKey.replace('layout_', ''); } else { const deiconifyLayout = Cast(this.Document.deiconifyLayout, 'string', null); - this.switchViews(deiconifyLayout ? true : false, deiconifyLayout, finalFinished); + this.switchViews(deiconifyLayout ? true : false, deiconifyLayout, finalFinished, true); this.Document.deiconifyLayout = undefined; this._props.bringToFront?.(this.Document); } diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index 8a49b4757..4ecaaa283 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -11,6 +11,7 @@ import { ViewBoxInterface } from '../DocComponent'; import { CollectionFreeFormDocumentView } from './CollectionFreeFormDocumentView'; import { DocumentView, OpenWhere } from './DocumentView'; import { PinProps } from './trails'; +import { computed } from 'mobx'; export interface FocusViewOptions { willPan?: boolean; // determines whether to pan to target document @@ -121,9 +122,12 @@ export class FieldView extends React.Component { public static LayoutString(fieldType: { name: string }, fieldStr: string) { return `<${fieldType.name} {...props} fieldKey={'${fieldStr}'}/>`; //e.g., "" } + @computed get fieldval() { + return this.props.Document[this.props.fieldKey]; + } render() { - const field = this.props.Document[this.props.fieldKey]; + const field = this.fieldval; // prettier-ignore if (field instanceof Doc) return

{field.title?.toString()}

; if (field === undefined) return

{''}

; diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx index 89a5ac0b8..2257e6455 100644 --- a/src/client/views/nodes/KeyValueBox.tsx +++ b/src/client/views/nodes/KeyValueBox.tsx @@ -6,7 +6,7 @@ import { Doc, Field, FieldResult } from '../../../fields/Doc'; import { List } from '../../../fields/List'; import { RichTextField } from '../../../fields/RichTextField'; import { ComputedField, ScriptField } from '../../../fields/ScriptField'; -import { DocCast } from '../../../fields/Types'; +import { DocCast, StrCast } from '../../../fields/Types'; import { ImageField } from '../../../fields/URLField'; import { Docs } from '../../documents/Documents'; import { SetupDrag } from '../../util/DragManager'; @@ -71,34 +71,51 @@ export class KeyValueBox extends ObservableReactComponent { } } }; - public static CompileKVPScript(value: string): KVPScript | undefined { - const eq = value.startsWith('='); - value = eq ? value.substring(1) : value; - const dubEq = value.startsWith(':=') ? 'computed' : value.startsWith('$=') ? 'script' : false; - value = dubEq ? value.substring(2) : value; - const options: ScriptOptions = { addReturn: true, typecheck: false, params: { this: Doc.name, self: Doc.name, documentView: 'any', _last_: 'any', _readOnly_: 'boolean' }, editable: true }; - if (dubEq) options.typecheck = false; - const script = CompileScript(value, { ...options, transformer: DocumentIconContainer.getTransformer() }); - return !script.compiled ? undefined : { script, type: dubEq, onDelegate: eq }; + /** + * this compiles a string as a script after parsing off initial characters that determine script parameters + * if the script starts with '=', then it will be stored on the delegate of the Doc, otherise on the data doc + * if the script then starts with a ':=', then it will be treated as ComputedField, + * '$=', then it will just be a Script + * @param value + * @returns + */ + public static CompileKVPScript(rawvalue: string): KVPScript | undefined { + const onDelegate = rawvalue.startsWith('='); + rawvalue = onDelegate ? rawvalue.substring(1) : rawvalue; + const type: 'computed' | 'script' | false = rawvalue.startsWith(':=') ? 'computed' : rawvalue.startsWith('$=') ? 'script' : false; + rawvalue = type ? rawvalue.substring(2) : rawvalue; + rawvalue = rawvalue.replace(/.*\(\((.*)\)\)/, 'dashCallChat(_setCacheResult_, this, "$1")'); + const value = ["'", '"', '`'].includes(rawvalue.length ? rawvalue[0] : '') || !isNaN(rawvalue as any) ? rawvalue : '`' + rawvalue + '`'; + + var script = ScriptField.CompileScript(rawvalue, {}, true, undefined, DocumentIconContainer.getTransformer()); + if (!script.compiled) { + script = ScriptField.CompileScript(value, {}, true, undefined, DocumentIconContainer.getTransformer()); + } + return !script.compiled ? undefined : { script, type, onDelegate }; } - public static ApplyKVPScript(doc: Doc, key: string, kvpScript: KVPScript, forceOnDelegate?: boolean): boolean { + public static ApplyKVPScript(doc: Doc, key: string, kvpScript: KVPScript, forceOnDelegate?: boolean, setResult?: (value: FieldResult) => void) { const { script, type, onDelegate } = kvpScript; //const target = onDelegate ? Doc.Layout(doc.layout) : Doc.GetProto(doc); // bcz: TODO need to be able to set fields on layout templates const target = forceOnDelegate || onDelegate || key.startsWith('_') ? doc : DocCast(doc.proto, doc); - let field: Field; - if (type === 'computed') { - field = new ComputedField(script); - } else if (type === 'script') { - field = new ScriptField(script); - } else { - const res = script.run({ this: Doc.Layout(doc), self: doc }, console.log); - if (!res.success) { - target[key] = script.originalScript; - return true; + let field: Field | undefined; + switch (type) { + case 'computed': field = new ComputedField(script); break; // prettier-ignore + case 'script': field = new ScriptField(script); break; // prettier-ignore + default: { + const _setCacheResult_ = (value: FieldResult) => { + field = value as Field; + setResult?.(value); + }; + const res = script.run({ this: Doc.Layout(doc), self: doc, _setCacheResult_ }, console.log); + if (!res.success) { + if (key) target[key] = script.originalScript; + return false; + } + field === undefined && (field = res.result); } - field = res.result; } + if (!key) return field; if (Field.IsField(field, true) && (key !== 'proto' || field !== target)) { target[key] = field; return true; @@ -107,10 +124,10 @@ export class KeyValueBox extends ObservableReactComponent { } @undoBatch - public static SetField(doc: Doc, key: string, value: string, forceOnDelegate?: boolean) { + public static SetField(doc: Doc, key: string, value: string, forceOnDelegate?: boolean, setResult?: (value: FieldResult) => void) { const script = this.CompileKVPScript(value); if (!script) return false; - return this.ApplyKVPScript(doc, key, script, forceOnDelegate); + return this.ApplyKVPScript(doc, key, script, forceOnDelegate, setResult); } onPointerDown = (e: React.PointerEvent): void => { diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx index f9e8ce4f3..d59489a78 100644 --- a/src/client/views/nodes/KeyValuePair.tsx +++ b/src/client/views/nodes/KeyValuePair.tsx @@ -125,7 +125,7 @@ export class KeyValuePair extends ObservableReactComponent { pinToPres: returnZero, }} GetValue={() => Field.toKeyValueString(this._props.doc, this._props.keyName)} - SetValue={(value: string) => KeyValueBox.SetField(this._props.doc, this._props.keyName, value)} + SetValue={(value: string) => (KeyValueBox.SetField(this._props.doc, this._props.keyName, value) ? true : false)} /> diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx index 5c4d850ad..62cb460c2 100644 --- a/src/client/views/nodes/formattedText/DashFieldView.tsx +++ b/src/client/views/nodes/formattedText/DashFieldView.tsx @@ -137,7 +137,7 @@ export class DashFieldViewInternal extends ObservableReactComponent this._props.tbox._props.PanelWidth() - 20 : returnZero} selectedCell={() => [this._dashDoc!, 0]} fieldKey={this._fieldKey} diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts index d5c91fc09..c798ae4b3 100644 --- a/src/client/views/nodes/formattedText/RichTextRules.ts +++ b/src/client/views/nodes/formattedText/RichTextRules.ts @@ -1,6 +1,6 @@ import { ellipsis, emDash, InputRule, smartQuotes, textblockTypeInputRule } from 'prosemirror-inputrules'; import { NodeSelection, TextSelection } from 'prosemirror-state'; -import { Doc, StrListCast } from '../../../../fields/Doc'; +import { Doc, FieldResult, StrListCast } from '../../../../fields/Doc'; import { DocData } from '../../../../fields/DocSymbols'; import { Id } from '../../../../fields/FieldSymbols'; import { List } from '../../../../fields/List'; @@ -8,13 +8,14 @@ import { NumCast, StrCast } from '../../../../fields/Types'; import { Utils } from '../../../../Utils'; import { DocServer } from '../../../DocServer'; import { Docs, DocUtils } from '../../../documents/Documents'; +import { CollectionViewType } from '../../../documents/DocumentTypes'; +import { CollectionView } from '../../collections/CollectionView'; +import { ContextMenu } from '../../ContextMenu'; +import { KeyValueBox } from '../KeyValueBox'; import { FormattedTextBox } from './FormattedTextBox'; import { wrappingInputRule } from './prosemirrorPatches'; import { RichTextMenu } from './RichTextMenu'; import { schema } from './schema_rts'; -import { CollectionView } from '../../collections/CollectionView'; -import { CollectionViewType } from '../../../documents/DocumentTypes'; -import { ContextMenu } from '../../ContextMenu'; export class RichTextRules { public Document: Doc; @@ -282,18 +283,28 @@ export class RichTextRules { : tr; }), + new InputRule(new RegExp(/(^|[^=])(\(\(.*\)\))/), (state, match, start, end) => { + var count = 0; // ignore first return value which will be the notation that chat is pending a result + KeyValueBox.SetField(this.Document, '', match[2], false, (gptval: FieldResult) => { + count && this.TextBox.EditorView?.dispatch(this.TextBox.EditorView!.state.tr.insertText(' ' + (gptval as string))); + count++; + }); + return null; + }), + // create a text display of a metadata field on this or another document, or create a hyperlink portal to another document // [[ : ]] // [[:docTitle]] => hyperlink // [[fieldKey]] => show field - // [[fieldKey=value]] => show field and also set its value + // [[fieldKey{:,=:}=value]] => show field and also set its value // [[fieldKey:docTitle]] => show field of doc new InputRule( - new RegExp(/\[\[([a-zA-Z_\? \-0-9]*)(=[a-z,A-Z_@\? /\-0-9]*)?(:[a-zA-Z_@:\.\? \-0-9]+)?\]\]$/), + new RegExp(/\[\[([a-zA-Z_\? \-0-9]*)((=:|:)?=)([a-z,A-Z_@\?+\-*/\ 0-9\(\)]*)?(:[a-zA-Z_@:\.\? \-0-9]+)?\]\]$/), (state, match, start, end) => { const fieldKey = match[1]; - const docTitle = match[3]?.replace(':', ''); - const value = match[2]?.substring(1); + const assign = match[2] === '=' ? '' : match[2]; + const value = match[4]; + const docTitle = match[5]?.replace(':', ''); const linkToDoc = (target: Doc) => { const rstate = this.TextBox.EditorView?.state; const selection = rstate?.selection.$from.pos; @@ -325,13 +336,14 @@ export class RichTextRules { } return state.tr; } - if (value?.includes(',')) { + // if the value has commas assume its an array (unless it's part of a chat gpt call indicated by '((' ) + if (value?.includes(',') && !value.startsWith('((')) { const values = value.split(','); const strs = values.some(v => !v.match(/^[-]?[0-9.]$/)); this.Document[DocData][fieldKey] = strs ? new List(values) : new List(values.map(v => Number(v))); - } else if (value !== '' && value !== undefined) { - const num = value.match(/^[0-9.]$/); - this.Document[DocData][fieldKey] = value === 'true' ? true : value === 'false' ? false : num ? Number(value) : value; + } else if (value) { + KeyValueBox.SetField(this.Document, fieldKey, assign + value, Doc.IsDataProto(this.Document) ? true : undefined, assign ? undefined: + (gptval: FieldResult) => this.Document[DocData][fieldKey] = gptval as string ); // prettier-ignore } const target = getTitledDoc(docTitle); const fieldView = state.schema.nodes.dashField.create({ fieldKey, docId: target?.[Id], hideKey: false }); diff --git a/src/client/views/nodes/formattedText/nodes_rts.ts b/src/client/views/nodes/formattedText/nodes_rts.ts index 4706a97fa..c9115be90 100644 --- a/src/client/views/nodes/formattedText/nodes_rts.ts +++ b/src/client/views/nodes/formattedText/nodes_rts.ts @@ -1,4 +1,3 @@ -import * as React from 'react'; import { DOMOutputSpec, Node, NodeSpec } from 'prosemirror-model'; import { listItem, orderedList } from 'prosemirror-schema-list'; import { ParagraphNodeSpec, toParagraphDOM, getParagraphNodeAttrs } from './ParagraphNodeSpec'; diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 921d7aa5d..30f5f716c 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -34,14 +34,29 @@ import * as JSZip from 'jszip'; import { FieldViewProps } from '../client/views/nodes/FieldView'; export const LinkedTo = '-linkedTo'; export namespace Field { - export function toKeyValueString(doc: Doc, key: string): string { - const onDelegate = Object.keys(doc).includes(key.replace(/^_/, '')); + /** + * Converts a field to its equivalent input string in the key value box such that if the string + * is entered into a keyValueBox it will create an equivalent field (except if showComputedValue is set). + * @param doc doc containing key + * @param key field key to display + * @param showComputedValue whether copmuted function should display its value instead of its function + * @returns string representation of the field + */ + export function toKeyValueString(doc: Doc, key: string, showComputedValue?: boolean): string { + const onDelegate = !Doc.IsDataProto(doc) && Object.keys(doc).includes(key.replace(/^_/, '')); const field = ComputedField.WithoutComputed(() => FieldValue(doc[key])); return !Field.IsField(field) ? key.startsWith('_') ? '=' : '' - : (onDelegate ? '=' : '') + (field instanceof ComputedField ? `:=${field.script.originalScript}` : field instanceof ScriptField ? `$=${field.script.originalScript}` : Field.toScriptString(field)); + : (onDelegate ? '=' : '') + + (field instanceof ComputedField && showComputedValue + ? field._lastComputedResult + : field instanceof ComputedField + ? `:=${field.script.originalScript}` + : field instanceof ScriptField + ? `$=${field.script.originalScript}` + : Field.toScriptString(field)); } export function toScriptString(field: Field) { switch (typeof field) { diff --git a/src/fields/ScriptField.ts b/src/fields/ScriptField.ts index c7fe72ca6..9021c8896 100644 --- a/src/fields/ScriptField.ts +++ b/src/fields/ScriptField.ts @@ -1,15 +1,17 @@ +import { action, makeObservable, observable } from 'mobx'; import { computedFn } from 'mobx-utils'; import { createSimpleSchema, custom, map, object, primitive, PropSchema, serializable, SKIP } from 'serializr'; import { DocServer } from '../client/DocServer'; -import { CompiledScript, CompileScript, ScriptOptions } from '../client/util/Scripting'; +import { CompiledScript, CompileScript, ScriptOptions, Transformer } from '../client/util/Scripting'; import { scriptingGlobal, ScriptingGlobals } from '../client/util/ScriptingGlobals'; import { autoObject, Deserializable } from '../client/util/SerializationHelper'; import { numberRange } from '../Utils'; -import { Doc, Field, Opt } from './Doc'; -import { Copy, Id, ToJavascriptString, ToScriptString, ToString, ToValue } from './FieldSymbols'; +import { Doc, Field, FieldResult, Opt } from './Doc'; +import { Copy, FieldChanged, Id, ToJavascriptString, ToScriptString, ToString, ToValue } from './FieldSymbols'; import { List } from './List'; import { ObjectField } from './ObjectField'; import { Cast, StrCast } from './Types'; +import { GPTCallType, gptAPICall } from '../client/apis/gpt/GPT'; function optional(propSchema: PropSchema) { return custom( @@ -85,6 +87,13 @@ export class ScriptField extends ObjectField { readonly script: CompiledScript; @serializable(object(scriptSchema)) readonly setterscript: CompiledScript | undefined; + @serializable + @observable + _cachedResult: FieldResult = undefined; + setCacheResult = action((value: FieldResult) => { + this._cachedResult = value; + this[FieldChanged]?.(); + }); @serializable(autoObject()) captures?: List; @@ -122,21 +131,25 @@ export class ScriptField extends ObjectField { [ToString]() { return this.script.originalScript; } - public static CompileScript(script: string, params: object = {}, addReturn = false, capturedVariables?: { [name: string]: Doc | string | number | boolean }) { + public static CompileScript(script: string, params: object = {}, addReturn = false, capturedVariables?: { [name: string]: Doc | string | number | boolean }, transformer?: Transformer) { return CompileScript(script, { params: { this: Doc?.name || 'Doc', // this is the doc that executes the script self: Doc?.name || 'Doc', // self is the root doc of the doc that executes the script + documentView: 'any', _last_: 'any', // _last_ is the previous value of a computed field when it is being triggered to re-run. + _setCacheResult_: 'any', // set the cached value of the function _readOnly_: 'boolean', // _readOnly_ is set when a computed field is executed to indicate that it should not have mobx side-effects. used for checking the value of a set function (see FontIconBox) ...params, }, + transformer, typecheck: false, editable: true, addReturn: addReturn, capturedVariables, }); } + public static MakeFunction(script: string, params: object = {}, capturedVariables?: { [name: string]: Doc | string | number | boolean }) { const compiled = ScriptField.CompileScript(script, params, true, capturedVariables); return compiled.compiled ? new ScriptField(compiled) : undefined; @@ -146,29 +159,62 @@ export class ScriptField extends ObjectField { const compiled = ScriptField.CompileScript(script, params, false, capturedVariables); return compiled.compiled ? new ScriptField(compiled) : undefined; } + public static CallGpt(queryText: string, setVal: (val: FieldResult) => void, target: Doc) { + if (typeof queryText === 'string' && setVal) { + while (queryText.match(/\(this\.[a-zA-Z_]*\)/)?.length) { + const fieldRef = queryText.split('(this.')[1].replace(/\).*/, ''); + queryText = queryText.replace(/\(this\.[a-zA-Z_]*\)/, Field.toString(target[fieldRef] as Field)); + } + setVal(`Chat Pending: ${queryText}`); + gptAPICall(queryText, GPTCallType.COMPLETION).then(result => { + if (queryText.includes('#')) { + const matches = result.match(/-?[0-9][0-9,]+[.]?[0-9]*/); + if (matches?.length) setVal(Number(matches[0].replace(/,/g, ''))); + } else setVal(result.trim()); + }); + } + } } @scriptingGlobal @Deserializable('computed', deserializeScript) export class ComputedField extends ScriptField { - _lastComputedResult: any; - //TODO maybe add an observable cache based on what is passed in for doc, considering there shouldn't really be that many possible values for doc - value = computedFn((doc: Doc) => this._valueOutsideReaction(doc)); - _valueOutsideReaction = (doc: Doc) => (this._lastComputedResult = this.script.compiled && this.script.run({ this: doc, self: doc, value: '', _last_: this._lastComputedResult, _readOnly_: true }, console.log).result); - - [ToValue](doc: Doc) { - return ComputedField.toValue(doc, this); + static undefined = '__undefined'; + static useComputed = true; + static DisableComputedFields() { this.useComputed = false; } // prettier-ignore + static EnableComputedFields() { this.useComputed = true; } // prettier-ignore + static WithoutComputed(fn: () => T) { + this.DisableComputedFields(); + try { + return fn(); + } finally { + this.EnableComputedFields(); + } } - [Copy](): ObjectField { - return new ComputedField(this.script, this.setterscript, this.rawscript); + + constructor(script: CompiledScript | undefined, setterscript?: CompiledScript, rawscript?: string) { + super(script, setterscript, rawscript); + makeObservable(this); } + _lastComputedResult: FieldResult; + value = computedFn((doc: Doc) => this._valueOutsideReaction(doc)); + _valueOutsideReaction = (doc: Doc) => { + this._lastComputedResult = + this._cachedResult ?? (this.script.compiled && this.script.run({ this: doc, self: doc, value: '', _setCacheResult_: this.setCacheResult, _last_: this._lastComputedResult, _readOnly_: true }, console.log).result); + return this._lastComputedResult; + }; + + [ToValue](doc: Doc) { if (ComputedField.useComputed) return { value: this._valueOutsideReaction(doc) }; } // prettier-ignore + [Copy](): ObjectField { return new ComputedField(this.script, this.setterscript, this.rawscript); } // prettier-ignore + public static MakeFunction(script: string, params: object = {}, capturedVariables?: { [name: string]: Doc | string | number | boolean }, setterscript?: string) { const compiled = ScriptField.CompileScript(script, params, true, { value: '', ...capturedVariables }); const compiledsetter = setterscript ? ScriptField.CompileScript(setterscript, { ...params, value: 'any' }, false, capturedVariables) : undefined; const compiledsetscript = compiledsetter?.compiled ? compiledsetter : undefined; return compiled.compiled ? new ComputedField(compiled, compiledsetscript) : undefined; } + public static MakeInterpolatedNumber(fieldKey: string, interpolatorKey: string, doc: Doc, curTimecode: number, defaultVal: Opt) { if (!doc[`${fieldKey}_indexed`]) { const flist = new List(numberRange(curTimecode + 1).map(i => undefined) as any as number[]); @@ -206,33 +252,6 @@ export class ComputedField extends ScriptField { return (doc[`${fieldKey}`] = getField.compiled ? new ComputedField(getField, setField?.compiled ? setField : undefined) : undefined); } } -export namespace ComputedField { - let useComputed = true; - export function DisableComputedFields() { - useComputed = false; - } - - export function EnableComputedFields() { - useComputed = true; - } - - export const undefined = '__undefined'; - - export function WithoutComputed(fn: () => T) { - DisableComputedFields(); - try { - return fn(); - } finally { - EnableComputedFields(); - } - } - - export function toValue(doc: any, value: any) { - if (useComputed) { - return { value: value._valueOutsideReaction(doc) }; - } - } -} ScriptingGlobals.add( function setIndexVal(list: any[], index: number, value: any) { @@ -258,3 +277,6 @@ ScriptingGlobals.add( 'returns the value at a given index of a list', '(list: any[], index: number)' ); +ScriptingGlobals.add(function dashCallChat(setVal: (val: FieldResult) => void, target: Doc, queryText: string) { + ScriptField.CallGpt(queryText, setVal, target); +}, 'calls chat gpt for the query string and then calls setVal with the result'); diff --git a/src/server/public/assets/documentation.png b/src/server/public/assets/documentation.png new file mode 100644 index 000000000..95c76b198 Binary files /dev/null and b/src/server/public/assets/documentation.png differ -- cgit v1.2.3-70-g09d2