From e262d9ac73af5b2cef384468c47d69917e205d44 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Tue, 7 Apr 2020 23:01:46 -0400 Subject: lots of code cleanup - removed all northstar db stuff. added scriptingBox. renamed things. made collectiontypes strings not numbers. --- src/client/views/nodes/ScriptingBox.tsx | 71 +++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 src/client/views/nodes/ScriptingBox.tsx (limited to 'src/client/views/nodes/ScriptingBox.tsx') diff --git a/src/client/views/nodes/ScriptingBox.tsx b/src/client/views/nodes/ScriptingBox.tsx new file mode 100644 index 000000000..a2dd134ed --- /dev/null +++ b/src/client/views/nodes/ScriptingBox.tsx @@ -0,0 +1,71 @@ +import { library } from '@fortawesome/fontawesome-svg-core'; +import { faEdit } from '@fortawesome/free-regular-svg-icons'; +import { computed } from 'mobx'; +import { observer } from 'mobx-react'; +import * as React from 'react'; +import { Doc } from '../../../new_fields/Doc'; +import { documentSchema } from '../../../new_fields/documentSchemas'; +import { CompileScript } from "../../util/Scripting"; +import { ScriptBox } from '../ScriptBox'; +import { createSchema, listSpec, makeInterface } from '../../../new_fields/Schema'; +import { ScriptField } from '../../../new_fields/ScriptField'; +import { BoolCast, ScriptCast } from '../../../new_fields/Types'; +import { DocComponent } from '../DocComponent'; +import { FieldView, FieldViewProps } from './FieldView'; +import './ScriptingBox.scss'; +import { DocumentIconContainer } from './DocumentIcon'; + + +library.add(faEdit as any); + +const ScriptingSchema = createSchema({ + onClick: ScriptField, + buttonParams: listSpec("string"), + text: "string" +}); + +type ScriptingDocument = makeInterface<[typeof ScriptingSchema, typeof documentSchema]>; +const ScriptingDocument = makeInterface(ScriptingSchema, documentSchema); + +@observer +export class ScriptingBox extends DocComponent(ScriptingDocument) { + public static LayoutString(fieldKey: string) { return FieldView.LayoutString(ScriptingBox, fieldKey); } + + @computed get dataDoc() { + return this.props.DataDoc && + (this.Document.isTemplateForField || BoolCast(this.props.DataDoc.isTemplateForField) || + this.props.DataDoc.layout === this.props.Document) ? this.props.DataDoc : Doc.GetProto(this.props.Document); + } + + specificContextMenu = (e: React.MouseEvent): void => { } + + render() { + const script = ScriptCast(this.props.Document[this.props.fieldKey]); + let originalText: string | undefined = undefined; + if (script) { + originalText = script.script.originalScript; + } + return !(this.props.Document instanceof Doc) ? (null) : + { }} + onCancel={() => { }} + onSave={(text, onError) => { + if (!text) { + this.dataDoc[this.props.fieldKey] = undefined; + } else { + const script = CompileScript(text, { + params: { this: Doc.name }, + typecheck: false, + editable: true, + transformer: DocumentIconContainer.getTransformer() + }); + if (!script.compiled) { + onError(script.errors.map(error => error.messageText).join("\n")); + } + else { + this.dataDoc[this.props.fieldKey] = new ScriptField(script); + } + } + }} showDocumentIcons />; + } +} \ No newline at end of file -- cgit v1.2.3-70-g09d2 From b21db9d40c1619df5455ba8ffe3ef76913cc92de Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Wed, 8 Apr 2020 19:14:47 -0400 Subject: added scripting box --- src/client/documents/Documents.ts | 4 +- src/client/util/Scripting.ts | 8 +- src/client/views/MainView.tsx | 3 +- src/client/views/nodes/ScriptingBox.scss | 33 +++++ src/client/views/nodes/ScriptingBox.tsx | 137 ++++++++++++--------- .../authentication/models/current_user_utils.ts | 1 + 6 files changed, 123 insertions(+), 63 deletions(-) (limited to 'src/client/views/nodes/ScriptingBox.tsx') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 5231dbb8c..5eca71ca9 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -509,8 +509,8 @@ export namespace Docs { return InstanceFromProto(Prototypes.get(DocumentType.PRES), initial, options); } - export function ScriptingDocument(url: string, options: DocumentOptions = {}) { - return InstanceFromProto(Prototypes.get(DocumentType.SCRIPTING), new YoutubeField(new URL(url)), options); + export function ScriptingDocument(options: DocumentOptions = {}) { + return InstanceFromProto(Prototypes.get(DocumentType.SCRIPTING), "", options); } export function VideoDocument(url: string, options: DocumentOptions = {}) { diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts index cf04c44ca..f97d91d10 100644 --- a/src/client/util/Scripting.ts +++ b/src/client/util/Scripting.ts @@ -89,9 +89,9 @@ const _scriptingGlobals: { [name: string]: any } = {}; let scriptingGlobals: { [name: string]: any } = _scriptingGlobals; function Run(script: string | undefined, customParams: string[], diagnostics: any[], originalScript: string, options: ScriptOptions): CompileResult { - const errors = diagnostics.some(diag => diag.category === ts.DiagnosticCategory.Error); - if ((options.typecheck !== false && errors) || !script) { - return { compiled: false, errors: diagnostics }; + const errors = diagnostics.filter(diag => diag.category === ts.DiagnosticCategory.Error); + if ((options.typecheck !== false && errors.length) || !script) { + return { compiled: false, errors }; } const paramNames = Object.keys(scriptingGlobals); @@ -201,7 +201,7 @@ export interface ScriptOptions { capturedVariables?: { [name: string]: Field }; // list of captured variables typecheck?: boolean; // should the compiler perform typechecking editable?: boolean; // can the script edit Docs - traverser?: TraverserParam; + traverser?: TraverserParam; transformer?: Transformer; // does the editor display a text label by each document that can be used as a captured document reference globals?: { [name: string]: any }; } diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 7c9f47fe4..1edfc871f 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -1,5 +1,5 @@ import { library } from '@fortawesome/fontawesome-svg-core'; -import { faArrowDown, faArrowUp, faBolt, faBullseye, faCaretUp, faCat, faCheck, faChevronRight, faClipboard, faClone, faCloudUploadAlt, faCommentAlt, faCompressArrowsAlt, faCut, faEllipsisV, faEraser, faExclamation, faFileAlt, faFileAudio, faFilePdf, faFilm, faFilter, faFont, faGlobeAsia, faHighlighter, faLongArrowAltRight, faMicrophone, faMousePointer, faMusic, faObjectGroup, faPause, faPen, faPenNib, faPhone, faPlay, faPortrait, faRedoAlt, faStamp, faStickyNote, faThumbtack, faTree, faTv, faUndoAlt, faVideo } from '@fortawesome/free-solid-svg-icons'; +import { faTerminal, faArrowDown, faArrowUp, faBolt, faBullseye, faCaretUp, faCat, faCheck, faChevronRight, faClipboard, faClone, faCloudUploadAlt, faCommentAlt, faCompressArrowsAlt, faCut, faEllipsisV, faEraser, faExclamation, faFileAlt, faFileAudio, faFilePdf, faFilm, faFilter, faFont, faGlobeAsia, faHighlighter, faLongArrowAltRight, faMicrophone, faMousePointer, faMusic, faObjectGroup, faPause, faPen, faPenNib, faPhone, faPlay, faPortrait, faRedoAlt, faStamp, faStickyNote, faThumbtack, faTree, faTv, faUndoAlt, faVideo } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, configure, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; @@ -101,6 +101,7 @@ export class MainView extends React.Component { } } + library.add(faTerminal); library.add(faFileAlt); library.add(faStickyNote); library.add(faFont); diff --git a/src/client/views/nodes/ScriptingBox.scss b/src/client/views/nodes/ScriptingBox.scss index e69de29bb..f31c72bb2 100644 --- a/src/client/views/nodes/ScriptingBox.scss +++ b/src/client/views/nodes/ScriptingBox.scss @@ -0,0 +1,33 @@ +.scriptingBox-outerDiv { + width: 100%; + height: 100%; + display: flex; + flex-direction: column; + pointer-events: all; + background-color: rgb(241, 239, 235); + padding: 10px; + .scriptingBox-inputDiv { + display: flex; + flex-direction: column; + height: calc(100% - 30px); + .scriptingBox-errorMessage { + overflow: auto; + } + .scriptingBox-textArea { + width: 100%; + height: 100%; + box-sizing: border-box; + resize: none; + padding: 7px; + } + } + + .scriptingBox-toolbar { + width: 100%; + height: 30px; + .scriptingBox-button { + width: 50% + } + } +} + diff --git a/src/client/views/nodes/ScriptingBox.tsx b/src/client/views/nodes/ScriptingBox.tsx index a2dd134ed..74d9c2e94 100644 --- a/src/client/views/nodes/ScriptingBox.tsx +++ b/src/client/views/nodes/ScriptingBox.tsx @@ -1,71 +1,96 @@ -import { library } from '@fortawesome/fontawesome-svg-core'; -import { faEdit } from '@fortawesome/free-regular-svg-icons'; -import { computed } from 'mobx'; -import { observer } from 'mobx-react'; -import * as React from 'react'; -import { Doc } from '../../../new_fields/Doc'; -import { documentSchema } from '../../../new_fields/documentSchemas'; +import { action, observable, computed } from "mobx"; +import { observer } from "mobx-react"; +import * as React from "react"; +import { Opt } from "../../../new_fields/Doc"; +import { documentSchema } from "../../../new_fields/documentSchemas"; +import { createSchema, makeInterface } from "../../../new_fields/Schema"; +import { ScriptField } from "../../../new_fields/ScriptField"; +import { StrCast, ScriptCast } from "../../../new_fields/Types"; +import { returnTrue } from "../../../Utils"; +import { InteractionUtils } from "../../util/InteractionUtils"; import { CompileScript } from "../../util/Scripting"; -import { ScriptBox } from '../ScriptBox'; -import { createSchema, listSpec, makeInterface } from '../../../new_fields/Schema'; -import { ScriptField } from '../../../new_fields/ScriptField'; -import { BoolCast, ScriptCast } from '../../../new_fields/Types'; -import { DocComponent } from '../DocComponent'; -import { FieldView, FieldViewProps } from './FieldView'; -import './ScriptingBox.scss'; -import { DocumentIconContainer } from './DocumentIcon'; - - -library.add(faEdit as any); +import { DocAnnotatableComponent } from "../DocComponent"; +import { EditableView } from "../EditableView"; +import { FieldView, FieldViewProps } from "../nodes/FieldView"; +import "./ScriptingBox.scss"; const ScriptingSchema = createSchema({ - onClick: ScriptField, - buttonParams: listSpec("string"), - text: "string" }); type ScriptingDocument = makeInterface<[typeof ScriptingSchema, typeof documentSchema]>; const ScriptingDocument = makeInterface(ScriptingSchema, documentSchema); @observer -export class ScriptingBox extends DocComponent(ScriptingDocument) { - public static LayoutString(fieldKey: string) { return FieldView.LayoutString(ScriptingBox, fieldKey); } +export class ScriptingBox extends DocAnnotatableComponent(ScriptingDocument) { + protected multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer | undefined; + public static LayoutString(fieldStr: string) { return FieldView.LayoutString(ScriptingBox, fieldStr); } - @computed get dataDoc() { - return this.props.DataDoc && - (this.Document.isTemplateForField || BoolCast(this.props.DataDoc.isTemplateForField) || - this.props.DataDoc.layout === this.props.Document) ? this.props.DataDoc : Doc.GetProto(this.props.Document); + @observable private _errorMessage: string = ""; + @computed get rawScript() { return StrCast(this.dataDoc[this.props.fieldKey + "-raw"]); } + set rawScript(value) { this.dataDoc[this.props.fieldKey + "-raw"] = value; } + + @action + componentDidMount() { + this.rawScript = ScriptCast(this.dataDoc[this.props.fieldKey])?.script?.originalScript || this.rawScript; } - specificContextMenu = (e: React.MouseEvent): void => { } + @action + onChange = (e: React.ChangeEvent) => { + this.rawScript = e.target.value; + } - render() { - const script = ScriptCast(this.props.Document[this.props.fieldKey]); - let originalText: string | undefined = undefined; - if (script) { - originalText = script.script.originalScript; + @action + onError = (error: any) => { + this._errorMessage = (error as any).map((e: any) => e.messageText).join(" "); + } + + @action + onCompile = () => { + const result = CompileScript(this.rawScript, {}); + this._errorMessage = ""; + if (result.compiled) { + this._errorMessage = ""; + this.dataDoc[this.props.fieldKey] = new ScriptField(result); } - return !(this.props.Document instanceof Doc) ? (null) : - { }} - onCancel={() => { }} - onSave={(text, onError) => { - if (!text) { - this.dataDoc[this.props.fieldKey] = undefined; - } else { - const script = CompileScript(text, { - params: { this: Doc.name }, - typecheck: false, - editable: true, - transformer: DocumentIconContainer.getTransformer() - }); - if (!script.compiled) { - onError(script.errors.map(error => error.messageText).join("\n")); - } - else { - this.dataDoc[this.props.fieldKey] = new ScriptField(script); - } - } - }} showDocumentIcons />; + else { + this.dataDoc[this.props.fieldKey] = undefined; + this.onError(result.errors); + } + } + + @action + onRun = () => { + this.onCompile(); + ScriptCast(this.dataDoc[this.props.fieldKey])?.script.run({}, + (err: any) => { + this._errorMessage = ""; + this.onError(err); + }); + } + + render() { + let onFocus: Opt<() => void> = undefined, onBlur: Opt<() => void> = undefined; + const params = ""} + SetValue={returnTrue} + />; + return ( +
this.props.isSelected() && e.stopPropagation()} onWheel={(e) => this.props.isSelected() && e.stopPropagation()}> +
+